Skip to content

examples/mqttc: add subscriber client implementation example#3618

Merged
xiaoxiang781216 merged 1 commit into
apache:masterfrom
dmihai03:mqtt-sub
Jul 13, 2026
Merged

examples/mqttc: add subscriber client implementation example#3618
xiaoxiang781216 merged 1 commit into
apache:masterfrom
dmihai03:mqtt-sub

Conversation

@dmihai03

@dmihai03 dmihai03 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR adds a MQTT-C subscriber client example to examples/mqttc, alongside the existing publisher, and makes it robust enough to run on a real (Wi-Fi) target.

The subscriber (mqttc_sub) is built as a separate program, selectable independently from the publisher. Beyond the basic subscribe loop, the change addresses several issues found when running on NuttX hardware:

  • Automatic reconnect. The client is now set up through mqtt_init_reconnect() with a reconnect_client() callback that rebuilds the transport (TCP + optional TLS), re-runs mqtt_reinit(), and re-issues CONNECT/SUBSCRIBE. This handles both the initial connection and recovery after broker/link errors, retrying with a short back-off instead of aborting. The callback intentionally does not return until a transport connection exists, because mqtt_sync() would otherwise operate on the sentinel socketfd (-1) and crash.
  • Wait for the network before connecting. wait_for_network() polls the interface (netlib_get_ipv4addr) until it has a usable IPv4 address or a timeout elapses, avoiding the spurious first-attempt ENETUNREACH/connect() failure that occurs when the example starts before Wi-Fi association/DHCP complete.
  • mbedTLS non-blocking BIO callbacks. Custom mqttc_net_send/mqttc_net_recv wrappers inspect errno directly and return MBEDTLS_ERR_SSL_WANT_READ/WRITE on EAGAIN. The stock mbedtls_net_recv() relies on fcntl(F_GETFL) reporting O_NONBLOCK, which NuttX does not retain (it applies non-blocking mode via FIONBIO), so the stock callback turned a benign EAGAIN into MBEDTLS_ERR_NET_RECV_FAILED.
  • Bounded message copy. publish_callback() now copies the incoming payload into a fixed-size buffer with an explicit length clamp instead of null-terminating the library's receive buffer in place (a potential out-of-bounds write).
  • Kconfig cleanup + TLS toggle. Split publisher/subscriber program-name options, added EXAMPLES_MQTTC_SUB to enable the subscriber, and added EXAMPLES_MQTTC_MBEDTLS to enable MQTT-over-TLS (guarded by NETUTILS_MQTTC_WITH_MBEDTLS). Both the CMake and Make build paths were updated accordingly.
  • Replaced the embedded PolarSSL test CA with the broker's own CA certificate (used when no CA file is supplied via -c).

Impact

  • Users: New optional example program mqttc_sub. Disabled by default (EXAMPLES_MQTTC_SUB=n), so existing configurations are unaffected unless explicitly enabled.
  • Build: Adds Kconfig symbols and build rules in both CMakeLists.txt and Makefile.
  • Security: TLS support is opt-in via mbedTLS. Note the example embeds a CA certificate in firmware and uses MBEDTLS_SSL_VERIFY_OPTIONAL; the -c option allows supplying a CA at runtime. Users should substitute their own broker CA.

Testing

Host:

  • OS: Linux 7.0.12-arch1-1
  • Compiler: xtensa-esp32-elf-gcc (crosstool-NG esp-12.2.0_20230208) 12.2.0

Target:

  • Arch / board:config: esp32-devkitc:wifi

Steps:

  1. Enabled EXAMPLES_MQTTC, EXAMPLES_MQTTC_SUB (and EXAMPLES_MQTTC_MBEDTLS for the TLS run).
  2. Built and flashed; ran mqttc_sub -h -p -t -q .
  3. Published to the topic from another client and confirmed Received: ... output.
  4. Verified reconnect by restarting the broker / dropping Wi-Fi and observing the subscriber re-subscribe automatically

Broker logs when subscribing:

