This guide shows how to make an IoT toggle switch with an Adafruit Feather TFT ESP32-S3, Zephyr, and Adafruit IO. Key features include: GPIO input for Boot button, LVGL graphics, MQTT over WiFi with TLSv1.2, and USB serial shell commands for saving WiFi and MQTT configuration settings to NVM flash. This guide is intended for people who want to learn how to write applications in C using Zephyr APIs.
Demo video: IoT toggle switch: Zephyr + Feather TFT + Adafruit IO
Previously in this series of guides about using Zephyr on Adafruit hardware, I focused on setting up developer tools and writing Devicetree board definitions. This time, I'm moving up the stack to show how to build an application tying together several Zephyr APIs along with a custom board definition.
Building an IoT app with WiFi, TLS, and graphics is unavoidably a bit complicated. It took me about three weeks to write the code, which totals a bit over 2100 lines. Listing all of that here would be awkward. If you want the details, you can browse the code in my zphqst-03 GitHub repo. The code has lots of comments, including citations for the references I used while learning to use the Zephyr APIs.
This guide will focus on:
- How to build, run, and configure the IoT toggle switch app
- High level tour of the source code with GitHub links: which files do what?
- Understanding C language features that you'll need to use Zephyr APIs effectively: structs, function pointers, etc.
- Zephyr troubleshooting tips: diagnose and fix memory allocation issues, enable various types of debug logging, etc.
- MQTT testing with
openssland themosquittoMQTT broker with its companion command line tools,mosquitto_pubandmosquitto_sub
To prepare for building this project:
To run Zephyr's
westcommandline tool, you need to set up a Zephyr project, including a Python virtual environment and the zephyr git repo. In my examples, the zephyr project directory is~/code/zephyr-workspace.To build for ESP32-S3, you need to install the Zephyr SDK including the
xtensa-espressif_esp32s3_zephyr-elftoolchain. The basic getting started guide instructions install all the toolchains. You only need to pay attention to specific toolchains if you want to do a custom install to save disk space and bandwidth.The first time you run
west flashon a board that had CircuitPython installed, you may need to activate the board's ESP32-S3 built in bootloader using the button sequence: hold BOOT, press and release RESET, release BOOT. Once you have installed the Zephyr bootloader,west flashshould work without needing to press any buttons.-
To build with WiFi support, you will need to fetch the
hal_espressifblobs if you have not already done so:west blobs fetch hal_espressif
-
Clone the zphqst-03 repository into your Zephyr project directory. For example:
cd ~/code/zephyr-workspace git clone https://github.com/samblenny/zphqst-03.git
To learn more about setting up a Zephyr project directory, you can read the Zephyr Project Getting Started Guide or my Getting Started with Zephyr on Linux Playground guide.
The directions here were written and tested for a terminal shell on Debian 12 Linux. Probably they will work about the same on recent versions of Ubuntu. For other operating systems, you may need to adapt the instructions to suit your local setup.
Zephyr version: I've been keeping my local zephyr repo more or less up to
date with the current development version. I most recently tested the code for
this project with zephyrprojet-rtos/zephyr commit c60ffe1e1bc,
which is a bit after their Zephyr 4.1.0 release.
To get started, you need to activate your Python venv that contains west and
change into the zphqst-03 directory within your Zephyr project directory.
For example:
$ cd ~/code/zephyr-workspace
$ source .venv/bin/activate
(.venv) $ cd zphqst-03
The examples below use make in a terminal on Debian 12 to run commands for
make targets defined in the zphqst-03 Makefile.
Using make
avoids a lot of typing that would otherwise be required to provide commandline
options to west.
To build and flash the IoT toggle switch app:
make clean
make app
make flash
When you first install the app, in order to connect to the network, you must
first provision the board with WiFi and MQTT login credentials. To do that,
you connect by USB serial to the Zephyr shell and use the settings command.
Depending on how you've used your Feather TFT board previously, it might already have data written to the settings partition used by Zephyr's Non-Volatile Storage (NVS) subsystem. In that case, you'll need to erase the partition before writing the settings. (see Erasing the NVM Flash Partition section below)
You can access the Zephyr shell with the command:
make monitor
The make monitor command runs west espressif monitor (keyboard shortcut to
exit the serial monitor is Ctrl+]). If you prefer a different serial
monitor program, like tio or screen, you could try one of these:
tio /dev/ttyACM0
screen -fn /dev/ttyACM0 115200
Once you are in the Zephyr shell, you should see the uart:~$ prompt. If not,
press the Enter key on your keyboard a time or two. At the prompt, to
provision your network credentials, you can write values to NVM flash using the
settings shell command provided by Zephyr's
Settings
subsystem.
The Zephyr Settings subsystem is a key-value store. For this application, I've configured it to use the Non-Volatile Storage (NVS) subsystem for persistent storage. These three settings keys store the network provisioning details for WiFi and MQTT:
-
zq3/ssid: WiFi ssid (use quotes if it has spaces) -
zq3/psk: WiFi WPA2-PSK passphrase (use quotes if it has spaces) -
zq3/url: MQTT broker url:mqtt[s]://<user>:<pass>@<hostname>/<topic>
Here is an example provisioning for a private test network with a local MQTT broker listening on port 1883 of 192.168.0.100, with no encryption and anonymous connections enabled (username and password can be blank):
uart:~$ settings write string zq3/ssid MySSID
uart:~$ settings write string zq3/psk "my wifi passphrase"
uart:~$ settings write string zq3/url mqtt://:@192.168.0.100/test
This second example is for an authenticated TLSv1.2 connection to Adafruit IO.
Note how there's an "s" in mqtts://, the username is 'User', the API key is
key, and the topic is User/f/test:
uart:~$ settings write string zq3/ssid MySSID
uart:~$ settings write string zq3/psk "my wifi passphrase"
uart:~$ settings write string zq3/url mqtts://User:[email protected]/User/f/test
If you try writing the settings and get an error, check the section below about erasing the NVM flash partition.
Once you have written the settings, you need to load them from NVM flash. You
can do this by either resetting the board (settings are loaded at boot) or by
running the aio reload Zephyr shell command.
Once the settings are loaded, you should see a "Press BOOT button to connect" message on the Feather TFT's screen.
Press the Feather TFT's Boot button. You should see a "Connecting..." message. Once WiFi is up, the WiFi icon in the statusbar (top right) should turn from gray to green. When MQTT connects, you should see a large toggle switch widget in the center of the screen.
If there are Wifi or MQTT connection errors, you should see an error message on the Feather TFT's screen. To troubleshoot the problem, it's best to connect to the serial shell so you can see more detailed error messages.
Troubleshooting Checklist:
Is your WiFi router working? Can you connect to it with another device?
Does your WiFi router use WPA2-PSK? If you need to use a different type of authentication or encryption, you will need to change the code as it is hardcoded for WPA2-PSK.
Does your WiFi use a captive portal? In that case, it won't work.
-
Are the WiFi SSID and PSK passphrase settings correct? You can check this in the serial shell with:
uart:~$ settings read zq3/ssid 00000000: 4d 79 53 53 49 44 00 |MySSID. | uart:~$ settings read zq3/psk 00000000: 6d 79 20 77 69 66 69 20 70 61 73 73 70 68 72 61 |my wifi passphra| 00000010: 73 65 00 |se. |
Is your MQTT broker working? You can check this with the
mosquitto_pubandmosquitto_subMQTT command line tools for Linux. (check the following sections for more details on using mosquitto as a local MQTT broker).Is the MQTT settings URL correct? The URL format is meant to match the format of
mosquitto_pub -L <URL> ...andmosquitto_sub -L <URL> ...(except this app always requires the:and@).Is your account being throttled by the MQTT broker because of exceeding rate limits? For example, if you use Adafruit IO, you can read about throttling in the Adafruit IO MQTT API web docs.
The settings API uses the NVM backend with the storage partition that is
defined as one of Espressif's default partitions in
zephyr/dts/common/espressif/partitions_0x0_amp_4M.dtsi. My app's
Devicetree configuration gives this partition the label of "settings".
Depending on how you used your Feather TFT ESP32-S3 board previously, there may be existing data in the storage partition. In that case, you might need to erase it.
One option to erase the NVM partition would be to erase all the flash with the
Adafruit ESPTool
ESP32 web flasher tool, then re-program the bootloader and firmware using
west flash.
You could also use Zephyr's flash_map list shell command to find the
partition labeled "settings" and determine its start offset and size. In the
example below, those are 0x3b0000 and 0x30000. Then, you could use the
flash erase shell command to erase the flash blocks for that partition.
For example:
uart:~$ flash_map list
ID | Device | Device Name | Label | Offset | Size
----------------------------------------------------------------------------------
0 0x3c0a91e8 flash-controller@60002000 mcuboot 0x0 0x20000
1 0x3c0a91e8 flash-controller@60002000 image-0 0x20000 0x150000
2 0x3c0a91e8 flash-controller@60002000 image-1 0x170000 0x150000
3 0x3c0a91e8 flash-controller@60002000 image-0-appcpu 0x2c0000 0x70000
4 0x3c0a91e8 flash-controller@60002000 image-1-appcpu 0x330000 0x70000
5 0x3c0a91e8 flash-controller@60002000 image-0-lpcore 0x3a0000 0x8000
6 0x3c0a91e8 flash-controller@60002000 image-1-lpcore 0x3a8000 0x8000
7 0x3c0a91e8 flash-controller@60002000 settings 0x3b0000 0x30000
8 0x3c0a91e8 flash-controller@60002000 image-scratch 0x3e0000 0x1f000
9 0x3c0a91e8 flash-controller@60002000 coredump 0x3ff000 0x1000
uart:~$
uart:~$ flash erase flash-controller@60002000 0x3b0000 0x30000
Erase success.
uart:~$ settings list
uart:~$
If you want to explore the various flash related shell commands, you can try reading their help messages:
uart:~$ flash_map -h
uart:~$ flash -h
uart:~$ settings -h
To see the Kconfig options that enable those shell commands, check out app/prj.conf.
Devicetree:
- boards/adafruit/feather_tft_esp32s3/buttons.dtsi: Button config
- boards/adafruit/feather_tft_esp32s3/mipi_st7789v.dtsi: Display config including comments explaining 4 hardware rotation options
- boards/adafruit/feather_tft_esp32s3/feather_tft_esp32s3_procpu.dts: Peripheral config including "gpio-hog" property for display and backlight power
- boards/adafruit/feather_tft_esp32s3/feather_tft_esp32s3-pinctrl.dtsi: GPIO config for UART, I2C, and SPI
- boards/adafruit/feather_tft_esp32s3/feather_connector.dtsi: GPIO config for Feather header pins
- app/app.overlay: Enables NVM storage partition and random number generator
KConfig & CMake:
-
app/CMakeLists.txt: Selects additional C files (besides
main.c) to include in the build - app/prj.conf: Main config file for Zephyr features. This has many memory allocation tuning settings that were critical for getting the networking to run reliably. See comments for how to enable debug logging, stack usage monitoring, and heap usage monitoring.
C Code:
- app/src/main.c: Hardware initialization, event handler callback functions (WiFi, MQTT, LVLG, settings), and main event loop with state machine to track network status
- app/src/zq3.h: Enum and struct definitions used by callbacks and event loop state machine.
- app/src/zq3_cert.h: String literals for compiling PEM format TLS CA certificates into the firmware. This includes my self-signed CA test certificate (which you can replace with one of your own) along with the "DigiCert Global Root G2" and "GeoTrust TLS RSA CA G1" certificates for use with Adafruit IO.
- app/src/zq3_dns.c + zq3_dns.h: Resolve DNS hostname string for MQTT broker to an IPv4 address (also works for converting IP address string to the IPv4 address struct format)
- app/src/zq3_lvgl.c + app/src/zq3_lvgl.h: LVGL graphical user interface: background color, WiFi status icon, text status message, large toggle switch widget, etc.
- app/src/zq3_mqtt.c + app/src/zq3_mqtt.h: MQTT broker config, TLS certificate registration, connection setup, publish and subscribe, etc.
- app/src/zq3_url.c + app/src/zq3_url.h: MQTT broker URL string parser. This is meant to work with the Zephyr Settings API. URL format is meant to match the format used by
mosquitto_sub -Landmosquitto_pub -Lto assist with testing on a private network, with Adafruit IO, or using some other MQTT broker (might need to change CA cert). - app/src/zq3_wifi.c + app/src/zq3_wifi.h: WiFi network connect and disconnect using Zephyr's Network Manager API.
C Language Features
To use Zephyr APIs, you need to understand some C features and patterns for combining them: structs, functions, pointers, function pointers, struct pointers, typedef, compound literals, etc.
Experienced C programmers will probably be familiar with this stuff already. But, for people new to C, these are some concepts and search terms you may want to explore:
-
Struct: Structs are a way of grouping related data, kind of like a Python class minus the methods. The main utility of structs is they make it easier to pass data around between functions. Struct definitions commonly happen in header files (
.h), and they might look likestruct {int foo; char buf[32];};or perhapstypedef struct {int foo; char buf[32];} foo_t; -
Pointer: Basically, a pointer is a special kind of integer that holds a memory address where some other larger thing is stored. Instead of spending lots of memory and CPU time to copy the larger thing, functions can take pointers as arguments. "Dereferencing" a pointer is how C uses a pointer to find the thing it points to. Pointer operations are commonly spelled with
*,&, and->. Also, the names of arrays, likegreetinchar greet[] = "hello";, are pointers. So it wouldn't make sense to say&greet, because it's already a pointer. - Function: Functions take arguments (int, float, pointer, or whatever) and return a result. The number and type of arguments, and the type of the return value, make a kind of fingerprint that the C compiler uses to check that function calls match function declarations. Trying to call a function with the wrong type or number of arguments is an error. Some related terms are "function signature" and "function prototype".
-
Function Pointer: When you write C code like
printf("hello, world\n");, you're making a call to the function namedprintf. But you can also use useprintfas the argument to a function, likefoo(printf);. In that case,foo()is a function that takes a function pointer as its argument, andprintfis the function pointer. A common use for function pointers is to tell some library code that you want it to call a certain function when something happens in the future. Effectively, it's a way to pass an algorithm or procedure (as opposed to just data) as the argument to a function. -
Typedef: C allows you to create named types that act as an alias for types like structs, enums, function pointers, and so on. It's common to use
typedefwhen the spelling out the full type each time you want to use it would be awkwardly long, as is often the case for structs and function pointers. -
Preprocessor Macro: Macros are another C feature that can be used to avoid typing out awkwardly long or complicated things. Zephyr APIs sometimes use macros for registering callback functions. Macro names are often spelled in all caps, like,
I_AM_A_MACRO(and, these, are, my, arguments);. -
Event Hander Callback Function: Event based library API's, such as the ones Zephyr provides for networking and graphics, allow you to register "callback" functions that should be called to handle events that may happen in the future. The API function for registering event handlers will take a function pointer, often with a name ending in
_cb, which is expected to match a particular function signature (check the library API docs). Usually, the callback function signature will provide a pointer to a struct as one of its arguments. Your code is expected to dereference the struct pointer and examine its contents to get data related to the event. - Compound Literals: Sometimes, to use Zephyr API's (e.g. making an MQTT connection), you are expected to create structs and initialize them with fairly elaborate configurations. The C99 feature called "compound literals" can be a helpful way to do this.
-
Scope and Use After Free: When using library APIs that take pointers, it's very important to consider the scope and lifetime of structs or array buffers if you use them with pointers. For example, suppose you have a function called
foo()that declares a struct calledbaras a local variable. What happens iffoo()saves a pointer tobar(&bar) somewhere and then returns? That pointer tobarwill now be dangerously pointing to the stack address wherebarused to be located, but sincefoo()has returned, that location on the stack is probably being used for something else (notbar!). Similar problems can happen with dynamic memory allocation (malloc(),free(), etc). -
Null Pointers: A pointer variable that contains the special value,
NULL, is called a null pointer. Many C bugs are caused by attempting to dereference null pointers. It's important for C functions that take pointer arguments to consider how they should behave if they receive a null pointer. Failing to check for null pointers may lead to the Zephyr kernel stopping your application with a hard fault error.
To learn more about the C language, the authoritative reference book is the "C Programming Language, 2nd Edition" by Kernighan and Ritchie, sometimes called "K&R", or the K&R C book. It's very good for understanding basic concepts of the C language, although some of the examples may show usage patterns that are not considered best practice now. To learn about newer C99 features, the GNU gcc compiler documentation might be useful (but it's a long read).
Zephyr Troubleshooting
TL;DR: To get a Zephyr IoT app working reliably, you will probably need to carefully tune various stack, heap, and buffer sizes in your Kconfig options. That will be easier if you learn how to use Zephyr Shell diagnostic commands and how to turn on debug logging (more complex than it sounds).
Long Version: While developing this app over the last month, I spent a significant amount of time diagnosing and debugging issues that turned out to be ultimately caused by memory allocation failures. Zephyr is built to use several threads, each with their own stack, along with various buffers and heaps reserved for different purposes. To figure out what's going on with memory allocation, Zephyr provides configuration features to enable measurements and Zephyr Shell commands for checking the measurements. There are also some configuration options that enable debug logging for memory allocation failures.
If you want to build a moderately complex app in Zephyr, you will probably need to get good at tuning memory allocations and enabling the different types of debug logging:
- Zephyr's Menuconfig tool for interactively setting build configuration options has a search function that works like the one in the vim editor (type
/to begin the search). For example, you can do searches like/log_level,/mbedtls,/stack, or/mem_pool. - Menuconfig has a help feature for reading descriptions of config options. When you have an option selected in the menuconfig tree view, you can press
?to bring up the help text for the selected option. - Look in my app/prj.conf file for comments about configuration features and shell commands that are useful for enabling debug logging and tuning memory use.
- It can be helpful to use a text search tool that can recursively search all the files in a directory for regular expression patterns. Many IDEs and programming editors have such search features. I'm partial to using
grep. For example, I often usedgrep -r 'some regex pattern' *in the top level of the zephyr repo directory to find samples, tests, or kernel code that mentioned particular config options I was trying to understand.
At this point, you might be wondering, "Why worry about memory allocation on an ESP32-S3 soc that has 2MB of PSRAM?" Good question. Indeed, it would be great if we could use the PSRAM for network buffers. But, alas, that is not currently practical.
There are Zephyr configuration options for enabling "SPIRAM" support on the ESP32-S3 and for using SPIRAM for heap and network buffers. However, those features don't currently work. When I tried it, the WiFi system failed to initialize due to memory allocation errors. At the time I'm writing this (late March 2025), there are at least two open issues and one pull request mentioning ESP32-S3 SPIRAM problems.
Mosquitto is the name of an open source project that provides an MQTT broker and MQTT client programs to publish and subscribe from the command line.
For developing an MQTT application on Zephyr, it can be helpful to run your own MQTT broker locally on a private network (Raspberry Pi, Debian box, or whatever).
Writing the Zephyr code for an MQTT client application involves many steps. Attempting to do all of that at once is challenging. You might find it easier to start with basic unencrypted (non-TLS) MQTT connections on a private network, then incrementally add encryption and authentication support.
CAUTION: Using this type of configuration on public WiFi is not safe. In the example here, I'm using a private Ethernet LAN (no internet gateway) with a dedicated WiFi AP that I use for testing.
This is how I set up a Debian box with the mosquitto MQTT broker for
unencrypted and unauthenticated access on my WiFi test network:
Install packages and configure the mosquitto server for manual start:
$ sudo apt install mosquitto mosquitto-clients
$ sudo systemctl stop mosquitto
$ sudo systemctl disable mosquitto
Check the IP address assigned to my WiFi interface with hostname -I. In this
case, I have a IP 10.0.x.x address for the private test LAN and a 192.168.0.x
address for the main internet connected WiFi router:
$ hostname -I
10.0.0.10 192.168.0.100
Reconfigure mosquitto MQTT broker to listen only on the private LAN:
$ cat <<EOF | sudo tee /etc/mosquitto/conf.d/LAN-listener.conf
persistence false
allow_anonymous true
listener 1883 10.0.0.10
EOF
$ sudo systemctl start mosquitto
On Debian, if you install the mosquitto-clents package, you can publish and
subscribe to MQTT topics from the command line. For example, assuming you were
running an MQTT broker listening on IP address 192.168.0.100, you could start
two terminal windows (or use tmux), and do this:
Terminal 1 (use Ctrl-C to disconnect mosquitto_sub when done):
$ mosquitto_sub --debug -v -L mqtt://192.168.0.100/test
Client (null) sending CONNECT
Client (null) received CONNACK (0)
Client (null) sending SUBSCRIBE (Mid: 1, Topic: test, QoS: 0, Options: 0x00)
Client (null) received SUBACK
Subscribed (mid: 1): 0
Client (null) received PUBLISH (d0, q0, r0, m0, 'test', ... (11 bytes))
test hello world
^CClient (null) sending DISCONNECT
Terminal 2:
$ mosquitto_pub --debug -L mqtt://192.168.0.100/test -m "hello world"
Client (null) sending CONNECT
Client (null) received CONNACK (0)
Client (null) sending PUBLISH (d0, q0, r0, m1, 'test', ... (11 bytes))
Client (null) sending DISCONNECT
If you wanted to test an authenticated and TLS encrypted connection to the
Adafruit IO MQTT broker, you could do something like this (replacing $USER
and $KEY with your AIO username and API key):
Terminal 1:
$ mosquitto_sub -L mqtts://$USER:[email protected]/$USER/f/test
Terminal 2:
$ mosquitto_pub -L mqtts://$USER:[email protected]/$USER/f/test -m 1
Note: In the MQTT broker URL, the URL scheme, mqtt:// or mqtts://, implicitly selects the port. The mqtt scheme defaults to port 1883. The mqtts
scheme defaults to port 8883.
To upgrade your local mosquitto MQTT broker to TLS, first you need to make
sure you have the openssl command line tool installed. Try openssl version
from a terminal. If that doesn't work, you can install openssl with:
sudo apt install openssl
Once you have openssl, you'll need to make a CA certificate and private key, then use those to sign a server key, then install those in the mosquitto configuration directory.
-
Change to a temporary working directory
mkdir ~/ca-cert cd ~/ca-cert
-
Create a Certificate Authority (CA) private key and certificate file
openssl req -newkey rsa:2048 -noenc -x509 -days 730 -extensions v3_ca \ -subj "/C=US/O=MyCA/CN=My Self-Signed CA" -keyout ca.key -out ca.crt
CAUTION: The mbed TLS certificate parser appears to care about the contents of the subject fields and perhaps the validity period. If your connection closes mysteriously and you see a
-0x2180error code from the mbed TLS debug logging, it may be unhappy with the subject or days. Or, it's also possible the problem could be due to a memory allocation failure. Certificate parsing uses mbed TLS heap space. -
Create a server private key and Certificate Signing Request (CSR). The stuff with
$(hostname -I|awk '{print $1}')is a way to automatically fill in the first hostname reported byhostname -I. You could just type in10.0.0.10or whatever instead if you wanted.openssl req -newkey rsa:2048 -noenc \ -subj "/CN=$(hostname -I|awk '{print $1}')" \ -keyout server.key -out server.csr -
Use your CA's certificate and private key to sign the CSR
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \ -CAcreateserial -copy_extensions copy -days 365 -out server.crt
-
Check contents of the PEM files
openssl x509 -in ca.crt -text -noout openssl req -in server.csr -text -noout openssl x509 -in server.crt -text -noout
-
Move the files into the /etc/mosquitto configuration directory and change their permissions. The point of this is to make the certificates publicly visible so you can use them with
mosquitto_pub, etc. while the private keys can only be used root or the mosquitto server.cd ~/ca-certs chmod 600 ca.* server.* sudo mv ca.* server.* /etc/mosquitto/ca_certificates/ cd /etc/mosquitto/ca_certificates/ sudo chown root:root ca.* server.* sudo chmod 600 ca.* server.* sudo chmod 644 *.crt sudo chown root:mosquitto server.key sudo chmod 640 server.key
The end result should have permissions that look like this:
$ ls -l /etc/mosquitto/ca_certificates/ total 28 -rw-r--r-- 1 root root 1135 Mar 22 12:00 ca.crt -rw------- 1 root root 1704 Mar 22 12:00 ca.key -rw------- 1 root root 41 Mar 22 12:00 ca.srl -rw-r--r-- 1 root root 73 Sep 30 2023 README -rw-r--r-- 1 root root 1131 Mar 22 12:00 server.crt -rw------- 1 root root 944 Mar 22 12:00 server.csr -rw-r----- 1 root mosquitto 1704 Mar 22 12:00 server.key
-
Modify mosquitto config to use TLS (the
$(hostname -I|awk '{print $1}')thing evaluates to the first IP address returned byhostname -I, in the case of this example, that would be10.0.0.10)cat <<EOF | sudo tee /etc/mosquitto/conf.d/LAN-listener.conf # To read the log, do: sudo tail -f /var/log/mosquitto/mosquitto.log log_type all per_listener_settings true listener 1883 $(hostname -I|awk '{print $1}') allow_anonymous true persistence false listener 8883 $(hostname -I|awk '{print $1}') allow_anonymous true persistence false certfile /etc/mosquitto/ca_certificates/server.crt keyfile /etc/mosquitto/ca_certificates/server.key EOF -
Restart mosquitto
sudo systemctl restart mosquitto
If all that worked, you can use mosquitto_sub and mosquito_pub over TLS by
changing to -L mqtts:// (note the "s") and adding a --cafile ... option.
For example to start a subscriber:
mosquitto_sub --cafile /etc/mosquitto/ca_certificates/ca.crt \
-L mqtts://10.0.3.17/test
And to publish:
mosquitto_pub --cafile /etc/mosquitto/ca_certificates/ca.crt \
-L mqtts://10.0.3.17/test -m 1
To check the Adafruit IO certificate chain with the openssl command line tool
from a terminal shell on Debian 12:
openssl s_client -showcerts -connect io.adafruit.com:8883
The output is long, but the main relevant points are:
- First cert:
O = Adafruit Industries LLC, CN = *.adafruit.comwitha:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256andNotAfter: Aug 2 23:59:59 2025 GMT - Second cert:
OU = www.digicert.com, CN = GeoTrust TLS RSA CA G1witha:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256andNotAfter: Nov 2 12:23:37 2027 GMT - Third cert:
OU = www.digicert.com, CN = DigiCert Global Root G2 - Negotiated connection used
TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
This page (Zephyr Quest: IoT Toggle Switch for Feather TFT) was last updated on March 30, 2025.
Text editor powered by tinymce.