Added MQTT Heartbeat and changed Firware Versioning System
This commit is contained in:
@@ -13,7 +13,8 @@ MQTTAsyncClient::MQTTAsyncClient(ConfigManager& configManager, Networking& netwo
|
||||
: _configManager(configManager)
|
||||
, _networking(networking)
|
||||
, _messageCallback(nullptr)
|
||||
, _mqttReconnectTimer(nullptr) {
|
||||
, _mqttReconnectTimer(nullptr)
|
||||
, _heartbeatTimer(nullptr) {
|
||||
|
||||
_instance = this; // Set static instance pointer
|
||||
|
||||
@@ -25,6 +26,15 @@ MQTTAsyncClient::MQTTAsyncClient(ConfigManager& configManager, Networking& netwo
|
||||
(void*)0, // Timer ID (can store data)
|
||||
mqttReconnectTimerCallback // Callback function when timer expires
|
||||
);
|
||||
|
||||
// Create heartbeat timer (auto-reload every 30 seconds)
|
||||
_heartbeatTimer = xTimerCreate(
|
||||
"mqttHeartbeat", // Timer name
|
||||
pdMS_TO_TICKS(HEARTBEAT_INTERVAL), // Period: 30000ms = 30 seconds
|
||||
pdTRUE, // Auto-reload (true) - repeating timer
|
||||
(void*)0, // Timer ID
|
||||
heartbeatTimerCallback // Callback function
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@@ -32,6 +42,10 @@ MQTTAsyncClient::~MQTTAsyncClient() {
|
||||
if (_mqttReconnectTimer) {
|
||||
xTimerDelete(_mqttReconnectTimer, portMAX_DELAY);
|
||||
}
|
||||
if (_heartbeatTimer) {
|
||||
xTimerStop(_heartbeatTimer, 0);
|
||||
xTimerDelete(_heartbeatTimer, portMAX_DELAY);
|
||||
}
|
||||
_mqttClient.disconnect();
|
||||
}
|
||||
|
||||
@@ -155,6 +169,9 @@ void MQTTAsyncClient::onMqttConnect(bool sessionPresent) {
|
||||
|
||||
// Subscribe to control topic
|
||||
subscribe();
|
||||
|
||||
// 🔥 Start heartbeat timer
|
||||
startHeartbeat();
|
||||
}
|
||||
|
||||
void MQTTAsyncClient::onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
|
||||
@@ -184,6 +201,9 @@ void MQTTAsyncClient::onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
|
||||
}
|
||||
|
||||
LOG_ERROR("❌ Disconnected from MQTT broker - Reason: %s (%d)", reasonStr, static_cast<int>(reason));
|
||||
|
||||
// Stop heartbeat timer when disconnected
|
||||
stopHeartbeat();
|
||||
|
||||
if (_networking.isConnected()) {
|
||||
LOG_INFO("Network still connected - scheduling MQTT reconnection in %d seconds", MQTT_RECONNECT_DELAY / 1000);
|
||||
@@ -237,4 +257,89 @@ void MQTTAsyncClient::mqttReconnectTimerCallback(TimerHandle_t xTimer) {
|
||||
if (MQTTAsyncClient::_instance) {
|
||||
MQTTAsyncClient::_instance->attemptReconnection();
|
||||
}
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════════════
|
||||
// HEARTBEAT FUNCTIONALITY
|
||||
// ═══════════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
void MQTTAsyncClient::startHeartbeat() {
|
||||
if (_heartbeatTimer) {
|
||||
LOG_INFO("💓 Starting MQTT heartbeat (every %d seconds)", HEARTBEAT_INTERVAL / 1000);
|
||||
|
||||
// Publish first heartbeat immediately
|
||||
publishHeartbeat();
|
||||
|
||||
// Start periodic timer
|
||||
xTimerStart(_heartbeatTimer, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void MQTTAsyncClient::stopHeartbeat() {
|
||||
if (_heartbeatTimer) {
|
||||
xTimerStop(_heartbeatTimer, 0);
|
||||
LOG_INFO("❤️ Stopped MQTT heartbeat");
|
||||
}
|
||||
}
|
||||
|
||||
void MQTTAsyncClient::publishHeartbeat() {
|
||||
if (!_mqttClient.connected()) {
|
||||
LOG_WARNING("⚠️ Cannot publish heartbeat - MQTT not connected");
|
||||
return;
|
||||
}
|
||||
|
||||
// Build heartbeat JSON message
|
||||
StaticJsonDocument<512> doc;
|
||||
doc["status"] = "INFO";
|
||||
doc["type"] = "heartbeat";
|
||||
|
||||
JsonObject payload = doc.createNestedObject("payload");
|
||||
|
||||
// Device ID from NVS
|
||||
payload["device_id"] = _configManager.getDeviceUID();
|
||||
|
||||
// Firmware version
|
||||
payload["firmware_version"] = _configManager.getFwVersion();
|
||||
|
||||
// Current date/time (from TimeKeeper if available, else uptime-based)
|
||||
// For now, we'll use a simple timestamp format
|
||||
unsigned long uptimeMs = millis();
|
||||
unsigned long uptimeSec = uptimeMs / 1000;
|
||||
unsigned long hours = uptimeSec / 3600;
|
||||
unsigned long minutes = (uptimeSec % 3600) / 60;
|
||||
unsigned long seconds = uptimeSec % 60;
|
||||
|
||||
char timestampStr[64];
|
||||
snprintf(timestampStr, sizeof(timestampStr), "Uptime: %luh %lum %lus", hours, minutes, seconds);
|
||||
payload["timestamp"] = timestampStr;
|
||||
|
||||
// IP address
|
||||
payload["ip_address"] = _networking.getLocalIP();
|
||||
|
||||
// Gateway address
|
||||
payload["gateway"] = _networking.getGateway();
|
||||
|
||||
// Uptime in milliseconds
|
||||
payload["uptime_ms"] = uptimeMs;
|
||||
|
||||
// Serialize to string
|
||||
String heartbeatMessage;
|
||||
serializeJson(doc, heartbeatMessage);
|
||||
|
||||
// Publish to heartbeat topic with RETAIN flag
|
||||
String heartbeatTopic = "vesper/" + _configManager.getDeviceUID() + "/status/heartbeat";
|
||||
uint16_t packetId = _mqttClient.publish(heartbeatTopic.c_str(), 1, true, heartbeatMessage.c_str());
|
||||
|
||||
if (packetId > 0) {
|
||||
LOG_DEBUG("💓 Published heartbeat (retained) - IP: %s, Uptime: %lums",
|
||||
_networking.getLocalIP().c_str(), uptimeMs);
|
||||
} else {
|
||||
LOG_ERROR("❌ Failed to publish heartbeat");
|
||||
}
|
||||
}
|
||||
|
||||
void MQTTAsyncClient::heartbeatTimerCallback(TimerHandle_t xTimer) {
|
||||
if (MQTTAsyncClient::_instance) {
|
||||
MQTTAsyncClient::_instance->publishHeartbeat();
|
||||
}
|
||||
}
|
||||
@@ -113,4 +113,12 @@ private:
|
||||
static const unsigned long MQTT_RECONNECT_DELAY = 5000; // 5 seconds
|
||||
void attemptReconnection();
|
||||
static void mqttReconnectTimerCallback(TimerHandle_t xTimer);
|
||||
|
||||
// Heartbeat Timer (30 seconds)
|
||||
TimerHandle_t _heartbeatTimer;
|
||||
static const unsigned long HEARTBEAT_INTERVAL = 30000; // 30 seconds
|
||||
void publishHeartbeat();
|
||||
static void heartbeatTimerCallback(TimerHandle_t xTimer);
|
||||
void startHeartbeat();
|
||||
void stopHeartbeat();
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user