4.3 KiB
MQTT API
The program is both an MQTT subscriber (remote control) and publisher (status + capture events). The
MQTT channel is the MqttControlChannel implementation of IControlChannel
(src/mqtt/MqttControlChannel.cpp, Eclipse Paho C++). It is used only when
MQTT is enabled; otherwise a NullControlChannel runs (publishes dropped, auto-sweep mode). 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_pw(preferring$FGC_MQTT_USER/$FGC_MQTT_PW);clean_session = true, keep-alive 20 s,set_automatic_reconnect(true). - Connect timeout 5 s. On connection loss Paho auto-reconnects; the channel re-subscribes on reconnect.
- The program no longer exits if MQTT is unavailable — it logs a warning and continues in degraded mode.
Use
--no-mqttto disable MQTT entirely (null channel).
Subscribed topics (inbound — remote control)
Subscribed on (re)connect; handled in MqttControlChannel::message_arrived()
(src/mqtt/MqttControlChannel.cpp).
| 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 yaw target used in ControlCode 1 (converted to encoder counts via [Motor]) |
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 (MqttControlChannel::poll()).
ControlCode semantics
| ControlCode | Behaviour (CaptureScheduler) |
|---|---|
0 |
Automatic sweep. Walk the scan grid (ping-pong): MOVE to the next (yaw,pitch) waypoint, wait for standstill, trigger cameras. |
1 |
Directed. MOVE yaw to target_HDG (pitch held), wait for standstill, 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 by MqttControlChannel::publishCamEvent from a CamEvent:
{ "fwt":"Staeffelsberg", "cam":"RGB", "hdg":1373, "pit":300, "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 yaw heading × 10 (one decimal place encoded as integer) at capture time |
pit |
int | Gimbal pitch elevation × 10 at capture time (derived from the pitch encoder) |
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, pit×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'