Skip to content

Commit

Permalink
mqtt_client_test (#498)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
3 people authored Feb 13, 2025
1 parent b01d600 commit bc20ff6
Show file tree
Hide file tree
Showing 12 changed files with 657 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ App|Description
[picow_httpd](pico_w/wifi/httpd) | Runs a LWIP HTTP server test app
[picow_http_client](pico_w/wifi/http_client) | Demonstrates how to make http and https requests
[picow_http_client_verify](pico_w/wifi/http_client) | Demonstrates how to make a https request with server authentication
[picow_mqtt_client](pico_w/wifi/mqtt) | Demonstrates how to implement an MQTT client application

#### FreeRTOS examples

Expand Down
1 change: 1 addition & 0 deletions pico_w/wifi/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ else()
add_subdirectory_exclude_platforms(tcp_server)
add_subdirectory_exclude_platforms(udp_beacon)
add_subdirectory_exclude_platforms(http_client)
add_subdirectory_exclude_platforms(mqtt)

if (NOT PICO_MBEDTLS_PATH)
message("Skipping tls examples as PICO_MBEDTLS_PATH is not defined")
Expand Down
2 changes: 2 additions & 0 deletions pico_w/wifi/lwipopts_examples_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
#define MEM_LIBC_MALLOC 0
#endif
#define MEM_ALIGNMENT 4
#ifndef MEM_SIZE
#define MEM_SIZE 4000
#endif
#define MEMP_NUM_TCP_SEG 32
#define MEMP_NUM_ARP_QUEUE 10
#define PBUF_POOL_SIZE 24
Expand Down
79 changes: 79 additions & 0 deletions pico_w/wifi/mqtt/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Define the host name of the MQTT server in an environment variable or pass it to cmake,
# e.g. cmake -DMQTT_SERVER=myserver ..

if (DEFINED ENV{MQTT_SERVER} AND (NOT MQTT_SERVER))
set(MQTT_SERVER $ENV{MQTT_SERVER})
message("Using MQTT_SERVER from environment ('${MQTT_SERVER}')")
endif()
if (NOT MQTT_SERVER)
message("Skipping MQTT example as MQTT_SERVER is not defined")
return()
endif()
# Define the name of an MQTT broker/server to enable this example
set(MQTT_SERVER "${MQTT_SERVER}" CACHE INTERNAL "MQTT server for examples")

if (DEFINED ENV{MQTT_USERNAME} AND (NOT MQTT_USERNAME))
set(MQTT_USERNAME $ENV{MQTT_USERNAME})
message("Using MQTT_USERNAME from environment ('${MQTT_USERNAME}')")
endif()
set(MQTT_USERNAME "${MQTT_USERNAME}" CACHE INTERNAL "MQTT user name for examples")
if (DEFINED ENV{MQTT_PASSWORD} AND (NOT MQTT_PASSWORD))
set(MQTT_PASSWORD $ENV{MQTT_PASSWORD})
message("Using MQTT_PASSWORD from environment")
endif()
set(MQTT_PASSWORD "${MQTT_PASSWORD}" CACHE INTERNAL "MQTT password for examples")

# Set path to the certificate include file
if (NOT MQTT_CERT_PATH)
set(MQTT_CERT_PATH ${CMAKE_CURRENT_LIST_DIR}/certs/${MQTT_SERVER})
endif()

# Set the name of the certificate include file
if (NOT MQTT_CERT_INC)
set(MQTT_CERT_INC mqtt_client.inc)
endif()

set(TARGET_NAME picow_mqtt_client)
add_executable(${TARGET_NAME}
mqtt_client.c
)
target_link_libraries(${TARGET_NAME}
pico_stdlib
hardware_adc
pico_cyw43_arch_lwip_threadsafe_background
pico_lwip_mqtt
pico_mbedtls
pico_lwip_mbedtls
)
target_include_directories(${TARGET_NAME} PRIVATE
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required
)
target_compile_definitions(${TARGET_NAME} PRIVATE
WIFI_SSID=\"${WIFI_SSID}\"
WIFI_PASSWORD=\"${WIFI_PASSWORD}\"
MQTT_SERVER=\"${MQTT_SERVER}\"
)
if (EXISTS "${MQTT_CERT_PATH}/${MQTT_CERT_INC}")
target_compile_definitions(${TARGET_NAME} PRIVATE
MQTT_CERT_INC=\"${MQTT_CERT_INC}\" # contains the tls certificates for MQTT_SERVER needed by the client
ALTCP_MBEDTLS_AUTHMODE=MBEDTLS_SSL_VERIFY_REQUIRED
)
target_include_directories(${TARGET_NAME} PRIVATE
${MQTT_CERT_PATH}
)
endif()
if (MQTT_USERNAME AND MQTT_PASSWORD)
target_compile_definitions(${TARGET_NAME} PRIVATE
MQTT_USERNAME=\"${MQTT_USERNAME}\"
MQTT_PASSWORD=\"${MQTT_PASSWORD}\"
)
endif()
pico_add_extra_outputs(${TARGET_NAME})

# Ignore warnings from lwip code
set_source_files_properties(
${PICO_LWIP_PATH}/src/apps/altcp_tls/altcp_tls_mbedtls.c
PROPERTIES
COMPILE_OPTIONS "-Wno-unused-result"
)
94 changes: 94 additions & 0 deletions pico_w/wifi/mqtt/README
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Quick start

To use this example you will need to install an MQTT server on your network.
To install the Mosquitto MQTT client on a Raspberry Pi Linux device run the following...

```
sudo apt install mosquitto
sudo apt install mosquitto-clients
```

Check it works...

```
mosquitto_pub -t test_topic -m "Yes it works" -r
mosquitto_sub -t test_topic -C 1
```

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

```
allow_anonymous true
listener 1883 0.0.0.0
```

Then restart the service.

```
sudo service mosquitto restart
```

When building the code set the host name of the MQTT server, e.g.

```
export MQTT_SERVER=myhost
cmake ..
```

The example should publish its core temperature to the /temperature topic. You can subscribe to this topic from another machine.

```
mosquitto_sub -h $MQTT_SERVER -t '/temperature'
```

You can turn the led on and off by publishing messages.

```
mosquitto_pub -h $MQTT_SERVER -t '/led' -m on
mosquitto_pub -h $MQTT_SERVER -t '/led' -m off
```

# Security

If your server has a username and password, you can set these with the following variables.

```
export MQTT_USERNAME=user
export MQTT_PASSWORD=pass
```

Be aware that these details are sent in plain text unless you use TLS.

## Using TLS

To use TLS and client server authentication you need some keys and certificates for the client and server.
The `certs/makecerts.sh` script demonstrates a way to make these.
It creates a folder named after `MQTT_SERVER` containing all the required files.
From these files it generates a header file `mqtt_client.inc` included by the code.
Your server will have to be configured to use TLS and the port 8883 rather than the non-TLS port 1883.

```
listener 1883 127.0.0.1

listener 8883 0.0.0.0
allow_anonymous true
cafile /etc/mosquitto/ca_certificates/ca.crt
certfile /etc/mosquitto/certs/server.crt
keyfile /etc/mosquitto/certs/server.key
require_certificate true
```

To connect to your server with the mosquitto tools in linux you will have to pass extra parameters.

```
mosquitto_pub -h $MQTT_SERVER --cafile $MQTT_SERVER/ca.crt --key $MQTT_SERVER/client.key --cert $MQTT_SERVER/client.crt -t /led -m on
mosquitto_sub -h $MQTT_SERVER --cafile $MQTT_SERVER/ca.crt --key $MQTT_SERVER/client.key --cert $MQTT_SERVER/client.crt -t "/temperature"
```

There are some shell scripts in the certs folder to reduce the amount of typing assuming `MQTT_SERVER` is defined.

```
cd certs
./pub.sh /led on
./sub.sh /temperature
```
2 changes: 2 additions & 0 deletions pico_w/wifi/mqtt/certs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*/

43 changes: 43 additions & 0 deletions pico_w/wifi/mqtt/certs/makecerts.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/bash

if [ "${PWD##*/}" != "certs" ]; then
echo Run this in the certs folder
exit 1
fi
if [ -z "$MQTT_SERVER" ]; then
echo Define MQTT_SERVER
exit 1
fi
SERVER_NAME=$MQTT_SERVER

if [ -d "$SERVER_NAME" ]; then
echo Run \"rm -fr $SERVER_NAME\" to regenerate these keys
exit 1
fi
mkdir $SERVER_NAME
echo Generating keys in $PWD/$SERVER_NAME

openssl genrsa -out $SERVER_NAME/ca.key 2048
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"

openssl genrsa -out $SERVER_NAME/server.key 2048
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"
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

openssl genrsa -out $SERVER_NAME/client.key 2048
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"
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

echo -n \#define TLS_ROOT_CERT \" > $SERVER_NAME/mqtt_client.inc
cat $SERVER_NAME/ca.crt | awk '{printf "%s\\n\\\n", $0}' >> $SERVER_NAME/mqtt_client.inc
echo "\"" >> $SERVER_NAME/mqtt_client.inc
echo >> $SERVER_NAME/mqtt_client.inc

echo -n \#define TLS_CLIENT_KEY \" >> $SERVER_NAME/mqtt_client.inc
cat $SERVER_NAME/client.key | awk '{printf "%s\\n\\\n", $0}' >> $SERVER_NAME/mqtt_client.inc
echo "\"" >> $SERVER_NAME/mqtt_client.inc
echo >> $SERVER_NAME/mqtt_client.inc

echo -n \#define TLS_CLIENT_CERT \" >> $SERVER_NAME/mqtt_client.inc
cat $SERVER_NAME/client.crt | awk '{printf "%s\\n\\\n", $0}' >> $SERVER_NAME/mqtt_client.inc
echo "\"" >> $SERVER_NAME/mqtt_client.inc
9 changes: 9 additions & 0 deletions pico_w/wifi/mqtt/certs/pub.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#/usr/bin/bash

if [ -z "$MQTT_SERVER" ]; then
echo Define MQTT_SERVER
exit 1
fi

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 changes: 9 additions & 0 deletions pico_w/wifi/mqtt/certs/sub.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#/usr/bin/bash

if [ -z "$MQTT_SERVER" ]; then
echo Define MQTT_SERVER
exit 1
fi

mosquitto_sub -h $MQTT_SERVER --cafile $MQTT_SERVER/ca.crt --key $MQTT_SERVER/client.key --cert $MQTT_SERVER/client.crt -t "$1"

33 changes: 33 additions & 0 deletions pico_w/wifi/mqtt/lwipopts.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#ifndef _LWIPOPTS_H
#define _LWIPOPTS_H

// Need more memory for TLS
#ifdef MQTT_CERT_INC
#define MEM_SIZE 8000
#endif

// Generally you would define your own explicit list of lwIP options
// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html)
//
// This example uses a common include to avoid repetition
#include "lwipopts_examples_common.h"

#define MEMP_NUM_SYS_TIMEOUT (LWIP_NUM_SYS_TIMEOUT_INTERNAL+1)

#ifdef MQTT_CERT_INC
#define LWIP_ALTCP 1
#define LWIP_ALTCP_TLS 1
#define LWIP_ALTCP_TLS_MBEDTLS 1
#ifndef NDEBUG
#define ALTCP_MBEDTLS_DEBUG LWIP_DBG_ON
#endif
/* TCP WND must be at least 16 kb to match TLS record size
or you will get a warning "altcp_tls: TCP_WND is smaller than the RX decrypion buffer, connection RX might stall!" */
#undef TCP_WND
#define TCP_WND 16384
#endif // MQTT_CERT_INC

// This defaults to 4
#define MQTT_REQ_MAX_IN_FLIGHT 5

#endif
6 changes: 6 additions & 0 deletions pico_w/wifi/mqtt/mbedtls_config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#ifndef MBEDTLS_CONFIG_TLS_CLIENT_H
#define MBEDTLS_CONFIG_TLS_CLIENT_H

#include "mbedtls_config_examples_common.h"

#endif
Loading

0 comments on commit bc20ff6

Please sign in to comment.