registration: self-assigned camera IDs (Option B) + tolerate clockless status
Build (Dev) / build (push) Failing after 16s
CI / quality (push) Failing after 0s
CI / quality (pull_request) Successful in 11s

Auto-registration never completed: the firmware announced on the wrong
topic, the hub never replied, and an unregistered node couldn't receive a
reply anyway. Switch to self-assigned IDs:

firmware (esp32-mqtt-bridge.cpp):
- camera_id defaults to the device id (clientID, e.g. rig-86d978)
- always subscribe to <id>/command; announce on the contract topic
  remoterig/cameras/<id>/announce (was the unmatched announce-<id> form)
- drop the bogus numeric timestamp from status (node has no clock)

hub (subscriber.go):
- handleAnnounce registers new cameras under the node's self-assigned id
  (no cam-NNN, no registered reply)
- handleStatus tolerates an empty/invalid timestamp and stamps server-side
  (previously rejected the status outright)

docs/MQTT_CONTRACT.md updated to match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Joshua King
2026-06-05 12:14:00 -04:00
parent 9fc80a27c9
commit 7929d1d969
3 changed files with 36 additions and 38 deletions
+13 -11
View File
@@ -311,23 +311,25 @@ bool connectMQTT() {
Serial.println("[MQTT] Connected");
// Subscribe to commands (if registered)
if (cfg.camera_id.length() > 0) {
mqtt.subscribe(mqttTopic("command").c_str(), 2);
// Option B: self-assigned, stable camera_id derived from the device id.
if (cfg.camera_id.length() == 0) {
cfg.camera_id = clientID(); // e.g. "rig-86d978"
}
// Announce if new
if (cfg.camera_id.length() == 0) {
// Subscribe to our command topic.
mqtt.subscribe(mqttTopic("command").c_str(), 2);
// Announce (retained) on the contract topic so the hub registers/tracks us.
{
JsonDocument doc;
doc["mac_address"] = WiFi.macAddress();
doc["firmware_version"] = "0.3.0-esp32-mqtt-bridge";
doc["friendly_name"] = "Cam-" + clientID();
doc["firmware_version"] = "0.4.0-esp32-mqtt-bridge";
doc["friendly_name"] = "Cam-" + cfg.camera_id;
JsonArray caps = doc["capabilities"].to<JsonArray>();
caps.add("start_stop"); caps.add("status");
String payload; serializeJson(doc, payload);
String announceTopic = "remoterig/cameras/announce-" + clientID();
mqtt.publish(announceTopic.c_str(), payload.c_str(), true);
Serial.println("[MQTT] Announced for registration");
mqtt.publish(mqttTopic("announce").c_str(), payload.c_str(), true);
Serial.printf("[MQTT] Announced as %s\n", cfg.camera_id.c_str());
}
return true;
@@ -489,7 +491,7 @@ void loop() {
// Build the MQTT status payload per contract
JsonDocument mqttDoc;
mqttDoc["camera_id"] = cfg.camera_id;
mqttDoc["timestamp"] = millis();
// No timestamp: the node has no real clock; the hub stamps on receipt.
mqttDoc["battery_raw"] = dispBatteryRaw;
int pct = batteryPct(dispBatteryRaw);
if (pct >= 0) mqttDoc["battery_pct"] = pct; // omit when uncalibrated