1782043828: New connection from 10.42.0.218:5289 on port 1883.
1782043828: New client connected from 10.42.0.218:5289 as auto - FCF3834C- B431 - FCE3 -10 AA -78 D82014BC2E ( p4 ,c1 , k400 ) .
1782043828: No will message specified .
1782043828: Sending CONNACK to auto - FCF3834C - B431 - FCE3 -10 AA -78D82014BC2E (0 , 0)
1782043828: Received SUBSCRIBE from auto - FCF3834C - B431 - FCE3 -10 AA -78D82014BC2E
1782043828: test ( QoS 2)
1782043828: auto - FCF3834C - B431 - FCE3 -10 AA -78 D82014BC2E 0 test
1782043828: Sending SUBACK to auto - FCF3834C - B431 - FCE3 -10 AA -78 D82014BC2E

Reconnect ping sent once in 5 minutes:

1782044129: Received PINGREQ from auto - FCF3834C - B431 - FCE3 -10 AA -78D82014BC2E
1782044129: Sending PINGRESP to auto - FCF3834C - B431 - FCE3 -10 AA -78D82014BC2E
1782044430: Received PINGREQ from auto - FCF3834C - B431 - FCE3 -10 AA -78D82014BC2E
1782044430: Sending PINGRESP to auto - FCF3834C - B431 - FCE3 -10 AA -78D82014BC2E
1782044731: Received PINGREQ from auto - FCF3834C - B431 - FCE3 -10 AA -78D82014BC2E
1782044731: Sending PINGRESP to auto - FCF3834C - B431 - FCE3 -10 AA -78D82014BC2E

Broker forwarding QoS 2 message:

1782064339: New connection from 10.42.0.156:27670 on port 1883.
1782064339: New client connected from 10.42.0.156:27670 as auto -2D924D93 - E286 -247 E -9118 -2251 D88786D6 ( p4 , c1 , k400 ) .
1782064339: No will message specified .
1782064339: Sending CONNACK to auto -2 D924D93 - E286 -247 E -9118 -2251D88786D6 (0 , 0)
1782064339: Received PUBLISH from auto -2 D924D93 - E286 -247 E -9118 -2251D88786D6 ( d0 , q2 , r0 , m46161 , ’ test ’ , ... (11 bytes ) )
1782064339: Sending PUBREC to auto -2 D924D93 - E286 -247 E -9118 -2251 D88786D6( m46161 , rc0 )
1782064339: Received PUBREL from auto -2 D924D93 - E286 -247 E -9118 -2251D88786D6 ( Mid : 46161)
1782064339: Sending PUBLISH to auto -36 E9D2AE -81 C2 -3 C45 -6 EA1 -085D5F124942 ( d0 , q2 , r0 , m2 , ’ test ’ , ... (11 bytes ) )
1782064339: Sending PUBCOMP to auto -2 D924D93 - E286 -247 E -9118 -2251D88786D6 ( m46161 )
1782064339: Received PUBREC from auto -36 E9D2AE -81 C2 -3 C45 -6 EA1 -085D5F124942 ( Mid : 2)
1782064339: Sending PUBREL to auto -36 E9D2AE -81 C2 -3 C45 -6 EA1 -085 D5F124942( m2 )
1782064339: Received PUBCOMP from auto -36 E9D2AE -81 C2 -3 C45 -6 EA1 -085D5F124942 ( Mid : 2 , RC :0)

Heap usage:

  • client without TLS: 14kB
  • client running TLS: 60kB

Flash memory usage (total image size):

  • client without TLS: 802kB
  • client running TLS: 1MB

@acassis

acassis commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Nice work @dmihai03
Please check these issues:

 Error: /home/runner/work/nuttx-apps/nuttx-apps/apps/examples/mqttc/mqttc_sub.c:53:78: error: Long line found
