3.8 KiB
MQTT API
The program is both an MQTT subscriber (remote control) and publisher (status + capture events). It uses the Eclipse Paho C++ async client (MQTT.cpp). All topics are namespaced by tower name:
GGS/FWT/<tower_name>/...
<tower_name> comes from config.ini (General.tower_name). All messages use QoS 1. Published messages
are retained.
Connection
- Broker URI =
Network.zkms_server_ipfromconfig.ini; client ID = the tower name. - Auth:
mqtt_user/mqtt_pwfrom config;clean_session = true, keep-alive 20 s. - Connect timeout 5 s; on connection loss the client auto-reconnects (
reconnect()with a 2.5 s backoff, re-subscribing on success). - The program exits at startup if the initial connect fails (main.cpp lines 162-165).
Subscribed topics (inbound — remote control)
Subscribed in MQTTCallback::connected() (MQTT.cpp lines 17-23). Handled in
message_arrived() (MQTT.cpp lines 25-54).
| Topic | Payload | Parsed as | Effect |
|---|---|---|---|
GGS/FWT/<tower>/target_HDG |
integer heading as string, e.g. "137" |
validated with stoi, stored as string |
Sets the target heading used in ControlCode 1 (kd<heading>) |
GGS/FWT/<tower>/ControlCode |
integer as string, "0" or "1" |
stoi → int |
Selects the capture mode (see below) |
Invalid (non-integer) payloads are caught and logged; the previous value is kept.
The main loop consumes these via get_sub_data(), which returns a snapshot and clears the "available"
flags so each update is acted on once (MQTT.h lines 125-131).
ControlCode semantics
| ControlCode | Behaviour (main.cpp lines 293-334) |
|---|---|
0 |
Automatic sweep. On each interval, send p to advance/stop the gimbal, then trigger cameras. |
1 |
Directed. On each interval, send kd<target_HDG> to drive to the heading from target_HDG, then trigger. |
When a ControlCode message arrives, the program echoes the current code back on the StatusCode topic.
Published topics (outbound)
| Topic | When | Payload | QoS / Retain |
|---|---|---|---|
GGS/FWT/<tower>/StatusCode |
At startup ("0"); whenever a ControlCode message is received (echoes the code) |
integer as string | 1 / retained |
GGS/FWT/<tower>/CamEvent |
After each image is saved | JSON object (below) | 1 / retained |
CamEvent payload
Built in Camera.cpp line 389:
{ "fwt":"Staeffelsberg", "cam":"RGB", "hdg":1373, "time":1719312345678 }
| Field | Type | Meaning |
|---|---|---|
fwt |
string | Tower name (config.ini tower_name) |
cam |
string | Camera label: RGB, ACR, or NIR (by camera index) |
hdg |
int | Gimbal heading × 10 (one decimal place encoded as integer) at capture time |
time |
int | Capture timestamp, Unix epoch milliseconds (matches the .jxl filename) |
The
timevalue is the same Unix-ms timestamp used as the image filename, so a consumer can locate the file for a given event:<RGB|ACR|NIR>/<time>.jxl.
Topic summary
subscribe: GGS/FWT/<tower>/target_HDG (int heading)
GGS/FWT/<tower>/ControlCode (0 = auto sweep, 1 = directed)
publish: GGS/FWT/<tower>/StatusCode (echoed control code)
GGS/FWT/<tower>/CamEvent (JSON: fwt, cam, hdg×10, time-ms)
Local testing
Point zkms_server_ip at a local broker and exercise the topics with Mosquitto:
mosquitto_sub -t 'GGS/FWT/Staeffelsberg/#' -v # watch everything for the tower
mosquitto_pub -t 'GGS/FWT/Staeffelsberg/ControlCode' -m '0'
mosquitto_pub -t 'GGS/FWT/Staeffelsberg/target_HDG' -m '180'