Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mqtt_client_test #498

Merged
merged 11 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
78 changes: 78 additions & 0 deletions pico_w/wifi/mqtt/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# 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)
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 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