# 💓 MQTT Heartbeat Feature ## Overview Implemented a **retained MQTT heartbeat** system that sends periodic status updates every 30 seconds when the controller is connected to MQTT. ## What It Does ### Heartbeat Message Every 30 seconds, the controller publishes a **retained** message to: ``` vesper/{deviceID}/status/heartbeat ``` ### Message Format ```json { "status": "INFO", "type": "heartbeat", "payload": { "device_id": "VESPER-ABC123", "firmware_version": "130", "timestamp": "Uptime: 5h 23m 45s", "ip_address": "192.168.1.100", "gateway": "192.168.1.1", "uptime_ms": 19425000 } } ``` ### Key Features ✅ **Retained Message** - Only the LAST heartbeat stays on the broker ✅ **Auto-Start** - Begins when MQTT connects ✅ **Auto-Stop** - Stops when MQTT disconnects ✅ **30-Second Interval** - Periodic updates ✅ **First Beat Immediate** - Sends first heartbeat right after connecting ✅ **QoS 1** - Reliable delivery ## Why This is Awesome ### For Your Flutter App 1. **Immediate Status** - Any new connection gets the last known status instantly 2. **Stale Detection** - Can detect if controller went offline (timestamp too old) 3. **Device Discovery** - Apps can subscribe to `vesper/+/status/heartbeat` to find all controllers 4. **No Polling** - Just subscribe once and get automatic updates ### Example App Logic ```dart // Subscribe to heartbeat mqtt.subscribe('vesper/DEVICE-123/status/heartbeat'); // On message received if (heartbeat.uptime_ms > lastSeen.uptime_ms + 120000) { // No heartbeat for 2+ minutes = controller offline showOfflineWarning(); } ``` ## Implementation Details ### Files Modified 1. **MQTTAsyncClient.hpp** - Added heartbeat timer and methods 2. **MQTTAsyncClient.cpp** - Implemented heartbeat logic 3. **Networking.hpp** - Added `getGateway()` method 4. **Networking.cpp** - Implemented `getGateway()` method ### New Methods Added ```cpp void startHeartbeat(); // Start 30s periodic timer void stopHeartbeat(); // Stop timer void publishHeartbeat(); // Build and publish message void heartbeatTimerCallback(); // Timer callback handler ``` ### Timer Configuration - **Type**: FreeRTOS Software Timer - **Mode**: Auto-reload (repeating) - **Period**: 30,000 ms (30 seconds) - **Core**: Runs on Core 0 (MQTT task core) ## Testing ### How to Test 1. Flash the firmware 2. Subscribe to the heartbeat topic: ```bash mosquitto_sub -h YOUR_BROKER -t "vesper/+/status/heartbeat" -v ``` 3. You should see heartbeats every 30 seconds 4. Disconnect the controller - the last message stays retained 5. Reconnect - you'll immediately see the last retained message, then new ones every 30s ### Expected Serial Output ``` 💓 Starting MQTT heartbeat (every 30 seconds) 💓 Published heartbeat (retained) - IP: 192.168.1.100, Uptime: 45000ms 💓 Published heartbeat (retained) - IP: 192.168.1.100, Uptime: 75000ms ❤️ Stopped MQTT heartbeat (when MQTT disconnects) ``` ## Future Enhancements (Optional) ### Possible Additions: - Add actual RTC timestamp (instead of just uptime) - Add WiFi signal strength (RSSI) for WiFi connections - Add free heap memory - Add current playback status - Add bell configuration version/hash ### Implementation Example: ```cpp // In publishHeartbeat() payload["rssi"] = WiFi.RSSI(); // WiFi signal strength payload["free_heap"] = ESP.getFreeHeap(); payload["playback_active"] = player.isPlaying; ``` ## Configuration ### Current Settings (can be changed in MQTTAsyncClient.hpp): ```cpp static const unsigned long HEARTBEAT_INTERVAL = 30000; // 30 seconds ``` To change interval to 60 seconds: ```cpp static const unsigned long HEARTBEAT_INTERVAL = 60000; // 60 seconds ``` ## Notes - Message is published with **QoS 1** (at least once delivery) - Message is **retained** (broker keeps last message) - Timer starts automatically when MQTT connects - Timer stops automatically when MQTT disconnects - First heartbeat is sent immediately upon connection (no 30s wait) --- **Feature Implemented**: January 2025 **Version**: Firmware v130+ **Status**: ✅ Production Ready