<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged mqtt</title>
<link>https://coderlegion.com/tag/mqtt</link>
<description>Powered by Question2Answer</description>
<item>
<title>Updating Radar Firmware Without a Cloud: Self-Hosted OTA over MQTT</title>
<link>https://coderlegion.com/32750/updating-radar-firmware-without-a-cloud-self-hosted-ota-over-mqtt</link>
<description>&lt;p&gt;Updating Radar Firmware Without a Cloud: Self-Hosted OTA over MQTT&lt;/p&gt;
&lt;p&gt;Most connected devices want you to push firmware through somebody's cloud. These do not ask for that. The sleep and fall radars fetch an image from a URL you hand them, and the command arrives on the same MQTT connection the telemetry already uses. Nothing has to leave your network, and there is no vendor account in the path.&lt;/p&gt;
&lt;p&gt;This is the practical shape of that: which units support it, the three commands involved, how to confirm an update actually landed, and the operational precautions the protocol document leaves to you.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Everything below comes from the Wi-Fi protocol documents for the sleep and fall units. Where they do not specify something, this says so rather than filling the gap.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;scope-first&quot;&gt;Scope first&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Unit &lt;/th&gt;&lt;th&gt;OTA update via this protocol&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Sleep radar (60 GHz, Wi-Fi) &lt;/td&gt;&lt;td&gt;Yes — topic family &lt;code&gt;/Radar60SP/{DeviceID}/sys/property/{post,set}&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Fall radar (60 GHz, Wi-Fi) &lt;/td&gt;&lt;td&gt;Yes — topic family &lt;code&gt;/Radar60FL/{DeviceID}/sys/property/{post,set}&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Presence radar (24 GHz, Wi-Fi) &lt;/td&gt;&lt;td&gt;No such command appears in its protocol document&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;That last row matters. The presence unit's document specifies &lt;code&gt;online&lt;/code&gt;, &lt;code&gt;version&lt;/code&gt;, &lt;code&gt;active&lt;/code&gt; and &lt;code&gt;passive&lt;/code&gt; report topics plus a &lt;code&gt;property/set&lt;/code&gt; command topic — and no update command at all. If you need to update those, that is a question for the hardware, not for this protocol.&lt;/p&gt;
&lt;p&gt;Commands go to the unit's &lt;code&gt;.../sys/property/set&lt;/code&gt; topic; everything it reports arrives on &lt;code&gt;.../sys/property/post&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;three-commands&quot;&gt;Three commands&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1. Select the target module&lt;/strong&gt; — required before every update.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{&quot;version&quot;: &quot;1.0&quot;, &quot;method&quot;: &quot;ota_set&quot;, &quot;params&quot;: {&quot;module&quot;: &quot;0&quot;}}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;0&lt;/code&gt; is the Wi-Fi module, &lt;code&gt;1&lt;/code&gt; is the radar module; the default is &lt;code&gt;0&lt;/code&gt;. The documentation is explicit that this control command has to be sent before each update — it is not a one-time setting you configure and forget.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Hand over the image location.&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{&quot;version&quot;: &quot;1.0&quot;, &quot;method&quot;: &quot;ota_set&quot;,
 &quot;params&quot;: {&quot;otaInform&quot;: {&quot;firmwareUrl&quot;: &quot;http://192.168.31.88/app.rbl&quot;, &quot;port&quot;: &quot;8090&quot;}}}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The device fetches the firmware from that URL and port and starts updating. A failure comes back as:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{&quot;version&quot;: &quot;1.0&quot;, &quot;opt&quot;: &quot;otaInform&quot;, &quot;res&quot;: &quot;fail&quot;, &quot;msg&quot;: &quot;ota failed!&quot;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;3. Observe progress.&lt;/strong&gt; Described as an active report during update, sent with method &lt;code&gt;ota_post&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{&quot;version&quot;: &quot;1.0&quot;, &quot;method&quot;: &quot;ota_post&quot;, &quot;params&quot;: {&quot;downloadProgress&quot;: &quot;0&quot;}}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Values run &lt;code&gt;0&lt;/code&gt;–&lt;code&gt;100&lt;/code&gt;. Subscribe to the unit's report topic before you start; if your unit reports it somewhere else, record that during commissioning rather than assuming.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;After a successful update&lt;/strong&gt;, the module restarts and then reports its version number. Treat that report — not the arrival of &lt;code&gt;100&lt;/code&gt; — as the completion signal.&lt;/p&gt;
&lt;h3 id=&quot;confirming-the-result&quot;&gt;Confirming the result&lt;/h3&gt;
&lt;p&gt;&quot;Transfer finished&quot; and &quot;the unit now runs the new code&quot; are two different statements.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Sleep and fall units&lt;/strong&gt; carry &lt;code&gt;firmwareVersion&lt;/code&gt; and &lt;code&gt;firmwareVersionWiFi&lt;/code&gt; in their property list, and the protocol states that the post-restart report carries the version number after updating. Compare against the value you recorded before starting.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Presence units&lt;/strong&gt; publish a version snapshot on &lt;code&gt;/RadarHP/{DeviceID}/sys/version/post&lt;/code&gt; containing &lt;code&gt;radarID&lt;/code&gt;, &lt;code&gt;softwareVersion&lt;/code&gt;, &lt;code&gt;firmwareVersion&lt;/code&gt; and &lt;code&gt;protocolVersion&lt;/code&gt;. These are read-only — they cannot be written, only read. There is also a request command to solicit it on demand: publish &lt;code&gt;{&quot;version&quot;:&quot;1.0&quot;,&quot;method&quot;:&quot;get_version&quot;,&quot;params&quot;:{}}&lt;/code&gt; to &lt;code&gt;/RadarHP/{DeviceID}/sys/property/set&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Keep the before-and-after version pair in the same commissioning record you keep for everything else. It is the only evidence you have later.&lt;/p&gt;
&lt;h3 id=&quot;running-an-update&quot;&gt;Running an update&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import json
import paho.mqtt.client as mqtt

BROKER, PORT = &quot;192.168.1.2&quot;, 1883
DEV = &quot;&amp;lt;DeviceID&amp;gt;&quot;                        # the MAC, printed on the label
SET_TOPIC  = f&quot;/Radar60SP/{DEV}/sys/property/set&quot;
POST_TOPIC = f&quot;/Radar60SP/{DEV}/sys/property/post&quot;

IMAGE_URL, IMAGE_PORT = &quot;http://192.168.1.50:8090/app.rbl&quot;, &quot;8090&quot;
TARGET = &quot;0&quot;                              # &quot;0&quot; Wi-Fi module, &quot;1&quot; radar module


def command(client, method, params):
    client.publish(SET_TOPIC,
                   json.dumps({&quot;version&quot;: &quot;1.0&quot;, &quot;method&quot;: method, &quot;params&quot;: params}),
                   qos=1)


def on_message(client, userdata, msg):
    payload = json.loads(msg.payload.decode())
    params = payload.get(&quot;params&quot;, {})
    if &quot;downloadProgress&quot; in params:
        print(&quot;transfer:&quot;, params[&quot;downloadProgress&quot;], &quot;%&quot;)
    if payload.get(&quot;opt&quot;) == &quot;otaInform&quot; and payload.get(&quot;res&quot;) == &quot;fail&quot;:
        print(&quot;update failed:&quot;, payload.get(&quot;msg&quot;))
    if &quot;firmwareVersion&quot; in params:
        print(&quot;reported version after restart:&quot;, params[&quot;firmwareVersion&quot;])


client = mqtt.Client()
client.username_pw_set(&quot;backend&quot;, &quot;...&quot;)
client.on_message = on_message
client.connect(BROKER, PORT, keepalive=60)
client.subscribe(POST_TOPIC, qos=1)
client.loop_start()

command(client, &quot;ota_set&quot;, {&quot;module&quot;: TARGET})          # select the target first
command(client, &quot;ota_set&quot;, {&quot;otaInform&quot;: {&quot;firmwareUrl&quot;: IMAGE_URL, &quot;port&quot;: IMAGE_PORT}})
client.loop_forever()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That is the whole mechanism — two publishes and a subscription. The rest is procedure.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Declare a maintenance window.&lt;/strong&gt; The unit is unreachable while it rewrites and restarts. Monitoring will read that as a failure unless you suppress it — see &lt;a rel=&quot;nofollow&quot; href=&quot;https://249263431.github.io/60GHz-Radar-SDK/guide/2026/10/27/online-heartbeat-radar-health.html&quot;&gt;Online Says It's Connected, HeartBeat Says It Works&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Select the target deliberately.&lt;/strong&gt; &lt;code&gt;module&lt;/code&gt; defaults to the Wi-Fi module. Sending a radar image to it is a mistake the command will not catch for you.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Make sure the URL is reachable from the device.&lt;/strong&gt; Same subnet or routable, firewall permits that port, and never &lt;code&gt;localhost&lt;/code&gt; or &lt;code&gt;127.0.0.1&lt;/code&gt; — the address is resolved by the radar, not by the machine issuing the command.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stage it.&lt;/strong&gt; One unit first. Watch &lt;code&gt;downloadProgress&lt;/code&gt; reach &lt;code&gt;100&lt;/code&gt;, wait for the restart, confirm the version changed, then proceed with the rest.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keep the previous image served.&lt;/strong&gt; Given what is not documented below, this is your only fallback.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Do not batch-update overnight in a care deployment.&lt;/strong&gt; A unit that comes back with different behaviour at 03:00 is discovered by nobody until morning.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;a-minimal-image-host&quot;&gt;A minimal image host&lt;/h3&gt;
&lt;p&gt;Nothing more than a static file server on the management network:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python -m http.server 8090 --directory ./firmware
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;which makes &lt;code&gt;http://{host}:8090/app.rbl&lt;/code&gt; available to the units. Use it on a segregated network or behind whatever reverse proxy you already trust; it is plain HTTP with directory listing enabled, and it is a build artifact service, not a public endpoint.&lt;/p&gt;
&lt;h3 id=&quot;what-the-documents-do-not-say&quot;&gt;What the documents do not say&lt;/h3&gt;
&lt;p&gt;Stated plainly, because guessing here costs hardware:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Transport.&lt;/strong&gt; Every example uses &lt;code&gt;http://&lt;/code&gt;. HTTPS support is not stated. Plan for plain HTTP on a network you control.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rollback.&lt;/strong&gt; No rollback command appears. Assume you cannot command a unit back to the previous revision — which is why keeping the old image available and piloting one unit first are not optional.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Duration and interruption.&lt;/strong&gt; No update duration is given, and the documents describe failure only as the &lt;code&gt;ota failed!&lt;/code&gt; response. Behaviour after power loss mid-update is undocumented.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The consequence is procedural rather than technical: update when someone is present to observe, one unit first, with the previous image still served.&lt;/p&gt;
&lt;h3 id=&quot;checklist&quot;&gt;Checklist&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;[ ] Unit confirmed to be one of the two that document this command&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;module&lt;/code&gt; selected explicitly before every update — Wi-Fi module vs radar module&lt;/li&gt;
&lt;li&gt;[ ] Image URL and port reachable from the device's network, not from yours only&lt;/li&gt;
&lt;li&gt;[ ] Subscribed to the report topic &lt;em&gt;before&lt;/em&gt; publishing either command&lt;/li&gt;
&lt;li&gt;[ ] Monitoring suppression window opened, and closed only after the version report arrives&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;downloadProgress&lt;/code&gt; observed to &lt;code&gt;100&lt;/code&gt;, restart observed, version compared against the pre-update record&lt;/li&gt;
&lt;li&gt;[ ] Previous firmware image still available at a known URL&lt;/li&gt;
&lt;li&gt;[ ] Staged: one unit, verified, then the cohort&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;related-reading&quot;&gt;Related reading&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Reading the stream these commands travel on: &lt;a rel=&quot;nofollow&quot; href=&quot;https://249263431.github.io/60GHz-Radar-SDK/guide/2026/10/25/reading-radar-data-your-own-app.html&quot;&gt;Reading Radar Data in Your Own Application&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Managing many units: &lt;a rel=&quot;nofollow&quot; href=&quot;https://249263431.github.io/60GHz-Radar-SDK/guide/2026/10/24/running-several-radars-one-home.html&quot;&gt;Running Several Radars in One Home&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;The broker underneath it: &lt;a rel=&quot;nofollow&quot; href=&quot;https://249263431.github.io/60GHz-Radar-SDK/guide/2026/10/23/apache-artemis-mqtt-broker-radar.html&quot;&gt;Apache Artemis setup&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Full documentation: &lt;a rel=&quot;nofollow&quot; href=&quot;https://249263431.github.io/60GHz-Radar-SDK/&quot;&gt;https://249263431.github.io/60GHz-Radar-SDK/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Modules and finished units: &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.tindie.com/stores/fuxinsuo/&quot;&gt;https://www.tindie.com/stores/fuxinsuo/&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32750/updating-radar-firmware-without-a-cloud-self-hosted-ota-over-mqtt</guid>
<pubDate>Tue, 27 Oct 2026 23:05:42 +0000</pubDate>
</item>
<item>
<title>Online Says It's Connected, HeartBeat Says It Works: Reading Two Status Signals Instead of Guessing</title>
<link>https://coderlegion.com/32601/online-says-its-connected-heartbeat-says-works-reading-status-signals-instead-guessing</link>
<description>&lt;hr&gt;
&lt;p&gt;title: &quot;Online Says It's Connected, HeartBeat Says It Works: Reading Two Status Signals Instead of Guessing From Silence&quot;&lt;br&gt;
published: true&lt;br&gt;
tags: iot, mqtt, radar, monitoring&lt;br&gt;
canonical_url: &lt;a rel=&quot;nofollow&quot; href=&quot;https://249263431.github.io/60GHz-Radar-SDK/guide/2026/10/27/online-heartbeat-radar-health.html&quot;&gt;https://249263431.github.io/60GHz-Radar-SDK/guide/2026/10/27/online-heartbeat-radar-health.html&lt;/a&gt;&lt;br&gt;
description: &quot;Two fields in a mmWave radar's MQTT output — online and heartBeat — look like the same health check and are not. One proves the link; the other proves the sensor. And neither supports a timeout.&quot;&lt;/p&gt;
&lt;h3 id=&quot;cover-image-&quot;&gt;cover_image:&lt;/h3&gt;
&lt;p&gt;Two fields in a radar's MQTT output get mistaken for each other constantly, and mistaking them is how a health monitor ends up trusted when it should not be.&lt;/p&gt;
&lt;p&gt;They answer different questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;online&lt;/code&gt; answers &quot;is the link up?&quot;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;heartBeat&lt;/code&gt; answers &quot;is the radar itself working?&quot;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;&lt;code&gt;online&lt;/code&gt; &lt;/th&gt;&lt;th&gt;&lt;code&gt;heartBeat&lt;/code&gt;&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Question answered &lt;/td&gt;&lt;td&gt;Is this device reachable? &lt;/td&gt;&lt;td&gt;Is this device operating correctly?&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Reported values &lt;/td&gt;&lt;td&gt;&lt;code&gt;&quot;0&quot;&lt;/code&gt; offline, &lt;code&gt;&quot;1&quot;&lt;/code&gt; online &lt;/td&gt;&lt;td&gt;&lt;code&gt;&quot;0&quot;&lt;/code&gt; abnormal, &lt;code&gt;&quot;1&quot;&lt;/code&gt; normal&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Layer &lt;/td&gt;&lt;td&gt;MQTT connection and broker session &lt;/td&gt;&lt;td&gt;The radar's own operating state&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Reported when &lt;/td&gt;&lt;td&gt;On state change only &lt;/td&gt;&lt;td&gt;On state change only&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Available on &lt;/td&gt;&lt;td&gt;Sleep radar, fall radar, presence radar &lt;/td&gt;&lt;td&gt;Sleep radar, fall radar&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The presence radar publishes its online state on a dedicated topic — &lt;code&gt;/RadarHP/&amp;lt;DeviceID&amp;gt;/sys/online/post&lt;/code&gt;; the sleep and fall radars publish &lt;code&gt;online&lt;/code&gt; as an attribute in the same property payload as everything else. Note the asymmetry: &lt;strong&gt;there is no &lt;code&gt;heartBeat&lt;/code&gt; in the presence radar's protocol documentation.&lt;/strong&gt; The other two report it like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{&quot;version&quot;: &quot;1.0&quot;, &quot;method&quot;: &quot;post&quot;, &quot;params&quot;: {&quot;online&quot;: &quot;0&quot;}}
{&quot;version&quot;: &quot;1.0&quot;, &quot;method&quot;: &quot;post&quot;, &quot;params&quot;: {&quot;heartBeat&quot;: &quot;0&quot;}}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;why-this-distinction-is-not-academic&quot;&gt;Why this distinction is not academic&lt;/h3&gt;
&lt;p&gt;Consider what has to be true for any &lt;code&gt;heartBeat&lt;/code&gt; message to reach your code: the radar has power, it is associated with the Wi-Fi access point, its MQTT session is established, and you are subscribed to its topic. &lt;strong&gt;A delivered &lt;code&gt;heartBeat&lt;/code&gt; therefore already proves the link is working.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;From that, one conclusion follows immediately:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;heartBeat&lt;/code&gt; cannot report a connectivity failure. Physically and logically, it cannot — a radar that has lost its link has no way to deliver that news.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The reverse holds for &lt;code&gt;online&lt;/code&gt;. An &lt;code&gt;online: &quot;0&quot;&lt;/code&gt; message is not written by the radar that went offline; it is generated &lt;strong&gt;by the broker&lt;/strong&gt; when the session it was holding stops answering. The radar contributes nothing to that judgement.&lt;/p&gt;
&lt;p&gt;So neither field overlaps with the other, and neither can substitute for the other:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you only watch &lt;code&gt;online&lt;/code&gt;, you will see &quot;connected&quot; while the radar sits there reporting a fault about itself.&lt;/li&gt;
&lt;li&gt;If you only watch &lt;code&gt;heartBeat&lt;/code&gt;, every network outage looks like nothing at all.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-trap-silence-is-not-evidence&quot;&gt;The trap: silence is not evidence&lt;/h3&gt;
&lt;p&gt;Both fields are documented as &lt;strong&gt;reported on state change&lt;/strong&gt;, not on a timer. That single sentence is where most monitoring code goes wrong.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Occupancy.&lt;/strong&gt; The presence state is event-driven. A room whose state does not change for three hours produces no occupancy messages, and that is correct behaviour — not a fault.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Heartbeat.&lt;/strong&gt; A radar that boots normally and stays normal has no reason to send &lt;code&gt;heartBeat&lt;/code&gt; at all, because no state change occurred.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The one periodic attribute.&lt;/strong&gt; In the sleep and fall protocols, the only attribute the documentation marks as periodically reported is &lt;code&gt;workDuration&lt;/code&gt; (4 bytes, value range &lt;code&gt;0x3C&lt;/code&gt;–&lt;code&gt;0xffffffff&lt;/code&gt;, unit seconds) — and it is marked &quot;not yet available&quot; in the same line. &lt;code&gt;workDurationReportFrequency&lt;/code&gt; carries the same note. &lt;strong&gt;Do not build a keepalive on either of them.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So the tempting pattern — &quot;if I have not heard from the device in 60 seconds, it must be gone&quot; — has no foundation here. Absence of messages is the normal state of a working installation.&lt;/p&gt;
&lt;h3 id=&quot;what-you-can-do-instead&quot;&gt;What you can do instead&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;&lt;code&gt;online&lt;/code&gt; &lt;/th&gt;&lt;th&gt;&lt;code&gt;heartBeat&lt;/code&gt; &lt;/th&gt;&lt;th&gt;Conclusion &lt;/th&gt;&lt;th&gt;Action&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;1 &lt;/td&gt;&lt;td&gt;1 &lt;/td&gt;&lt;td&gt;Link up, radar reports normal &lt;/td&gt;&lt;td&gt;—&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;1 &lt;/td&gt;&lt;td&gt;0 &lt;/td&gt;&lt;td&gt;&lt;strong&gt;Link up, radar reports a fault&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;Raise the higher-severity alarm; this is the case only &lt;code&gt;heartBeat&lt;/code&gt; can surface&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0 &lt;/td&gt;&lt;td&gt;— &lt;/td&gt;&lt;td&gt;Session dropped, as judged by the broker &lt;/td&gt;&lt;td&gt;Investigate the link; the radar's own state is unknown&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;no messages &lt;/td&gt;&lt;td&gt;no messages &lt;/td&gt;&lt;td&gt;Most likely nothing changed &lt;/td&gt;&lt;td&gt;&lt;strong&gt;Nothing can be concluded&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Treat the two as different classes of event with different severities. A lost link is an infrastructure ticket; a radar reporting itself abnormal is a hardware ticket, and it is the one that should wake someone up.&lt;/p&gt;
&lt;h3 id=&quot;two-rules-you-must-not-break&quot;&gt;Two rules you must not break&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1. Do not disable &lt;code&gt;online&lt;/code&gt; or &lt;code&gt;heartBeat&lt;/code&gt; with &lt;code&gt;limit_set&lt;/code&gt;.&lt;/strong&gt; The Wi-Fi protocols expose a reporting-suppression command: &lt;code&gt;limit_set&lt;/code&gt; / &lt;code&gt;limit_get&lt;/code&gt;, where &lt;code&gt;1&lt;/code&gt; means &quot;suppress this attribute&quot; and &lt;code&gt;0&lt;/code&gt; means &quot;allow it&quot;. It exists for the attributes that report frequently — &lt;code&gt;someoneExists&lt;/code&gt;, &lt;code&gt;motionStatus&lt;/code&gt;, &lt;code&gt;movementSigns&lt;/code&gt;, &lt;code&gt;humanDistance&lt;/code&gt;, &lt;code&gt;humanPosition&lt;/code&gt; — and it is a legitimate way to cut traffic and noise. Both &lt;code&gt;online&lt;/code&gt; and &lt;code&gt;heartBeat&lt;/code&gt; appear in that list. Suppress them and you remove the only two signals that stand between you and a timeout guess.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Do not try to poll &lt;code&gt;heartBeat&lt;/code&gt;.&lt;/strong&gt; Every neighbouring attribute in those documents — &lt;code&gt;initFinish&lt;/code&gt;, &lt;code&gt;workDuration&lt;/code&gt;, &lt;code&gt;productMode&lt;/code&gt; — carries the line &quot;this attribute supports querying&quot; followed by a &lt;code&gt;get&lt;/code&gt; example. &lt;code&gt;heartBeat&lt;/code&gt; does not. The only &lt;code&gt;?&lt;/code&gt; it ever appears with is inside &lt;code&gt;limit_get&lt;/code&gt;, and that question means &quot;is reporting suppressed for this attribute&quot;, not &quot;what is its current value&quot;. There is no documented way to ask the radar for that value on demand.&lt;/p&gt;
&lt;p&gt;That leaves one workable approach for the things you genuinely need on demand: &lt;strong&gt;query the attributes that do support it.&lt;/strong&gt; If you must confirm a radar has finished starting up, &lt;code&gt;initFinish&lt;/code&gt; (&lt;code&gt;0&lt;/code&gt; not complete, &lt;code&gt;1&lt;/code&gt; complete) is queryable. &lt;code&gt;heartBeat&lt;/code&gt; is not, and no amount of polling will change that.&lt;/p&gt;
&lt;h3 id=&quot;a-minimal-health-monitor&quot;&gt;A minimal health monitor&lt;/h3&gt;
&lt;p&gt;Subscribe, merge the sparse payload, and raise the two classes separately:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import json, time
import paho.mqtt.client as mqtt

TOPIC = &quot;/Radar60SP/&amp;lt;MAC&amp;gt;/#&quot;

state = {}
last_any = {}


def on_message(client, userdata, msg):
    try:
        payload = json.loads(msg.payload.decode())
    except ValueError:
        return
    params = payload.get(&quot;params&quot;, {})
    if not params:
        return

    # Only the fields that changed are present.
    for k in (&quot;online&quot;, &quot;heartBeat&quot;, &quot;initFinish&quot;):
        if k in params:
            state[k] = params[k]

    last_any[&quot;ts&quot;] = time.time()

    # Two different failures, two different severities.
    if params.get(&quot;online&quot;) == &quot;0&quot;:
        raise_alarm(&quot;link&quot;, device=msg.topic, severity=&quot;infra&quot;)
    if params.get(&quot;heartBeat&quot;) == &quot;0&quot;:
        raise_alarm(&quot;sensor&quot;, device=msg.topic, severity=&quot;high&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note the shape: nothing here fires on elapsed time. Every action is triggered by an arriving value.&lt;/p&gt;
&lt;p&gt;If you still want a timeout as a last resort, the threshold must come from measurement, not habit. Log the longest interval your real installation goes without any message during normal operation, then add margin to that. A number you made up will either fire constantly or never fire.&lt;/p&gt;
&lt;h3 id=&quot;measure-these-at-commissioning&quot;&gt;Measure these at commissioning&lt;/h3&gt;
&lt;p&gt;Two things the documentation does not state, and that you should record per installation rather than assume:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Does the radar announce &lt;code&gt;heartBeat: &quot;1&quot;&lt;/code&gt; on power-up?&lt;/strong&gt; A device that boots healthy may never have a reason to report it. Power one up on the bench with a subscriber attached and write down what actually arrives, and how long after power-up it arrives.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Does &lt;code&gt;online: &quot;1&quot;&lt;/code&gt; get republished after a reconnect?&lt;/strong&gt; If it does not, your consumer must treat the absence of a change as &quot;still online&quot; rather than as unknown.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both belong in the same commissioning note as the empty-room baseline from &lt;a rel=&quot;nofollow&quot; href=&quot;https://249263431.github.io/60GHz-Radar-SDK/guide/2026/10/24/distance-position-debug-installation.html&quot;&gt;Using Distance and Position Output to Debug a Radar Installation&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;maintenance-looks-like-a-failure&quot;&gt;Maintenance looks like a failure&lt;/h3&gt;
&lt;p&gt;One consequence of all this that catches people out: the sleep and fall protocols support firmware update over the air — &lt;code&gt;ota_set&lt;/code&gt; with &lt;code&gt;otaModule&lt;/code&gt; (&lt;code&gt;0&lt;/code&gt; Wi-Fi module, &lt;code&gt;1&lt;/code&gt; radar module, and the command must be sent before every update), &lt;code&gt;otaInform&lt;/code&gt; giving a firmware URL and port, and &lt;code&gt;downloadProgress&lt;/code&gt; reporting &lt;code&gt;0&lt;/code&gt;–&lt;code&gt;100&lt;/code&gt;. On success the module restarts and then reports its new version number.&lt;/p&gt;
&lt;p&gt;During that window the device is legitimately unreachable. If your monitoring treats a missing device as an alarm, a firmware update will page someone. Put planned maintenance in a suppression window, and use the version report afterwards — the presence radar's &lt;code&gt;/RadarSP/&amp;lt;DeviceID&amp;gt;/sys/version/post&lt;/code&gt; topic, or the &lt;code&gt;firmwareVersion&lt;/code&gt; attribute — to confirm the update actually landed.&lt;/p&gt;
&lt;p&gt;The update server is one you host yourself; the documentation states plainly that you build the web server the firmware is fetched from.&lt;/p&gt;
&lt;h3 id=&quot;two-controls-worth-knowing-about&quot;&gt;Two controls worth knowing about&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reporting suppression&lt;/strong&gt; (&lt;code&gt;limit_set&lt;/code&gt; / &lt;code&gt;limit_get&lt;/code&gt;): set &lt;code&gt;1&lt;/code&gt; to suppress an attribute, &lt;code&gt;0&lt;/code&gt; to allow it. Use it on the high-frequency attributes to cut traffic and clutter. Leave &lt;code&gt;online&lt;/code&gt; and &lt;code&gt;heartBeat&lt;/code&gt; alone.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Per-attribute QoS&lt;/strong&gt; (&lt;code&gt;control_qos&lt;/code&gt; in the presence protocol, &lt;code&gt;qos_set&lt;/code&gt; / &lt;code&gt;qos_get&lt;/code&gt; with &lt;code&gt;Qos_post&lt;/code&gt; in the sleep and fall protocols): the default is QoS 1, where &lt;code&gt;0&lt;/code&gt; is QoS 0, &lt;code&gt;1&lt;/code&gt; is QoS 1 and &lt;code&gt;2&lt;/code&gt; is QoS 2. The presence protocol additionally exposes per-attribute keys — &lt;code&gt;Qos_online&lt;/code&gt;, &lt;code&gt;Qos_movementSigns&lt;/code&gt;, &lt;code&gt;Qos_breathRate&lt;/code&gt;, and so on. This is worth knowing because it contradicts the usual assumption that QoS is decided only by the subscriber.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;checklist-1&quot;&gt;Checklist&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;[ ] &lt;code&gt;online&lt;/code&gt; and &lt;code&gt;heartBeat&lt;/code&gt; handled as two separate signals with two separate severities&lt;/li&gt;
&lt;li&gt;[ ] No logic concludes anything from the absence of messages&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;limit_set&lt;/code&gt; used, if at all, only on high-frequency attributes — never on &lt;code&gt;online&lt;/code&gt; or &lt;code&gt;heartBeat&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;[ ] No polling of &lt;code&gt;heartBeat&lt;/code&gt;; anything needed on demand read from attributes that document query support&lt;/li&gt;
&lt;li&gt;[ ] Power-up behaviour of &lt;code&gt;heartBeat&lt;/code&gt; measured and recorded&lt;/li&gt;
&lt;li&gt;[ ] Reconnect behaviour of &lt;code&gt;online&lt;/code&gt; measured and recorded&lt;/li&gt;
&lt;li&gt;[ ] Any timeout threshold derived from measured worst-case silence, not assumed&lt;/li&gt;
&lt;li&gt;[ ] Firmware updates placed in a suppression window, and verified afterwards by version report&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;where-to-go-next&quot;&gt;Where to go next&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Getting the stream in the first place: &lt;a rel=&quot;nofollow&quot; href=&quot;https://249263431.github.io/60GHz-Radar-SDK/guide/2026/10/25/reading-radar-data-your-own-app.html&quot;&gt;Reading Radar Data in Your Own Application&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;The broker underneath it: &lt;a rel=&quot;nofollow&quot; href=&quot;https://249263431.github.io/60GHz-Radar-SDK/guide/2026/10/23/apache-artemis-mqtt-broker-radar.html&quot;&gt;Apache Artemis setup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Keeping several devices straight: &lt;a rel=&quot;nofollow&quot; href=&quot;https://249263431.github.io/60GHz-Radar-SDK/guide/2026/10/24/running-several-radars-one-home.html&quot;&gt;Running Several Radars in One Home&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Keeping the history: &lt;a rel=&quot;nofollow&quot; href=&quot;https://249263431.github.io/60GHz-Radar-SDK/guide/2026/10/26/storing-radar-data-retention-trends.html&quot;&gt;Storing Radar Data&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;This is not a medical device. A status field tells you what a radar reports about itself; it does not guarantee what the radar detects. For anyone at real risk, monitoring is an addition to a care routine, never a substitute for one.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32601/online-says-its-connected-heartbeat-says-works-reading-status-signals-instead-guessing</guid>
<pubDate>Tue, 27 Oct 2026 01:03:42 +0000</pubDate>
</item>
<item>
<title>MQTT: The Protocol Designed for a World Where Things Fail</title>
<link>https://coderlegion.com/23534/mqtt-the-protocol-designed-for-a-world-where-things-fail</link>
<description>&lt;p&gt;Imagine thousands of sensors distributed across a factory, a fleet of delivery drones, or millions of smart meters in different locations. How can these devices communicate reliably when networks are slow, unreliable, and expensive? MQTT (Message Queuing Telemetry Transport) was created to solve exactly this problem.&lt;/p&gt;
&lt;h3 id=&quot;the-origin-story-of-mqtt&quot;&gt;The origin story of MQTT&lt;/h3&gt;
&lt;p&gt;The story of MQTT begins in the late 1990s, before the term IoT (Internet of Things) became popular. At that time, engineers were already trying to connect remote machines, sensors, and industrial equipment, but the available communication methods were not well suited for these environments.&lt;/p&gt;
&lt;p&gt;Most people describe MQTT as a messaging protocol, but it was created because the physical world is unreliable. Instead of describing it feature by feature, I’ll uncover it through the questions it was designed to answer. Before that, it helps to understand the three roles involved in every MQTT system.&lt;/p&gt;
&lt;h3 id=&quot;the-publisher&quot;&gt;The publisher&lt;/h3&gt;
&lt;p&gt;A publisher is a device or application that sends information. It publishes messages to a topic without knowing who, if anyone, will receive them.&lt;/p&gt;
&lt;p&gt;For example: a temperature sensor may publish the current temperature every minute, or an EV (Electric Vehicle) charger may publish its charging status.&lt;/p&gt;
&lt;h3 id=&quot;the-subscriber&quot;&gt;The subscriber&lt;/h3&gt;
&lt;p&gt;A subscriber is a device or application that receives information. It subscribes to one or more topics and automatically receives every message published on those topics.&lt;/p&gt;
&lt;p&gt;For example: a monitoring dashboard can subscribe to temperature readings from all sensors in a factory, while an alarm subscribes to the same data to detect overheating.&lt;/p&gt;
&lt;h3 id=&quot;the-broker&quot;&gt;The broker&lt;/h3&gt;
&lt;p&gt;The broker is the central communication hub. Every message sent by a publisher first arrives at the broker, which then forwards it to all subscribers interested in that topic.&lt;/p&gt;
&lt;p&gt;The broker also handles many of MQTT’s reliability features, such as keeping track of connected clients, storing messages when needed, and detecting when a client disconnects unexpectedly.&lt;/p&gt;
&lt;h3 id=&quot;what-do-clients-and-brokers-look-like-in-practice-&quot;&gt;What do clients and brokers look like in practice?&lt;/h3&gt;
&lt;p&gt;None of this is as abstract as it sounds. A “client” is just anything running MQTT client software: an ESP32 microcontroller with a few kilobytes of firmware, a Raspberry Pi running Home Assistant, a smartphone app checking today’s solar production, or a few lines embedded in an industrial PLC. A commercial smart plug running Tasmota is a client. Home Assistant itself is a client too; it just happens to be one that subscribes to hundreds of topics and publishes commands back to many of them at once.&lt;/p&gt;
&lt;p&gt;A “broker” is just as unglamorous: server software, usually a single process. &lt;strong&gt;Eclipse Mosquitto&lt;/strong&gt; is the one most home labs reach for — lightweight enough to run on a Raspberry Pi, in a Docker container, or as a Home Assistant add-on, and largely forget about; it comfortably handles thousands of messages a second on hardware that costs less than the charger it’s monitoring. At the other end of the scale, &lt;strong&gt;EMQX&lt;/strong&gt; and &lt;strong&gt;HiveMQ&lt;/strong&gt; are broker platforms built to run clustered across many servers, handling millions of simultaneously connected clients — the kind of infrastructure a nationwide smart meter rollout would need. Cloud providers also sell brokers as a managed service — &lt;strong&gt;AWS IoT Core, Azure IoT Hub&lt;/strong&gt; — so a manufacturer never has to run one at all.&lt;/p&gt;
&lt;p&gt;None of that changes the protocol underneath. A $3 microcontroller and a clustered enterprise broker are speaking exactly the same MQTT to each other. The roles are identical; only the scale differs.&lt;/p&gt;
&lt;h3 id=&quot;one-client-multiple-roles&quot;&gt;One client, multiple roles&lt;/h3&gt;
&lt;p&gt;In MQTT, a client is not limited to a single role. The same client can both publish and subscribe.&lt;/p&gt;
&lt;p&gt;For example, a smart thermostat publishes the current room temperature while also subscribing to commands that change the desired temperature.&lt;/p&gt;
&lt;p&gt;In practice, MQTT has only two types of participants:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Clients, which can publish, subscribe, or do both&lt;/li&gt;
&lt;li&gt;The broker, which connects clients and manages the communications&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This simple architecture is one of the reasons MQTT scales so well. Publishers never need to know who receives their messages, and subscribers never need to know who produced them. The broker takes care of connecting the two.&lt;/p&gt;
&lt;p&gt;Looking a bit closer, MQTT implements the Pub/Sub architecture pattern. It is not something unique to MQTT; MQTT is simply one implementation of that pattern. The key idea behind this is &lt;strong&gt;decoupling&lt;/strong&gt;. Publishers and subscribers do not know about each other. They only agree on a common “language” (topics or events), while a broker or message bus delivers the messages.&lt;/p&gt;
&lt;p&gt;MQTT’s Publish/Subscribe model is closely related to several other software patterns, like the Observer Pattern, common in object-oriented programming. An object (the subject) notifies all registered observers when its state changes. You’ll find it most often in GUI frameworks. You can find the same behaviour in Event-Driven Architecture — an event-driven communication pattern applied to the physical world. Instead of software services producing business events, devices produce events about the state of the real world. The architecture is the same: something happens, an event is published, and interested systems react.&lt;/p&gt;
&lt;p&gt;Networks fail. Power disappears. Cellular coverage comes and goes. Sensors reboot. Messages get lost. Yet industrial automation, smart cities, connected vehicles and energy systems still need to operate.&lt;/p&gt;
&lt;h3 id=&quot;how-do-you-know-a-device-is-still-alive-&quot;&gt;How do you know a device is still alive?&lt;/h3&gt;
&lt;p&gt;Imagine a temperature sensor installed on a remote pipeline. It may not send data every minute because the temperature hasn’t changed. If nothing arrives for an hour, is the temperature stable? Or has the device died?&lt;/p&gt;
&lt;p&gt;MQTT answers this with the &lt;strong&gt;Keep Alive&lt;/strong&gt; mechanism.&lt;/p&gt;
&lt;p&gt;Rather than continuously transmitting data, the client periodically proves it is still connected. If the broker stops hearing from it within the agreed interval, the connection is considered lost.&lt;/p&gt;
&lt;h3 id=&quot;what-happens-when-a-device-suddenly-disappears-&quot;&gt;What happens when a device suddenly disappears?&lt;/h3&gt;
&lt;p&gt;A factory robot loses power, a drone flies out of cellular coverage, an EV charger reboots. Nobody sends a goodbye message. How does the rest of the system know?&lt;/p&gt;
&lt;p&gt;MQTT introduces the &lt;strong&gt;Last Will and Testament&lt;/strong&gt; (LWT).&lt;/p&gt;
&lt;p&gt;Before communication starts, the client leaves instructions with the broker:&lt;/p&gt;
&lt;p&gt;“If I disappear unexpectedly, publish this message on my behalf.”&lt;/p&gt;
&lt;p&gt;Other clients immediately know that the device went offline.&lt;/p&gt;
&lt;h3 id=&quot;what-happens-to-messages-while-the-device-is-offline-&quot;&gt;What happens to messages while the device is offline?&lt;/h3&gt;
&lt;p&gt;Not every message has the same value. A temperature reading from five minutes ago may be useless. A payment confirmation must never disappear. MQTT allows applications to choose. Should messages be discarded or should the broker keep them until the client reconnects?&lt;/p&gt;
&lt;p&gt;This is where persistent sessions become important. Instead of treating disconnections as failures, MQTT treats them as temporary interruptions.&lt;/p&gt;
&lt;h3 id=&quot;how-do-you-know-a-message-actually-arrived-&quot;&gt;How do you know a message actually arrived?&lt;/h3&gt;
&lt;p&gt;A sensor publishes a reading. Did the broker receive it? Did the subscriber? On a shaky mobile network, packets get dropped mid-transmission all the time. MQTT doesn’t leave this to chance — it lets you choose, per message, how hard the protocol should work to make sure it lands.&lt;/p&gt;
&lt;p&gt;At &lt;strong&gt;QoS 0,&lt;/strong&gt; a message is sent once and forgotten — fire and forget. Fine for a reading that will be superseded a second later anyway.&lt;/p&gt;
&lt;p&gt;At &lt;strong&gt;QoS 1&lt;/strong&gt;, the message is redelivered until the receiver acknowledges it — guaranteed to arrive, but possibly more than once.&lt;/p&gt;
&lt;p&gt;At &lt;strong&gt;QoS 2&lt;/strong&gt;, MQTT adds an extra handshake that guarantees the message arrives exactly once — no duplicates, no drops — at the cost of more round trips.&lt;/p&gt;
&lt;p&gt;Nobody has to pick one level for the whole system. A pipeline pressure reading might travel at QoS 0. A valve-close command might travel at QoS 2. The protocol lets the importance of the message decide how much effort goes into delivering it.&lt;/p&gt;
&lt;h3 id=&quot;how-does-a-system-recover-after-failure-&quot;&gt;How does a system recover after failure?&lt;/h3&gt;
&lt;p&gt;A client reconnects after being offline for ten minutes. What happened to it in that time?&lt;/p&gt;
&lt;p&gt;If it connected with a persistent session, the broker was holding onto anything sent to it at QoS 1 or QoS 2 while it was gone — those messages arrive the moment it comes back, in order. The client doesn’t need any special recovery logic; from its point of view, the outage just collapses into a slightly longer wait.&lt;/p&gt;
&lt;p&gt;If it connected with a clean session instead, none of that history follows it. It starts fresh, subscribes again, and only sees what’s published from that point forward.&lt;/p&gt;
&lt;p&gt;Neither choice is “correct” on its own — a factory alarm system wants the messages it missed; a live GPS tracker mostly doesn’t care what happened while it was disconnected, only where the vehicle is now. MQTT lets each client decide which kind of recovery it actually needs.&lt;/p&gt;
&lt;h3 id=&quot;why-wasn-t-http-enough-&quot;&gt;Why wasn’t HTTP enough?&lt;/h3&gt;
&lt;p&gt;HTTP is a request-response protocol: a client asks, a server answers. That model assumes the client knows who to ask, and that the server is reachable and listening right at that moment.&lt;/p&gt;
&lt;p&gt;Neither assumption holds up well for a sensor network. A pipeline sensor doesn’t know which of several dashboards, alarms, or logging systems might care about its reading — and it shouldn’t have to know. If it had to open a connection and send a request to each one individually, adding a new subscriber would mean reconfiguring every publisher in the system.&lt;/p&gt;
&lt;p&gt;HTTP also expects a server to be reachable on demand. Devices on a cellular or satellite link aren’t always reachable, and polling constantly to ask “did anything change?” burns bandwidth and battery for the vast majority of checks that come back with nothing new.&lt;/p&gt;
&lt;p&gt;MQTT flips the relationship. A device doesn’t ask anyone anything — it announces what it knows, and whoever’s listening gets notified. Adding a new subscriber means one more client connecting to the broker, not any change on the publisher’s side at all.&lt;/p&gt;
&lt;h3 id=&quot;why-does-mqtt-still-matter-decades-later-&quot;&gt;Why does MQTT still matter, decades later?&lt;/h3&gt;
&lt;p&gt;MQTT was designed in 1999 for oil pipelines and satellite links — about as far from a smart home as you can get. And yet it’s the same protocol quietly running inside Home Assistant, inside industrial SCADA systems, inside the EV charger in someone’s garage.&lt;/p&gt;
&lt;p&gt;That’s not nostalgia. The problem MQTT solves was never really about the technology of any particular decade — networks were unreliable in 1999 and they’re still unreliable now, just in new shapes: a Wi-Fi dropout instead of a satellite delay, a cheap microcontroller instead of a pipeline modem. The underlying pattern — decouple publishers from subscribers, let a broker absorb the unreliability, treat presence and disconnection as first-class events instead of edge cases — turned out to generalize to almost anything that produces data intermittently and needs that data to reach more than one place.&lt;/p&gt;
&lt;p&gt;That’s why a protocol old enough to predate the term “IoT” is still the obvious choice for a new EV charger deciding when to draw power from the sun instead of the grid.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/23534/mqtt-the-protocol-designed-for-a-world-where-things-fail</guid>
<pubDate>Sun, 26 Jul 2026 17:39:08 +0000</pubDate>
</item>
<item>
<title>System Architecture: End-to-End Encryption in a Flutter-to-Microcontroller MQTT Bridge</title>
<link>https://coderlegion.com/19892/system-architecture-end-to-end-encryption-in-a-flutter-to-microcontroller-mqtt-bridge</link>
<description>&lt;p&gt;As software developers, we must treat physical IoT hardware as a secured endpoint. Relying on unencrypted, local Wi-Fi connections to trigger microcontrollers leaves physical devices vulnerable to packet sniffing and local network exploits.&lt;/p&gt;
&lt;p&gt;In this architectural overview, we outline a secure, end-to-end communication pipeline:&lt;br&gt;
Local Biometric Keystore Verification: Flutter client authenticates the user locally and generates a cryptographically signed payload token via the OS secure enclave.&lt;/p&gt;
&lt;p&gt;Encrypted Decoupling: The payload is published over encrypted MQTT (MQTTS with TLS 1.3 over Port 8883) to a secure broker.&lt;/p&gt;
&lt;p&gt;Hardware Execution: The local microcontroller (ESP32/PCBA) subscribes to the secured topic, verifies the cryptographic signature of the token, and executes the physical command.&lt;/p&gt;
&lt;p&gt;We also dive into integrating edge-sensing telemetry arrays (such as our local ML-driven E-Noses Precision Agriculture AI project) under the same unified gateway architecture.&lt;/p&gt;
&lt;p&gt;Check out the full system topology and code pipeline design:&lt;br&gt;
 &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.seosiri.com/2026/06/biometric-iot-security-hardware-manufacturer.html&quot;&gt;https://www.seosiri.com/2026/06/biometric-iot-security-hardware-manufacturer.html&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/19892/system-architecture-end-to-end-encryption-in-a-flutter-to-microcontroller-mqtt-bridge</guid>
<pubDate>Sun, 07 Jun 2026 09:04:37 +0000</pubDate>
</item>
<item>
<title>How to Set Up MQTT With ESP8266?</title>
<link>https://coderlegion.com/14080/how-to-set-up-mqtt-with-esp8266</link>
<description>
&lt;p&gt;Hello, I'm Ganesh. I'm building &lt;em&gt;git-lrc&lt;/em&gt;, an AI code reviewer that runs on every commit. It is free, unlimited, and source-available on Github. &lt;a href=&quot;https://github.com/HexmosTech/git-lrc&quot; rel=&quot;noopener noreferrer nofollow&quot;&gt;Star Us&lt;/a&gt; to help devs discover the project. Do give it a try and share your feedback for improving the product.&lt;/p&gt;

&lt;p&gt;In this article, I will be demonstrating how to use MQTT with a NodeMCUv2 (ESP8266) board.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is MQTT?
&lt;/h2&gt;

&lt;p&gt;MQTT is a lightweight messaging protocol for IoT devices. &lt;/p&gt;

&lt;p&gt;It is a publish-subscribe protocol, which means that devices can publish messages to a topic and other devices can subscribe to that topic to receive messages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is MQTT needed?
&lt;/h2&gt;

&lt;p&gt;As the ESP8266 is a low-cost WiFi microcontroller, it can connect to a WiFi network, and it can transmit data wirelessly.&lt;/p&gt;

&lt;p&gt;For sending data from the ESP8266 to the cloud, we need to use the MQTT protocol. &lt;/p&gt;

&lt;h2&gt;
  
  
  Integrate MQTT with ESP8266
&lt;/h2&gt;

&lt;p&gt;Let's start with setting up mqtt with esp8266.&lt;/p&gt;

&lt;p&gt;We use &lt;code&gt;PubSubClient&lt;/code&gt; library for mqtt with esp8266.&lt;/p&gt;

&lt;p&gt;Add this in platformio.ini file.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight ini&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;[env:nodemcuv2]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;platform&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;espressif8266&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;board&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nodemcuv2&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;framework&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;arduino&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;upload_speed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;115200&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;monitor_speed&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;115200&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;lib_deps&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; 
    &lt;span class=&quot;s&quot;&gt;knolleary/PubSubClient @ ^2.8&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the simple example of MQTT with ESP8266.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight cpp&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;Arduino.h&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;WiFi.h&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;PubSubClient.h&amp;gt;&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt; // &amp;lt;-- Added MQTT library&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// --- 1. WiFi Credentials ---&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ssid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Add your wifi name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; 
&lt;span class=&quot;c1&quot;&gt;// !! YOU MUST CHANGE THIS to your WiFi password !!&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Add your wifi password&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; 

&lt;span class=&quot;c1&quot;&gt;// --- 2. MQTT Credentials ---&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mqtt_server&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Add your mqtt server&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;mqtt_port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1883&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mqtt_user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Add your mqtt username&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mqtt_pass&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Add your mqtt password&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// This is the topic we will listen to&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mqtt_topic_to_subscribe&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;nodemcu/messages&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// --- 3. MQTT Client Objects ---&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;WiFiClient&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;espClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;PubSubClient&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;espClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;


&lt;span class=&quot;c1&quot;&gt;// --- 4. Function to handle incoming MQTT messages ---&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;topic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Message arrived [&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;topic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;] &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Print the message payload&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// --- 5. Function to connect to MQTT ---&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;reconnect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Loop until we're reconnected&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Attempting MQTT connection...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Attempt to connect&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;esp32-client-123&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mqtt_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mqtt_pass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;connected&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Subscribe to the topic&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mqtt_topic_to_subscribe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Subscribed to topic: &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mqtt_topic_to_subscribe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;failed, rc=&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot; try again in 5 seconds&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Wait 5 seconds before retrying&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// --- 6. setup() ---&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// This runs once at the beginning&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;115200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Connecting to: &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ssid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Start connecting to WiFi&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;WiFi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;begin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ssid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Wait for the connection to complete&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WiFi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WL_CONNECTED&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// If we get here, we are connected!&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// New line&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;WiFi connected!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;IP address: &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WiFi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;localIP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// --- Add MQTT setup ---&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setServer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mqtt_server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mqtt_port&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setCallback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Set the function to run when a message arrives&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;


&lt;span class=&quot;c1&quot;&gt;// --- 7. loop() ---&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// This runs over and over, forever&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;loop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Check if connected to MQTT, if not, reconnect&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;reconnect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// This function processes MQTT messages&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; 

    &lt;span class=&quot;c1&quot;&gt;// Small delay to keep things stable&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; 
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;/p&gt;

&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyvss13x2rvf3o2xpz35e.png&quot; class=&quot;article-body-image-wrapper&quot;&gt;&lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyvss13x2rvf3o2xpz35e.png&quot; alt=&quot;&quot; width=&quot;800&quot; height=&quot;461&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Finally, we could set up MQTT with ESP8266. &lt;/p&gt;

&lt;p&gt;In the next article, we will use MQTT with PainlessMesh. To send data from one ESP8266 to another ESP8266.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/HexmosTech/git-lrc&quot; rel=&quot;noopener noreferrer nofollow&quot;&gt;&lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fabn6nl3iehrohehnxgje.png&quot; alt=&quot;git-lrc&quot; width=&quot;800&quot; height=&quot;373&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Any feedback or contributors are welcome! It&amp;rsquo;s online, source-available, and ready for anyone to use. &lt;br&gt;
&amp;#11088; Star it on GitHub: &lt;a href=&quot;https://github.com/HexmosTech/git-lrc&quot; rel=&quot;noopener noreferrer nofollow&quot;&gt;https://github.com/HexmosTech/git-lrc&lt;/a&gt;&lt;/p&gt;


</description>
<category>Other</category>
<guid isPermaLink="true">https://coderlegion.com/14080/how-to-set-up-mqtt-with-esp8266</guid>
<pubDate>Tue, 21 Apr 2026 21:12:45 +0000</pubDate>
</item>
<item>
<title>How to Send Data from PainlessMesh to the Cloud Using MQTT in an ESP8266?</title>
<link>https://coderlegion.com/14078/how-to-send-data-from-painlessmesh-to-the-cloud-using-mqtt-in-an-esp8266</link>
<description>
&lt;p&gt;Hello, I'm Ganesh. I'm building &lt;em&gt;git-lrc&lt;/em&gt;, an AI code reviewer that runs on every commit. It is free, unlimited, and source-available on Github. &lt;a href=&quot;https://github.com/HexmosTech/git-lrc&quot; rel=&quot;noopener noreferrer nofollow&quot;&gt;Star Us&lt;/a&gt; to help devs discover the project. Do give it a try and share your feedback for improving the product.&lt;/p&gt;

&lt;p&gt;In this article, I will be demonstrating how to use PainlessMesh and MQTT with a NodeMCUv2 (ESP8266) board.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Problem we are solving?
&lt;/h2&gt;

&lt;p&gt;We will be reciving data from multiple nodes and send it to cloud using MQTT.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Publish the data from received at gateway to cloud using MQTT&lt;/li&gt;
&lt;li&gt;Scallable MQTT topics publishing&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjxazz1kqoaahvzrp0kxy.png&quot; class=&quot;article-body-image-wrapper&quot;&gt;&lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjxazz1kqoaahvzrp0kxy.png&quot; alt=&quot;&quot; width=&quot;800&quot; height=&quot;1446&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Receive data from multiple nodes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These data will be in compressed in these format.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight cpp&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onMeshReceive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint32_t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[mesh] From %u: %s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;JsonDocument&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deserializeJson&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DeserializationError&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[mesh] JSON parse failed&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msgType&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;data&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;topic&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buildTopic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msgType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mqttClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;mqttClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;publish&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;topic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[mqtt] Published &amp;rarr; %s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;topic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[mqtt] Not connected &amp;mdash; message dropped&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Publish the data from received at gateway to cloud using MQTT&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Setup wifi credentials.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight cpp&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;#define STATION_SSID &quot;your_ssid&quot;
#define STATION_PASSWORD &quot;your_password&quot;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Connect to wifi using painlessmesh.&lt;/p&gt;

&lt;p&gt;As painlessmesh is heavy library it will consume lot of power and data.&lt;/p&gt;

&lt;p&gt;So we will use mesh.stationManual() to connect to wifi.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight cpp&quot;&gt;&lt;code&gt; &lt;span class=&quot;n&quot;&gt;mesh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stationManual&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;STATION_SSID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                     &lt;span class=&quot;n&quot;&gt;STATION_PASSWORD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// &amp;larr; key: mesh owns the STA&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;mesh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setHostname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ais-gateway&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Scallable MQTT topics publishing
We will use MQTT to send data from gateway to cloud.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We will use these credentials for MQTT.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight cpp&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;#define MQTT_HOST &quot;your_mqtt_host&quot;
#define MQTT_PORT_TLS 8883
#define MQTT_USER &quot;your_mqtt_user&quot;
#define MQTT_PASS &quot;your_mqtt_password&quot;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will use these topics for MQTT.&lt;/p&gt;

&lt;p&gt;Each topic will have different meaning.&lt;/p&gt;

&lt;p&gt;For example we will use these topics for MQTT.&lt;/p&gt;

&lt;p&gt;For sending an industrial sensor data we will use these topics.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight cpp&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;#define T_VERSION &quot;v1&quot;
#define T_TENANT &quot;electrotech&quot;
#define T_INDUSTRY &quot;pcba&quot;
#define T_SITE &quot;plant1&quot;
#define T_GATEWAY &quot;gw_01&quot;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally followed with config message.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight json&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;config&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;schema&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;temp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;float&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;scale&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;offset&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;min&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;max&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;51.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;hum&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;float&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;scale&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;offset&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;-20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;min&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;max&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;122.3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;gas&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;float&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;scale&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;offset&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;min&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;max&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1023&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight cpp&quot;&gt;&lt;code&gt;&lt;span class=&quot;cm&quot;&gt;/**
 * gateway.cpp &amp;mdash; PainlessMesh Root + MQTT Bridge (HiveMQ TLS)
 *
 * Follows the official painlessMesh MQTT bridge pattern:
 *   - mesh.stationManual() hands WiFi STA management to the mesh library
 *     so it never disconnects from the router to scan for mesh peers.
 *   - MQTT connect is triggered by IP change detection in loop().
 *   - mesh.setRoot(true) called BEFORE init().
 *
 * Data flow: sensor node &amp;rarr; mesh &amp;rarr; gateway &amp;rarr; HiveMQ MQTT &amp;rarr; backend
 * Topic format: v1/{tenant}/{industry}/{site}/{gateway}/{node}/{type}
 */&lt;/span&gt;

&lt;span class=&quot;cp&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;Arduino.h&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;ArduinoJson.h&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;ESP8266WiFi.h&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;PubSubClient.h&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;WiFiClientSecure.h&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;painlessMesh.h&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// &amp;#9472;&amp;#9472;&amp;#9472; WiFi credentials &amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#define STATION_SSID &quot;your_ssid&quot;
#define STATION_PASSWORD &quot;your_password&quot;
&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// &amp;#9472;&amp;#9472;&amp;#9472; Mesh credentials &amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#define MESH_PREFIX &quot;your_mesh_prefix&quot;
#define MESH_PASSWORD &quot;your_mesh_password&quot;
#define MESH_PORT 5555
&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// &amp;#9472;&amp;#9472;&amp;#9472; MQTT credentials (from backend .env) &amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#define MQTT_HOST &quot;your_mqtt_host&quot;
#define MQTT_PORT_TLS 8883
#define MQTT_USER &quot;your_mqtt_user&quot;
#define MQTT_PASS &quot;your_mqtt_password&quot;
&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// &amp;#9472;&amp;#9472;&amp;#9472; Topic routing constants &amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;#define T_VERSION &quot;v1&quot;
#define T_TENANT &quot;electrotech&quot;
#define T_INDUSTRY &quot;pcba&quot;
#define T_SITE &quot;plant1&quot;
#define T_GATEWAY &quot;gw_01&quot;
&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// &amp;#9472;&amp;#9472;&amp;#9472; Globals &amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;painlessMesh&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mesh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;BearSSL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WiFiClientSecure&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wifiClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;PubSubClient&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;mqttClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wifiClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;IPAddress&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;myIP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;long&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lastMqttAttempt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// &amp;#9472;&amp;#9472;&amp;#9472; Helpers &amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;buildTopic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;uint32_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nodeId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msgType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T_VERSION&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;/&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T_TENANT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;/&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T_INDUSTRY&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;/&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T_SITE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
         &lt;span class=&quot;s&quot;&gt;&quot;/&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T_GATEWAY&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;/&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nodeId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;/&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msgType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;IPAddress&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;getStationIP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IPAddress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mesh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getStationIP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// &amp;#9472;&amp;#9472;&amp;#9472; MQTT callback (placeholder for future /cmd downlink) &amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;mqttCallback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;topic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;byte&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[mqtt] Received on %s: %.*s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;topic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;payload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// Phase 2: parse /cmd and forward to specific mesh node via mesh.sendSingle()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// &amp;#9472;&amp;#9472;&amp;#9472; Mesh callback: forward every message to the correct MQTT topic&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// &amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;onMeshReceive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint32_t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[mesh] From %u: %s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;JsonDocument&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deserializeJson&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DeserializationError&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[mesh] JSON parse failed&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msgType&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;data&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;topic&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;buildTopic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msgType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mqttClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;mqttClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;publish&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;topic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[mqtt] Published &amp;rarr; %s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;topic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Serial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[mqtt] Not connected &amp;mdash; message dropped&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// &amp;#9472;&amp;#9472;&amp;#9472; Setup &amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&amp;#9472;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
</description>
<category>Other</category>
<guid isPermaLink="true">https://coderlegion.com/14078/how-to-send-data-from-painlessmesh-to-the-cloud-using-mqtt-in-an-esp8266</guid>
<pubDate>Mon, 13 Apr 2026 23:52:28 +0000</pubDate>
</item>
</channel>
</rss>