Error: /home/runner/work/nuttx-apps/nuttx-apps/apps/examples/mqttc/mqttc_sub.c:154:78: error: Long line found
Error: /home/runner/work/nuttx-apps/nuttx-apps/apps/examples/mqttc/mqttc_sub.c:274:2: error: C++ style comment
Error: /home/runner/work/nuttx-apps/nuttx-apps/apps/examples/mqttc/mqttc_sub.c:276:2: error: C++ style comment
Error: /home/runner/work/nuttx-apps/nuttx-apps/apps/examples/mqttc/mqttc_sub.c:276:25: error: Space follows left parenthesis
Error: /home/runner/work/nuttx-apps/nuttx-apps/apps/examples/mqttc/mqttc_sub.c:276:43: error: Space precedes right parenthesis
Error: /home/runner/work/nuttx-apps/nuttx-apps/apps/examples/mqttc/mqttc_sub.c:281:3: error: Right brace must be followed by a blank line
Error: /home/runner/work/nuttx-apps/nuttx-apps/apps/examples/mqttc/mqttc_sub.c:415:1: error: Blank line contains whitespace
Error: /home/runner/work/nuttx-apps/nuttx-apps/apps/examples/mqttc/mqttc_sub.c:616:78: error: Long line found
Error: /home/runner/work/nuttx-apps/nuttx-apps/apps/examples/mqttc/mqttc_sub.c:667:7: error: Mixed case identifier found
Error: /home/runner/work/nuttx-apps/nuttx-apps/apps/examples/mqttc/mqttc_sub.c:684:78: error: Long line found
Error: /home/runner/work/nuttx-apps/nuttx-apps/apps/examples/mqttc/mqttc_sub.c:698:1: error: Blank line contains whitespace
Error: /home/runner/work/nuttx-apps/nuttx-apps/apps/examples/mqttc/mqttc_sub.c:725:26: error: Operator/assignment must be preceded with whitespace
Error: /home/runner/work/nuttx-apps/nuttx-apps/apps/examples/mqttc/mqttc_sub.c:734:7: error: Mixed case identifier found
Error: /home/runner/work/nuttx-apps/nuttx-apps/apps/examples/mqttc/mqttc_sub.c:817:1: error: Missing blank line after comment
Error: /home/runner/work/nuttx-apps/nuttx-apps/apps/examples/mqttc/mqttc_sub.c:818:2: error: Missing whitespace after keyword
Error: /home/runner/work/nuttx-apps/nuttx-apps/apps/examples/mqttc/mqttc_sub.c:820:1: error: Missing blank line after comment

I noticed there was already a Mixed case identifier in the original code, please add MQTTErrors to g_white_prefix at nuttx/tools/nxstyle.c to fix this Mixed case error.

Some suggestion to improve to original code too: move the certificate string to a separated cert.inc and just add #include "cert.inc" to keep it separated from the source code.

@dmihai03

dmihai03 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

Thank you @acassis!
Should I modify the publisher as well to use the include file for the certificate?

@acassis

acassis commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Thank you @acassis! Should I modify the publisher as well to use the include file for the certificate?

Yes, please!

Later I will fix these too:
$ git grep "BEGIN CERTIFICATE"
crypto/controlse/ccertificate.cxx:static constexpr char certificate_header[] = "-----BEGIN CERTIFICATE-----\n";
crypto/controlse/ccsr.cxx: = "-----BEGIN CERTIFICATE REQUEST-----\n";

acassis
acassis previously approved these changes Jul 8, 2026
@cederom

cederom commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Very nice, big thank you @dmihai03 and thank your for very good PR description with testing!! :-)

CI check still complains about these, please take a look :-)

❌ Missing Signed-off-by
ERROR __main__.py:618: Check failed: /home/runner/work/nuttx-apps/nuttx-apps/apps/examples/mqttc/CMakeLists.txt

cmake-format check failed, run following command to update the style:
  $ cmake-format <src> -o <dst>

ERROR __main__.py:618: Check failed: /home/runner/work/nuttx-apps/nuttx-apps/apps/examples/mqttc/CMakeLists.txt
Used config files:
    1: .codespellrc
Error: /home/runner/work/nuttx-apps/nuttx-apps/apps/examples/mqttc/mqttc_pub.c:470:7: error: Mixed case identifier found
Error: /home/runner/work/nuttx-apps/nuttx-apps/apps/examples/mqttc/mqttc_sub.c:644:7: error: Mixed case identifier found
Error: /home/runner/work/nuttx-apps/nuttx-apps/apps/examples/mqttc/mqttc_sub.c:711:7: error: Mixed case identifier found
Some checks failed. For contributing guidelines, see:
  https://github.com/apache/nuttx/blob/master/CONTRIBUTING.md

@dmihai03

dmihai03 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

ERROR main.py:618: Check failed: /home/runner/work/nuttx-apps/nuttx-apps/apps/examples/mqttc/CMakeLists.txt
Used config files:
1: .codespellrc
Error: /home/runner/work/nuttx-apps/nuttx-apps/apps/examples/mqttc/mqttc_pub.c:470:7: error: Mixed case identifier found
Error: /home/runner/work/nuttx-apps/nuttx-apps/apps/examples/mqttc/mqttc_sub.c:644:7: error: Mixed case identifier found
Error: /home/runner/work/nuttx-apps/nuttx-apps/apps/examples/mqttc/mqttc_sub.c:711:7: error: Mixed case identifier found

