Automatic Publication of Status and Config¶
The RTK Base firmware automatically publishes its live status and full
device configuration as a single JSON document to the MQTT topic
base/{SN}/info. The publication happens on connect, periodically, on
important hardware events, and on demand. This is the primary mechanism
by which a fleet manager, cloud portal, or external integration learns
what the base is, what it is configured to do, and what it is currently
doing.
The same JSON document is also returned by the HTTP GET /get-info endpoint;
both surfaces are byte-identical at the field level. See
REST API » Get System Information
for the HTTP counterpart.
Transport¶
| Property | Value | Source |
|---|---|---|
| Topic | base/<SN>/info |
components/mqtt_manager/mqtt_api.c:314 |
| QoS | 0 (best effort) |
components/mqtt_manager/mqtt_api.c:315 (literal 0) |
| Retain | 0 (not retained) |
components/mqtt_manager/mqtt_api.c:325 |
| Content-Type | raw UTF-8 JSON (MQTT has no content type) | — |
| Payload upper bound | MQTT_PAYLOAD_MAX_SIZE = 131072 bytes (128 KiB) |
components/mqtt_manager/mqtt_api.c:25 |
| ESP-MQTT outbox limit | 64 * 1024 bytes |
components/mqtt_manager/mqtt_manager.c:44 |
| Transport | esp_mqtt_client_publish() via mqtt_publish_data() |
components/mqtt_manager/mqtt_manager.c:277 |
| Client type literal | "base" (so the topic prefix is always base/...) |
components/rtk_base/app_base_manager.c:1304 |
<SN> source |
NVS key BASE_CONFIG_SERIAL_NUM |
components/mqtt_manager/mqtt_manager.c:99 |
The publish primitive logs a single ESP_LOGE ("Impossible to publish data,
mqtt isn't ready.") the first time the link is not up, and silently returns
ESP_ERR_INVALID_STATE on every subsequent call until the link is restored.
Publication Triggers¶
There are four code paths that schedule an info publication. All four go
through the same work-queue entry mqtt-publish-info, executed by
mqtt_api_publish_info_work() at
components/mqtt_manager/mqtt_api.c:321, which calls
mqtt_api_publish_info(~0, 0).
| # | Trigger | Condition | Source |
|---|---|---|---|
| 1 | MQTT connect | MQTT_EVENT_CONNECTED is received |
components/mqtt_manager/mqtt_manager.c:336 (mqtt_api_schedule_initial_info()) |
| 2 | Periodic 6 h | uptime_sec() - last_info > 6 * 3600 |
components/mqtt_manager/mqtt_api.c:336-340, polled from components/rtk_base/periodic_task.c:190 |
| 3 | ZED UBX MON-VER received | First time the ZED module reports its MON-VER UBX message (class 0x27, id 0x03) carrying the unique ID |
components/rtk_base/app_base_manager.c:1037-1053 (zed_ubx_recv_callback) |
| 4 | On-demand request | Any client publishes to base/{SN}/info/get |
components/mqtt_manager/mqtt_api.c:398-399 |
Throttle semantics¶
- The 6-hour counter (
last_info) is updated only on a successful publish (components/mqtt_manager/mqtt_api.c:326-327). - The periodic task wakes every
TASK_WAKE_UP_PERIOD = 100 ms(components/rtk_base/periodic_task.c:29) and re-evaluates the throttle each tick. - On a fresh MQTT connect, Trigger 1 fires first and resets the 6-hour timer.
- If MQTT has been offline for an extended period, the moment the link comes back up Trigger 1 publishes and resets the timer — there is no "catch-up burst" of missed publications.
Async dispatch¶
Triggers 1, 2, and 3 all call mqtt_api_schedule_initial_info() which uses
the brlib work queue:
If the work queue is full, add_work() returns ESP_FAIL and the publish
is silently dropped (components/mqtt_manager/mqtt_api.c does not check
the return value).
On-Demand Request¶
A consumer can request a fresh info publication at any time by publishing an empty message to the trigger topic:
mosquitto_pub -h mqtt.yamabikorobots.net \
-p 8883 \
--cert client.crt --key client.key \
-t "base/BRRB000021/info/get" \
-m ""
The base responds by publishing the same full JSON document to
base/{SN}/info (Trigger 4 above). The request/response pair is handled in
components/mqtt_manager/mqtt_api.c:393-401:
This is the same pattern used for the other REST-like operations over MQTT (see MQTT API).
JSON Payload Schema¶
The payload is built by rtkbase_json_get_info() in
components/http_manager/http_server.c:165 (approximately 750 lines).
It takes a bitmask argument that controls which fields are emitted.
The MQTT publisher always passes ~0 (all bits set), so what the
broker sees is the maximum-detail document.
Bitmask flags¶
Defined in components/mqtt_manager/include/mqtt_api.h:20-24:
| Flag | Value | Meaning |
|---|---|---|
INF_RUNTIME |
0x001 |
Live values: state, uptime, memory, antenna, RTCM, position, IP, etc. |
INF_STATIC |
0x002 |
Values that do not change at runtime: firmware version, sha256, idf, build date, hardware, modem SIM, etc. |
INF_CONFIG |
0x004 |
User-configurable settings: NVS-stored parameters. |
INF_BASIC |
0x100 |
Granularity level: emit only the core top-level fields. |
INF_ALL |
0x200 |
Granularity level: emit all sub-fields. |
ANY |
~0 (used internally) |
All bits set — full document. |
The combination is evaluated by the helper macro at
components/http_manager/http_server.c:167:
Schema¶
The full per-field schema (every top-level key, every nested object,
every conditional field, and a complete worked example) lives in
REST API » Get System Information.
The HTTP GET /get-info endpoint and the MQTT base/{SN}/info
publication are byte-identical at the JSON level, so the schema is
authoritative for both surfaces.
The HTTP path also lets the caller request a reduced document (for
example, ?0x06 to get config + runtime only). No current MQTT caller
exercises that — the broker always sees the full document.
Top-level keys¶
For quick orientation, the document groups configuration and runtime
state under these keys (the modem block is only present on hardware
with a 4G modem attached):
| Key | Description |
|---|---|
esp |
ESP32 identity, uptime, memory |
gnss |
ZED module, FSM state, position, reference, RTCM |
base |
Application-level state, LEDs, firmware-update progress |
eth |
Ethernet link state and IP configuration |
wap |
WiFi access point state and connected stations |
sta |
WiFi station uplink state (when STA mode is enabled) |
udp |
UDP RTCM output configuration |
mqtt |
MQTT broker configuration and link state |
router |
Teltonika RUT2xx router |
modem |
Quectel 4G modem and SIM (only on equipped hardware) |
Online Status & Last Will¶
The firmware publishes a single-byte state to base/{SN}/online to
let a remote consumer (fleet manager, dashboard, monitoring agent)
detect whether each base is currently reachable on the broker. The
topic uses the MQTT Last Will and Testament (LWT) mechanism so that
even an ungraceful crash is observable.
| Payload | Meaning |
|---|---|
y |
Online — fresh MQTT session established. The base just published this on MQTT_EVENT_CONNECTED. |
r |
Restarting — the base is briefly reconnecting (cert renewal, broker URL change, error recovery). Reappears within ~5 s as y. |
q |
Quitting — graceful stop (reboot, OTA, WiFi STA lost, console mqtt stop). May come back later as y. |
L |
Lost — broker detected an ungraceful disconnect (no DISCONNECT packet before the 120 s keepalive expired). |
Lowercase letters are published by the base, uppercase L by the
broker — a passive observer can tell which side emitted the message.
Transport: topic base/{SN}/online, QoS 0, retain 1, 1-byte
payload. The LWT is registered on every MQTT_EVENT_BEFORE_CONNECT
(components/mqtt_manager/mqtt_manager.c:325-330); the explicit y is
published on MQTT_EVENT_CONNECTED
(components/mqtt_manager/mqtt_manager.c:339); r is published by
mqtt_schedule_restart() (:654); q is published by mqtt_stop()
(:682); L is the LWT message ("L", :33-38).
Consumer pattern¶
Subscribe once to base/+/online and react to the single retained
byte. A fresh subscriber receives the last-known state immediately on
subscribe (the broker delivers the retained message as a normal
PUBLISH).
mosquitto_sub -h mqtt.yamabikorobots.net -p 8883 \
--cert client.crt --key client.key \
-t "base/+/online" -v
For guaranteed delivery of every transition, subscribe with QoS ≥ 1: the publisher uses QoS 0, but MQTT downgrades to the subscriber's maximum, so a QoS 1 subscriber gets an acknowledged delivery of each state change.
Edge cases¶
- A clean
MQTT_EVENT_DISCONNECTED(transient link drop) does not fire the LWT — the base sends a properDISCONNECTfirst. Consumers do not see a spuriousLduring a normal reconnect. - A transient
rorqpublished with QoS 0 may be lost under network stress, but the next retained publication (yon reconnect, or the broker'sLon keepalive expiry) always converges the consumer's view to the last-known state.
Errors and Failure Modes¶
MQTT offline¶
If the link is not up when mqtt_publish_data() is called, it returns
ESP_ERR_INVALID_STATE, logs a single ESP_LOGE the first time, and
suppresses further warnings via a static link_warned flag
(components/mqtt_manager/mqtt_manager.c:281-303).
For Trigger 2 (the 6-hour periodic), last_info is only updated on
success (components/mqtt_manager/mqtt_api.c:326-327), so the next
periodic-task tick will see now - last_info > 6 * 3600 and re-schedule
the work entry. The publication will then succeed at the first
opportunity.
For Trigger 1 (MQTT connect), the publication is performed inside the
MQTT_EVENT_CONNECTED handler, so the link is by definition up. For
Trigger 3 (ZED UBX), if the link is down at the moment the work entry
runs, the publish is dropped silently and the next trigger will repeat
the attempt.
JSON build failure¶
mqtt_api_publish_info() at
components/mqtt_manager/mqtt_api.c:300-318 does not check whether
mqtt_api_get_info_fn returned NULL. In practice, the function
pointer is always assigned to rtkbase_json_get_info (set in
app_base_manager.c:1306) and that function always returns a valid
cJSON * because its first line is cJSON_CreateObject(). The
defensive fallback at mqtt_api.c:310-312 only kicks in if the
function pointer itself is NULL (impossible in production):
Work-queue overflow¶
add_work() from components/brlib/longwork.c:31-42 returns ESP_FAIL
when the FreeRTOS queue is full. mqtt_api_schedule_initial_info() does
not check this return; the publication is silently dropped. This is
considered acceptable because the next periodic tick or the next
connect/reconnect will retry.
No retries at the publish layer¶
mqtt_api_publish_info() has no internal retry loop. The only retry
mechanism is the periodic trigger (#2) combined with the connect trigger
(#1): if a publish fails, the next 6-hour tick (or the next connect)
will schedule another one. Triggers #3 and #4 have no natural retry —
they depend on the ZED module reporting MON-VER again, or a consumer
re-issuing the info/get request.
See Also¶
- REST API » Get System Information — the HTTP counterpart; same JSON, same bitmask flags
- MQTT API — broker connection details, other REST-like operations over MQTT
- Architecture — system state machines and FreeRTOS task layout
- ↑ Online Status & Last Will — the
base/{SN}/onlinetopic and LWT-based offline detection