Skip to content

Commit bc20ff6

Browse files
ellebi2000ellebi2000peterharperuk
authored
mqtt_client_test (#498)
* added mqtt pub/sub example * added mqtt pub/sub example * removed unnecessary comments * Move mqtt example to wifi folder * Tidy up mqtt example Fix review comments and build warnings. Use MQTT_SERVER build variable instead of hard coding IP address. Make a DNS request for the server. Use async timer for publishing the temperature. * Add support for username and password * Add TLS support Move the code down one level * Add some more topics print, just print the message to stdout ping, publish uptime to pong exit, quit the client * Add will topic /online Add some defines for some magic numbers * Unique topic names per device Disabled by default with MQTT_UNIQUE_TOPIC * Some MQTT updates Change /pong to /uptime Display a message if MQTT_SERVER is not defined Put picow_ in the name like the other examples Add details to the readme! --------- Co-authored-by: ellebi2000 <[email protected]> Co-authored-by: Peter Harper <[email protected]>
1 parent b01d600 commit bc20ff6

12 files changed

+657
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ App|Description
191191
[picow_httpd](pico_w/wifi/httpd) | Runs a LWIP HTTP server test app
192192
[picow_http_client](pico_w/wifi/http_client) | Demonstrates how to make http and https requests
193193
[picow_http_client_verify](pico_w/wifi/http_client) | Demonstrates how to make a https request with server authentication
194+
[picow_mqtt_client](pico_w/wifi/mqtt) | Demonstrates how to implement an MQTT client application
194195

195196
#### FreeRTOS examples
196197

pico_w/wifi/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ else()
1818
add_subdirectory_exclude_platforms(tcp_server)
1919
add_subdirectory_exclude_platforms(udp_beacon)
2020
add_subdirectory_exclude_platforms(http_client)
21+
add_subdirectory_exclude_platforms(mqtt)
2122

2223
if (NOT PICO_MBEDTLS_PATH)
2324
message("Skipping tls examples as PICO_MBEDTLS_PATH is not defined")

pico_w/wifi/lwipopts_examples_common.h

+2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
#define MEM_LIBC_MALLOC 0
2121
#endif
2222
#define MEM_ALIGNMENT 4
23+
#ifndef MEM_SIZE
2324
#define MEM_SIZE 4000
25+
#endif
2426
#define MEMP_NUM_TCP_SEG 32
2527
#define MEMP_NUM_ARP_QUEUE 10
2628
#define PBUF_POOL_SIZE 24

pico_w/wifi/mqtt/CMakeLists.txt

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Define the host name of the MQTT server in an environment variable or pass it to cmake,
2+
# e.g. cmake -DMQTT_SERVER=myserver ..
3+
4+
if (DEFINED ENV{MQTT_SERVER} AND (NOT MQTT_SERVER))
5+
set(MQTT_SERVER $ENV{MQTT_SERVER})
6+
message("Using MQTT_SERVER from environment ('${MQTT_SERVER}')")
7+
endif()
8+
if (NOT MQTT_SERVER)
9+
message("Skipping MQTT example as MQTT_SERVER is not defined")
10+
return()
11+
endif()
12+
# Define the name of an MQTT broker/server to enable this example
13+
set(MQTT_SERVER "${MQTT_SERVER}" CACHE INTERNAL "MQTT server for examples")
14+
15+
if (DEFINED ENV{MQTT_USERNAME} AND (NOT MQTT_USERNAME))
16+
set(MQTT_USERNAME $ENV{MQTT_USERNAME})
17+
message("Using MQTT_USERNAME from environment ('${MQTT_USERNAME}')")
18+
endif()
19+
set(MQTT_USERNAME "${MQTT_USERNAME}" CACHE INTERNAL "MQTT user name for examples")
20+
if (DEFINED ENV{MQTT_PASSWORD} AND (NOT MQTT_PASSWORD))
21+
set(MQTT_PASSWORD $ENV{MQTT_PASSWORD})
22+
message("Using MQTT_PASSWORD from environment")
23+
endif()
24+
set(MQTT_PASSWORD "${MQTT_PASSWORD}" CACHE INTERNAL "MQTT password for examples")
25+
26+
# Set path to the certificate include file
27+
if (NOT MQTT_CERT_PATH)
28+
set(MQTT_CERT_PATH ${CMAKE_CURRENT_LIST_DIR}/certs/${MQTT_SERVER})
29+
endif()
30+
31+
# Set the name of the certificate include file
32+
if (NOT MQTT_CERT_INC)
33+
set(MQTT_CERT_INC mqtt_client.inc)
34+
endif()
35+
36+
set(TARGET_NAME picow_mqtt_client)
37+
add_executable(${TARGET_NAME}
38+
mqtt_client.c
39+
)
40+
target_link_libraries(${TARGET_NAME}
41+
pico_stdlib
42+
hardware_adc
43+
pico_cyw43_arch_lwip_threadsafe_background
44+
pico_lwip_mqtt
45+
pico_mbedtls
46+
pico_lwip_mbedtls
47+
)
48+
target_include_directories(${TARGET_NAME} PRIVATE
49+
${CMAKE_CURRENT_LIST_DIR}
50+
${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required
51+
)
52+
target_compile_definitions(${TARGET_NAME} PRIVATE
53+
WIFI_SSID=\"${WIFI_SSID}\"
54+
WIFI_PASSWORD=\"${WIFI_PASSWORD}\"
55+
MQTT_SERVER=\"${MQTT_SERVER}\"
56+
)
57+
if (EXISTS "${MQTT_CERT_PATH}/${MQTT_CERT_INC}")
58+
target_compile_definitions(${TARGET_NAME} PRIVATE
59+
MQTT_CERT_INC=\"${MQTT_CERT_INC}\" # contains the tls certificates for MQTT_SERVER needed by the client
60+
ALTCP_MBEDTLS_AUTHMODE=MBEDTLS_SSL_VERIFY_REQUIRED
61+
)
62+
target_include_directories(${TARGET_NAME} PRIVATE
63+
${MQTT_CERT_PATH}
64+
)
65+
endif()
66+
if (MQTT_USERNAME AND MQTT_PASSWORD)
67+
target_compile_definitions(${TARGET_NAME} PRIVATE
68+
MQTT_USERNAME=\"${MQTT_USERNAME}\"
69+
MQTT_PASSWORD=\"${MQTT_PASSWORD}\"
70+
)
71+
endif()
72+
pico_add_extra_outputs(${TARGET_NAME})
73+
74+
# Ignore warnings from lwip code
75+
set_source_files_properties(
76+
${PICO_LWIP_PATH}/src/apps/altcp_tls/altcp_tls_mbedtls.c
77+
PROPERTIES
78+
COMPILE_OPTIONS "-Wno-unused-result"
79+
)

pico_w/wifi/mqtt/README

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Quick start
2+
3+
To use this example you will need to install an MQTT server on your network.
4+
To install the Mosquitto MQTT client on a Raspberry Pi Linux device run the following...
5+
6+
```
7+
sudo apt install mosquitto
8+
sudo apt install mosquitto-clients
9+
```
10+
11+
Check it works...
12+
13+
```
14+
mosquitto_pub -t test_topic -m "Yes it works" -r
15+
mosquitto_sub -t test_topic -C 1
16+
```
17+
18+
To allow an external client to connect to the server you will have to change the configuration. Add the following to /etc/mosquitto/conf.d/mosquitto.conf
19+
20+
```
21+
allow_anonymous true
22+
listener 1883 0.0.0.0
23+
```
24+
25+
Then restart the service.
26+
27+
```
28+
sudo service mosquitto restart
29+
```
30+
31+
When building the code set the host name of the MQTT server, e.g.
32+
33+
```
34+
export MQTT_SERVER=myhost
35+
cmake ..
36+
```
37+
38+
The example should publish its core temperature to the /temperature topic. You can subscribe to this topic from another machine.
39+
40+
```
41+
mosquitto_sub -h $MQTT_SERVER -t '/temperature'
42+
```
43+
44+
You can turn the led on and off by publishing messages.
45+
46+
```
47+
mosquitto_pub -h $MQTT_SERVER -t '/led' -m on
48+
mosquitto_pub -h $MQTT_SERVER -t '/led' -m off
49+
```
50+
51+
# Security
52+
53+
If your server has a username and password, you can set these with the following variables.
54+
55+
```
56+
export MQTT_USERNAME=user
57+
export MQTT_PASSWORD=pass
58+
```
59+
60+
Be aware that these details are sent in plain text unless you use TLS.
61+
62+
## Using TLS
63+
64+
To use TLS and client server authentication you need some keys and certificates for the client and server.
65+
The `certs/makecerts.sh` script demonstrates a way to make these.
66+
It creates a folder named after `MQTT_SERVER` containing all the required files.
67+
From these files it generates a header file `mqtt_client.inc` included by the code.
68+
Your server will have to be configured to use TLS and the port 8883 rather than the non-TLS port 1883.
69+
70+
```
71+
listener 1883 127.0.0.1
72+
73+
listener 8883 0.0.0.0
74+
allow_anonymous true
75+
cafile /etc/mosquitto/ca_certificates/ca.crt
76+
certfile /etc/mosquitto/certs/server.crt
77+
keyfile /etc/mosquitto/certs/server.key
78+
require_certificate true
79+
```
80+
81+
To connect to your server with the mosquitto tools in linux you will have to pass extra parameters.
82+
83+
```
84+
mosquitto_pub -h $MQTT_SERVER --cafile $MQTT_SERVER/ca.crt --key $MQTT_SERVER/client.key --cert $MQTT_SERVER/client.crt -t /led -m on
85+
mosquitto_sub -h $MQTT_SERVER --cafile $MQTT_SERVER/ca.crt --key $MQTT_SERVER/client.key --cert $MQTT_SERVER/client.crt -t "/temperature"
86+
```
87+
88+
There are some shell scripts in the certs folder to reduce the amount of typing assuming `MQTT_SERVER` is defined.
89+
90+
```
91+
cd certs
92+
./pub.sh /led on
93+
./sub.sh /temperature
94+
```

pico_w/wifi/mqtt/certs/.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*/
2+

pico_w/wifi/mqtt/certs/makecerts.sh

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#!/usr/bin/bash
2+
3+
if [ "${PWD##*/}" != "certs" ]; then
4+
echo Run this in the certs folder
5+
exit 1
6+
fi
7+
if [ -z "$MQTT_SERVER" ]; then
8+
echo Define MQTT_SERVER
9+
exit 1
10+
fi
11+
SERVER_NAME=$MQTT_SERVER
12+
13+
if [ -d "$SERVER_NAME" ]; then
14+
echo Run \"rm -fr $SERVER_NAME\" to regenerate these keys
15+
exit 1
16+
fi
17+
mkdir $SERVER_NAME
18+
echo Generating keys in $PWD/$SERVER_NAME
19+
20+
openssl genrsa -out $SERVER_NAME/ca.key 2048
21+
openssl req -new -x509 -days 99999 -key $SERVER_NAME/ca.key -out $SERVER_NAME/ca.crt -subj "/C=UK/ST=Cambridgeshire/L=Cambridge/O=Raspberry Pi Ltd/OU=Software/CN=rpiroot"
22+
23+
openssl genrsa -out $SERVER_NAME/server.key 2048
24+
openssl req -new -out $SERVER_NAME/server.csr -key $SERVER_NAME/server.key -subj "/C=UK/ST=Cambridgeshire/L=Cambridge/O=Raspberry Pi Ltd/OU=Software/CN=$SERVER_NAME"
25+
openssl x509 -req -in $SERVER_NAME/server.csr -CA $SERVER_NAME/ca.crt -CAkey $SERVER_NAME/ca.key -CAcreateserial -out $SERVER_NAME/server.crt -days 9999
26+
27+
openssl genrsa -out $SERVER_NAME/client.key 2048
28+
openssl req -new -out $SERVER_NAME/client.csr -key $SERVER_NAME/client.key -subj "/C=UK/ST=Cambridgeshire/L=Cambridge/O=Raspberry Pi Ltd/OU=Software/CN=$SERVER_NAME"
29+
openssl x509 -req -in $SERVER_NAME/client.csr -CA $SERVER_NAME/ca.crt -CAkey $SERVER_NAME/ca.key -CAcreateserial -out $SERVER_NAME/client.crt -days 999
30+
31+
echo -n \#define TLS_ROOT_CERT \" > $SERVER_NAME/mqtt_client.inc
32+
cat $SERVER_NAME/ca.crt | awk '{printf "%s\\n\\\n", $0}' >> $SERVER_NAME/mqtt_client.inc
33+
echo "\"" >> $SERVER_NAME/mqtt_client.inc
34+
echo >> $SERVER_NAME/mqtt_client.inc
35+
36+
echo -n \#define TLS_CLIENT_KEY \" >> $SERVER_NAME/mqtt_client.inc
37+
cat $SERVER_NAME/client.key | awk '{printf "%s\\n\\\n", $0}' >> $SERVER_NAME/mqtt_client.inc
38+
echo "\"" >> $SERVER_NAME/mqtt_client.inc
39+
echo >> $SERVER_NAME/mqtt_client.inc
40+
41+
echo -n \#define TLS_CLIENT_CERT \" >> $SERVER_NAME/mqtt_client.inc
42+
cat $SERVER_NAME/client.crt | awk '{printf "%s\\n\\\n", $0}' >> $SERVER_NAME/mqtt_client.inc
43+
echo "\"" >> $SERVER_NAME/mqtt_client.inc

pico_w/wifi/mqtt/certs/pub.sh

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#/usr/bin/bash
2+
3+
if [ -z "$MQTT_SERVER" ]; then
4+
echo Define MQTT_SERVER
5+
exit 1
6+
fi
7+
8+
mosquitto_pub -h $MQTT_SERVER --cafile $MQTT_SERVER/ca.crt --key $MQTT_SERVER/client.key --cert $MQTT_SERVER/client.crt -t "$1" -m "$2"
9+

pico_w/wifi/mqtt/certs/sub.sh

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#/usr/bin/bash
2+
3+
if [ -z "$MQTT_SERVER" ]; then
4+
echo Define MQTT_SERVER
5+
exit 1
6+
fi
7+
8+
mosquitto_sub -h $MQTT_SERVER --cafile $MQTT_SERVER/ca.crt --key $MQTT_SERVER/client.key --cert $MQTT_SERVER/client.crt -t "$1"
9+

pico_w/wifi/mqtt/lwipopts.h

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#ifndef _LWIPOPTS_H
2+
#define _LWIPOPTS_H
3+
4+
// Need more memory for TLS
5+
#ifdef MQTT_CERT_INC
6+
#define MEM_SIZE 8000
7+
#endif
8+
9+
// Generally you would define your own explicit list of lwIP options
10+
// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html)
11+
//
12+
// This example uses a common include to avoid repetition
13+
#include "lwipopts_examples_common.h"
14+
15+
#define MEMP_NUM_SYS_TIMEOUT (LWIP_NUM_SYS_TIMEOUT_INTERNAL+1)
16+
17+
#ifdef MQTT_CERT_INC
18+
#define LWIP_ALTCP 1
19+
#define LWIP_ALTCP_TLS 1
20+
#define LWIP_ALTCP_TLS_MBEDTLS 1
21+
#ifndef NDEBUG
22+
#define ALTCP_MBEDTLS_DEBUG LWIP_DBG_ON
23+
#endif
24+
/* TCP WND must be at least 16 kb to match TLS record size
25+
or you will get a warning "altcp_tls: TCP_WND is smaller than the RX decrypion buffer, connection RX might stall!" */
26+
#undef TCP_WND
27+
#define TCP_WND 16384
28+
#endif // MQTT_CERT_INC
29+
30+
// This defaults to 4
31+
#define MQTT_REQ_MAX_IN_FLIGHT 5
32+
33+
#endif

pico_w/wifi/mqtt/mbedtls_config.h

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#ifndef MBEDTLS_CONFIG_TLS_CLIENT_H
2+
#define MBEDTLS_CONFIG_TLS_CLIENT_H
3+
4+
#include "mbedtls_config_examples_common.h"
5+
6+
#endif

0 commit comments

Comments
 (0)