For these errors I've opened a PR where I added MQTTErrors into the nxstyle.c.

acassis
acassis previously approved these changes Jul 9, 2026
@acassis

acassis commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

@xiaoxiang781216 PTAL, his patch to nxstyle is already on mainline

Comment thread examples/mqttc/CMakeLists.txt
Comment thread examples/mqttc/Kconfig Outdated
@cederom

cederom commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

@dmihai03 dmihai03 force-pushed the mqtt-sub branch 2 times, most recently from c07a1fa to 4f02ba5 Compare July 10, 2026 12:13
Comment thread examples/mqttc/mqttc_sub.c Outdated
Comment thread examples/mqttc/mqttc_sub.c Outdated
@xiaoxiang781216

Copy link
Copy Markdown
Contributor

@dmihai03 please rebase to the last master which fix the ci build error.

@dmihai03 dmihai03 force-pushed the mqtt-sub branch 2 times, most recently from 512e1e3 to ac2c4b7 Compare July 11, 2026 17:33
@xiaoxiang781216

Copy link
Copy Markdown
Contributor

@dmihai03 please fix the build break:

====================================================================================
Configuration/Tool: esp32-devkitc/mqttc
2026-07-11 18:54:35
------------------------------------------------------------------------------------
  Cleaning...
  Configuring...
  Building NuttX...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed

  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0

100 13168    0 13168    0     0  37602      0 --:--:-- --:--:-- --:--:-- 37602
100  277k    0  277k    0     0   627k      0 --:--:-- --:--:-- --:--:-- 2876k
Error: mqttc_pub.c:465:10: error: expected identifier or '(' before 'int'
  465 | int main(int argc, FAR char *argv[])
      |          ^~~
Error: mqttc_pub.c:447:13: error: 'close_conn' defined but not used [-Werror=unused-function]
  447 | static void close_conn(FAR struct mqtt_conn_context_s *conn)
      |             ^~~~~~~~~~
Error: mqttc_pub.c:202:12: error: 'init_conn' defined but not used [-Werror=unused-function]
  202 | static int init_conn(FAR const struct mqttc_cfg_s *cfg,
      |            ^~~~~~~~~
Error: mqttc_pub.c:137:13: error: 'parsearg' defined but not used [-Werror=unused-function]
  137 | static void parsearg(int argc, FAR char *argv[],
      |             ^~~~~~~~
Error: mqttc_pub.c:118:18: error: 'client_refresher' defined but not used [-Werror=unused-function]
  118 | static FAR void *client_refresher(FAR void *data)
      |                  ^~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
make[2]: *** [/github/workspace/sources/apps/Application.mk:330: mqttc_pub.c.github.workspace.sources.apps.examples.mqttc.o] Error 1
make[2]: Target 'all' not remade because of errors.
make[1]: *** [Makefile:54: /github/workspace/sources/apps/examples/mqttc_all] Error 2
make[1]: Target 'all' not remade because of errors.
make: *** [tools/LibTargets.mk:248: /github/workspace/sources/apps/libapps.a] Error 2
make: Target 'all' not remade because of errors.
/github/workspace/sources/nuttx/tools/testbuild.sh: line 397: /github/workspace/sources/nuttx/../nuttx/nuttx.manifest: No such file or directory
  [1/1] Normalize esp32-devkitc/mqttc
/github/workspace/sources/nuttx /github/workspace/sources/nuttx
On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean
/github/workspace/sources/nuttx
/github/workspace/sources/apps /github/workspace/sources/nuttx
HEAD detached at pull/3618/merge
nothing to commit, working tree clean

    Implement the subscriber client that optionally integrates
    the TLS layer and can reconnect to the broker if the
    application is running in unstable network environments.

    The client is compliant with all 3 levels of QoS.

Signed-off-by: Mihai Pacuraru <mpacuraru@protonmail.com>
@xiaoxiang781216 xiaoxiang781216 merged commit 3762a64 into apache:master Jul 13, 2026
41 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants