mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 15:59:52 +03:00
added MQTT heartbeat and fixed uptime
This commit is contained in:
@@ -43,6 +43,7 @@ MyESP::MyESP() {
|
|||||||
_helpProjectCmds_count = 0;
|
_helpProjectCmds_count = 0;
|
||||||
|
|
||||||
_use_serial = false;
|
_use_serial = false;
|
||||||
|
_heartbeat = false;
|
||||||
_mqtt_host = NULL;
|
_mqtt_host = NULL;
|
||||||
_mqtt_password = NULL;
|
_mqtt_password = NULL;
|
||||||
_mqtt_username = NULL;
|
_mqtt_username = NULL;
|
||||||
@@ -137,6 +138,26 @@ bool MyESP::getUseSerial() {
|
|||||||
return (_use_serial);
|
return (_use_serial);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// heartbeat
|
||||||
|
bool MyESP::getHeartbeat() {
|
||||||
|
return (_heartbeat);
|
||||||
|
}
|
||||||
|
|
||||||
|
// init heap ram
|
||||||
|
uint32_t MyESP::getInitialFreeHeap() {
|
||||||
|
static uint32_t _heap = 0;
|
||||||
|
|
||||||
|
if (0 == _heap) {
|
||||||
|
_heap = ESP.getFreeHeap();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _heap;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t MyESP::getUsedHeap() {
|
||||||
|
return getInitialFreeHeap() - ESP.getFreeHeap();
|
||||||
|
}
|
||||||
|
|
||||||
// called when WiFi is connected, and used to start OTA, MQTT
|
// called when WiFi is connected, and used to start OTA, MQTT
|
||||||
void MyESP::_wifiCallback(justwifi_messages_t code, char * parameter) {
|
void MyESP::_wifiCallback(justwifi_messages_t code, char * parameter) {
|
||||||
if ((code == MESSAGE_CONNECTED)) {
|
if ((code == MESSAGE_CONNECTED)) {
|
||||||
@@ -507,7 +528,11 @@ void MyESP::_consoleShowHelp() {
|
|||||||
} else {
|
} else {
|
||||||
myDebug_P(PSTR("* Hostname: %s (%s)"), _getESPhostname().c_str(), WiFi.localIP().toString().c_str());
|
myDebug_P(PSTR("* Hostname: %s (%s)"), _getESPhostname().c_str(), WiFi.localIP().toString().c_str());
|
||||||
myDebug_P(PSTR("* WiFi SSID: %s (signal %d%%)"), WiFi.SSID().c_str(), getWifiQuality());
|
myDebug_P(PSTR("* WiFi SSID: %s (signal %d%%)"), WiFi.SSID().c_str(), getWifiQuality());
|
||||||
myDebug_P(PSTR("* MQTT %s"), mqttClient.connected() ? "connected" : "disconnected");
|
if (isMQTTConnected()) {
|
||||||
|
myDebug_P(PSTR("* MQTT connected (heartbeat %s)"), getHeartbeat() ? "enabled" : "disabled");
|
||||||
|
} else {
|
||||||
|
myDebug_P(PSTR("* MQTT disconnected"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
myDebug_P(PSTR("*"));
|
myDebug_P(PSTR("*"));
|
||||||
@@ -598,6 +623,7 @@ void MyESP::_printSetCommands() {
|
|||||||
|
|
||||||
myDebug_P(PSTR("")); // newline
|
myDebug_P(PSTR("")); // newline
|
||||||
myDebug_P(PSTR(" serial=%s"), (_use_serial) ? "on" : "off");
|
myDebug_P(PSTR(" serial=%s"), (_use_serial) ? "on" : "off");
|
||||||
|
myDebug_P(PSTR(" heartbeat=%s"), (_heartbeat) ? "on" : "off");
|
||||||
|
|
||||||
// print any custom settings
|
// print any custom settings
|
||||||
(_fs_settings_callback)(MYESP_FSACTION_LIST, 0, NULL, NULL);
|
(_fs_settings_callback)(MYESP_FSACTION_LIST, 0, NULL, NULL);
|
||||||
@@ -700,6 +726,23 @@ bool MyESP::_changeSetting(uint8_t wc, const char * setting, const char * value)
|
|||||||
ok = false;
|
ok = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else if (strcmp(setting, "heartbeat") == 0) {
|
||||||
|
ok = true;
|
||||||
|
_heartbeat = false;
|
||||||
|
if (value) {
|
||||||
|
if (strcmp(value, "on") == 0) {
|
||||||
|
_heartbeat = true;
|
||||||
|
ok = true;
|
||||||
|
myDebug_P(PSTR("Heartbeat on"));
|
||||||
|
} else if (strcmp(value, "off") == 0) {
|
||||||
|
_heartbeat = false;
|
||||||
|
ok = true;
|
||||||
|
myDebug_P(PSTR("Heartbeat off"));
|
||||||
|
} else {
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// finally check for any custom commands
|
// finally check for any custom commands
|
||||||
ok = (_fs_settings_callback)(MYESP_FSACTION_SET, wc, setting, value);
|
ok = (_fs_settings_callback)(MYESP_FSACTION_SET, wc, setting, value);
|
||||||
@@ -917,7 +960,6 @@ void MyESP::_setCustomResetReason(uint8_t reason) {
|
|||||||
_setSystemResetReason(reason);
|
_setSystemResetReason(reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Treat memory as dirty on cold boot, hardware wdt reset and rst pin
|
|
||||||
bool MyESP::_rtcmemStatus() {
|
bool MyESP::_rtcmemStatus() {
|
||||||
bool readable;
|
bool readable;
|
||||||
|
|
||||||
@@ -961,9 +1003,6 @@ void MyESP::_deferredReset(unsigned long delaytime, unsigned char reason) {
|
|||||||
// Call this method on boot with start=true to increase the crash counter
|
// Call this method on boot with start=true to increase the crash counter
|
||||||
// Call it again once the system is stable to decrease the counter
|
// Call it again once the system is stable to decrease the counter
|
||||||
// If the counter reaches SYSTEM_CHECK_MAX then the system is flagged as unstable
|
// If the counter reaches SYSTEM_CHECK_MAX then the system is flagged as unstable
|
||||||
// setting _systemOK = false;
|
|
||||||
//
|
|
||||||
// An unstable system will only have serial access, WiFi in AP mode and OTA
|
|
||||||
void MyESP::_setSystemCheck(bool stable) {
|
void MyESP::_setSystemCheck(bool stable) {
|
||||||
uint8_t value = 0;
|
uint8_t value = 0;
|
||||||
|
|
||||||
@@ -1019,9 +1058,10 @@ void MyESP::showSystemStats() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// uptime
|
// uptime
|
||||||
uint32_t t = _getUptime(); // seconds
|
uint32_t t = _getUptime(); // seconds
|
||||||
|
|
||||||
uint32_t d = t / 86400L;
|
uint32_t d = t / 86400L;
|
||||||
uint32_t h = (t / 3600L) % 60;
|
uint32_t h = ((t % 86400L) / 3600L) % 60;
|
||||||
uint32_t rem = t % 3600L;
|
uint32_t rem = t % 3600L;
|
||||||
uint8_t m = rem / 60;
|
uint8_t m = rem / 60;
|
||||||
uint8_t s = rem % 60;
|
uint8_t s = rem % 60;
|
||||||
@@ -1099,10 +1139,58 @@ void MyESP::showSystemStats() {
|
|||||||
myDebug_P(PSTR(" [MEM] Firmware size: %d"), ESP.getSketchSize());
|
myDebug_P(PSTR(" [MEM] Firmware size: %d"), ESP.getSketchSize());
|
||||||
myDebug_P(PSTR(" [MEM] Max OTA size: %d"), (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000);
|
myDebug_P(PSTR(" [MEM] Max OTA size: %d"), (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000);
|
||||||
myDebug_P(PSTR(" [MEM] OTA Reserved: %d"), 4 * SPI_FLASH_SEC_SIZE);
|
myDebug_P(PSTR(" [MEM] OTA Reserved: %d"), 4 * SPI_FLASH_SEC_SIZE);
|
||||||
myDebug_P(PSTR(" [MEM] Free Heap: %d"), ESP.getFreeHeap());
|
|
||||||
|
uint32_t total_memory = getInitialFreeHeap();
|
||||||
|
uint32_t free_memory = ESP.getFreeHeap();
|
||||||
|
|
||||||
|
myDebug(" [MEM] Free Heap: %d bytes initially | %d bytes used (%2u%%) | %d bytes free (%2u%%)",
|
||||||
|
total_memory,
|
||||||
|
total_memory - free_memory,
|
||||||
|
100 * (total_memory - free_memory) / total_memory,
|
||||||
|
free_memory,
|
||||||
|
100 * free_memory / total_memory);
|
||||||
|
|
||||||
myDebug_P(PSTR(""));
|
myDebug_P(PSTR(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send heartbeat via MQTT with all system data
|
||||||
|
*/
|
||||||
|
void MyESP::_heartbeatCheck(bool force = false) {
|
||||||
|
static uint32_t last_heartbeat = 0;
|
||||||
|
|
||||||
|
if ((millis() - last_heartbeat > HEARTBEAT_INTERVAL) || force) {
|
||||||
|
last_heartbeat = millis();
|
||||||
|
|
||||||
|
if (!isMQTTConnected() || !(_heartbeat)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t total_memory = getInitialFreeHeap();
|
||||||
|
uint32_t free_memory = ESP.getFreeHeap();
|
||||||
|
uint8_t mem_available = 100 * free_memory / total_memory; // as a %
|
||||||
|
|
||||||
|
char payload[300] = {0};
|
||||||
|
char s[10];
|
||||||
|
strlcpy(payload, "version=", sizeof(payload));
|
||||||
|
strlcat(payload, _app_version, sizeof(payload)); // version
|
||||||
|
strlcat(payload, ", IP=", sizeof(payload));
|
||||||
|
strlcat(payload, WiFi.localIP().toString().c_str(), sizeof(payload)); // IP address
|
||||||
|
strlcat(payload, ", rssid=", sizeof(payload));
|
||||||
|
strlcat(payload, itoa(getWifiQuality(), s, 10), sizeof(payload)); // rssi %
|
||||||
|
strlcat(payload, "%, load=", sizeof(payload));
|
||||||
|
strlcat(payload, ltoa(getSystemLoadAverage(), s, 10), sizeof(payload)); // load
|
||||||
|
strlcat(payload, "%, uptime=", sizeof(payload));
|
||||||
|
strlcat(payload, ltoa(_getUptime(), s, 10), sizeof(payload)); // uptime in secs
|
||||||
|
strlcat(payload, "secs, freemem=", sizeof(payload));
|
||||||
|
strlcat(payload, itoa(mem_available, s, 10), sizeof(payload)); // free mem as a %
|
||||||
|
strlcat(payload, "%", sizeof(payload));
|
||||||
|
|
||||||
|
// send to MQTT
|
||||||
|
myESP.mqttPublish(MQTT_TOPIC_HEARTBEAT, payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// handler for Telnet
|
// handler for Telnet
|
||||||
void MyESP::_telnetHandle() {
|
void MyESP::_telnetHandle() {
|
||||||
SerialAndTelnet.handle();
|
SerialAndTelnet.handle();
|
||||||
@@ -1396,6 +1484,8 @@ bool MyESP::_fs_loadConfig() {
|
|||||||
|
|
||||||
_use_serial = (bool)json["use_serial"];
|
_use_serial = (bool)json["use_serial"];
|
||||||
|
|
||||||
|
_heartbeat = (bool)json["heartbeat"];
|
||||||
|
|
||||||
// callback for loading custom settings
|
// callback for loading custom settings
|
||||||
// ok is false if there's a problem loading a custom setting (e.g. does not exist)
|
// ok is false if there's a problem loading a custom setting (e.g. does not exist)
|
||||||
bool ok = (_fs_callback)(MYESP_FSACTION_LOAD, json);
|
bool ok = (_fs_callback)(MYESP_FSACTION_LOAD, json);
|
||||||
@@ -1424,6 +1514,7 @@ bool MyESP::fs_saveConfig() {
|
|||||||
json["mqtt_username"] = _mqtt_username;
|
json["mqtt_username"] = _mqtt_username;
|
||||||
json["mqtt_password"] = _mqtt_password;
|
json["mqtt_password"] = _mqtt_password;
|
||||||
json["use_serial"] = _use_serial;
|
json["use_serial"] = _use_serial;
|
||||||
|
json["heartbeat"] = _heartbeat;
|
||||||
|
|
||||||
// callback for saving custom settings
|
// callback for saving custom settings
|
||||||
(void)(_fs_callback)(MYESP_FSACTION_SAVE, json);
|
(void)(_fs_callback)(MYESP_FSACTION_SAVE, json);
|
||||||
@@ -1475,19 +1566,19 @@ void MyESP::_fs_setup() {
|
|||||||
// _fs_printConfig(); // enable for debugging
|
// _fs_printConfig(); // enable for debugging
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t MyESP::getSystemLoadAverage() {
|
uint32_t MyESP::getSystemLoadAverage() {
|
||||||
return _load_average;
|
return _load_average;
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate load average
|
// calculate load average
|
||||||
void MyESP::_calculateLoad() {
|
void MyESP::_calculateLoad() {
|
||||||
static unsigned long last_loadcheck = 0;
|
static uint32_t last_loadcheck = 0;
|
||||||
static unsigned long load_counter_temp = 0;
|
static uint32_t load_counter_temp = 0;
|
||||||
load_counter_temp++;
|
load_counter_temp++;
|
||||||
|
|
||||||
if (millis() - last_loadcheck > LOADAVG_INTERVAL) {
|
if (millis() - last_loadcheck > LOADAVG_INTERVAL) {
|
||||||
static unsigned long load_counter = 0;
|
static uint32_t load_counter = 0;
|
||||||
static unsigned long load_counter_max = 1;
|
static uint32_t load_counter_max = 1;
|
||||||
|
|
||||||
load_counter = load_counter_temp;
|
load_counter = load_counter_temp;
|
||||||
load_counter_temp = 0;
|
load_counter_temp = 0;
|
||||||
@@ -1694,9 +1785,11 @@ void MyESP::begin(const char * app_hostname, const char * app_name, const char *
|
|||||||
_app_name = strdup(app_name);
|
_app_name = strdup(app_name);
|
||||||
_app_version = strdup(app_version);
|
_app_version = strdup(app_version);
|
||||||
|
|
||||||
|
getInitialFreeHeap(); // get initial free mem
|
||||||
|
|
||||||
_rtcmemSetup();
|
_rtcmemSetup();
|
||||||
_telnet_setup(); // Telnet setup, called first to set Serial
|
_telnet_setup(); // Telnet setup, called first to set Serial
|
||||||
_eeprom_setup(); // set up eeprom for storing crash data, if compiled with -DCRASH
|
_eeprom_setup(); // set up EEPROM for storing crash data, if compiled with -DCRASH
|
||||||
_fs_setup(); // SPIFFS setup, do this first to get values
|
_fs_setup(); // SPIFFS setup, do this first to get values
|
||||||
_wifi_setup(); // WIFI setup
|
_wifi_setup(); // WIFI setup
|
||||||
_ota_setup(); // init OTA
|
_ota_setup(); // init OTA
|
||||||
@@ -1706,6 +1799,7 @@ void MyESP::begin(const char * app_hostname, const char * app_name, const char *
|
|||||||
SerialAndTelnet.flush();
|
SerialAndTelnet.flush();
|
||||||
|
|
||||||
_setSystemCheck(false); // reset system check
|
_setSystemCheck(false); // reset system check
|
||||||
|
_heartbeatCheck(true); // force heartbeat
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1714,11 +1808,10 @@ void MyESP::begin(const char * app_hostname, const char * app_name, const char *
|
|||||||
void MyESP::loop() {
|
void MyESP::loop() {
|
||||||
_calculateLoad();
|
_calculateLoad();
|
||||||
_systemCheckLoop();
|
_systemCheckLoop();
|
||||||
|
_heartbeatCheck();
|
||||||
|
|
||||||
_telnetHandle();
|
_telnetHandle();
|
||||||
|
jw.loop(); // WiFi
|
||||||
jw.loop(); // WiFi
|
|
||||||
|
|
||||||
ArduinoOTA.handle(); // OTA
|
ArduinoOTA.handle(); // OTA
|
||||||
_mqttConnect(); // MQTT
|
_mqttConnect(); // MQTT
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
#ifndef MyEMS_h
|
#ifndef MyEMS_h
|
||||||
#define MyEMS_h
|
#define MyEMS_h
|
||||||
|
|
||||||
#define MYESP_VERSION "1.1.14"
|
#define MYESP_VERSION "1.1.16"
|
||||||
|
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <ArduinoOTA.h>
|
#include <ArduinoOTA.h>
|
||||||
@@ -18,7 +18,6 @@
|
|||||||
#include <FS.h>
|
#include <FS.h>
|
||||||
#include <JustWifi.h> // https://github.com/xoseperez/justwifi
|
#include <JustWifi.h> // https://github.com/xoseperez/justwifi
|
||||||
#include <TelnetSpy.h> // modified from https://github.com/yasheena/telnetspy
|
#include <TelnetSpy.h> // modified from https://github.com/yasheena/telnetspy
|
||||||
#include <Ticker.h>
|
|
||||||
|
|
||||||
#include <EEPROM_Rotate.h>
|
#include <EEPROM_Rotate.h>
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -50,8 +49,9 @@ extern struct rst_info resetInfo;
|
|||||||
#define MQTT_RECONNECT_DELAY_MIN 2000 // Try to reconnect in 3 seconds upon disconnection
|
#define MQTT_RECONNECT_DELAY_MIN 2000 // Try to reconnect in 3 seconds upon disconnection
|
||||||
#define MQTT_RECONNECT_DELAY_STEP 3000 // Increase the reconnect delay in 3 seconds after each failed attempt
|
#define MQTT_RECONNECT_DELAY_STEP 3000 // Increase the reconnect delay in 3 seconds after each failed attempt
|
||||||
#define MQTT_RECONNECT_DELAY_MAX 120000 // Set reconnect time to 2 minutes at most
|
#define MQTT_RECONNECT_DELAY_MAX 120000 // Set reconnect time to 2 minutes at most
|
||||||
#define MQTT_MAX_TOPIC_SIZE 50 // max length of MQTT message
|
#define MQTT_MAX_TOPIC_SIZE 50 // max length of MQTT topic
|
||||||
#define MQTT_TOPIC_START "start"
|
#define MQTT_TOPIC_START "start"
|
||||||
|
#define MQTT_TOPIC_HEARTBEAT "heartbeat"
|
||||||
#define MQTT_TOPIC_START_PAYLOAD "start"
|
#define MQTT_TOPIC_START_PAYLOAD "start"
|
||||||
#define MQTT_TOPIC_RESTART "restart"
|
#define MQTT_TOPIC_RESTART "restart"
|
||||||
|
|
||||||
@@ -153,8 +153,9 @@ struct RtcmemData {
|
|||||||
|
|
||||||
static_assert(sizeof(RtcmemData) <= (RTCMEM_BLOCKS * 4u), "RTCMEM struct is too big");
|
static_assert(sizeof(RtcmemData) <= (RTCMEM_BLOCKS * 4u), "RTCMEM struct is too big");
|
||||||
|
|
||||||
#define SYSTEM_CHECK_TIME 60000 // The system is considered stable after these many millis
|
#define SYSTEM_CHECK_TIME 60000 // The system is considered stable after these many millis (1 minute)
|
||||||
#define SYSTEM_CHECK_MAX 5 // After this many crashes on boot
|
#define SYSTEM_CHECK_MAX 5 // After this many crashes on boot
|
||||||
|
#define HEARTBEAT_INTERVAL 120000 // in milliseconds, how often the MQTT heartbeat is sent (2 mins)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool set; // is it a set command
|
bool set; // is it a set command
|
||||||
@@ -230,14 +231,14 @@ class MyESP {
|
|||||||
void crashInfo();
|
void crashInfo();
|
||||||
|
|
||||||
// general
|
// general
|
||||||
void end();
|
void end();
|
||||||
void loop();
|
void loop();
|
||||||
void begin(const char * app_hostname, const char * app_name, const char * app_version);
|
void begin(const char * app_hostname, const char * app_name, const char * app_version);
|
||||||
void setBoottime(const char * boottime);
|
void setBoottime(const char * boottime);
|
||||||
void resetESP();
|
void resetESP();
|
||||||
uint16_t getSystemLoadAverage();
|
int getWifiQuality();
|
||||||
int getWifiQuality();
|
void showSystemStats();
|
||||||
void showSystemStats();
|
bool getHeartbeat();
|
||||||
|
|
||||||
// rtcmem and reset reason
|
// rtcmem and reset reason
|
||||||
bool rtcmemStatus();
|
bool rtcmemStatus();
|
||||||
@@ -322,6 +323,7 @@ class MyESP {
|
|||||||
char * _boottime;
|
char * _boottime;
|
||||||
bool _suspendOutput;
|
bool _suspendOutput;
|
||||||
bool _use_serial;
|
bool _use_serial;
|
||||||
|
bool _heartbeat;
|
||||||
unsigned long _getUptime();
|
unsigned long _getUptime();
|
||||||
String _buildTime();
|
String _buildTime();
|
||||||
|
|
||||||
@@ -347,10 +349,15 @@ class MyESP {
|
|||||||
void _systemCheckLoop();
|
void _systemCheckLoop();
|
||||||
void _setSystemCheck(bool stable);
|
void _setSystemCheck(bool stable);
|
||||||
|
|
||||||
|
// load average (0..100) and heap ram
|
||||||
|
uint32_t getSystemLoadAverage();
|
||||||
|
void _calculateLoad();
|
||||||
|
uint32_t _load_average;
|
||||||
|
uint32_t getInitialFreeHeap();
|
||||||
|
uint32_t getUsedHeap();
|
||||||
|
|
||||||
// load average (0..100)
|
// heartbeat
|
||||||
void _calculateLoad();
|
void _heartbeatCheck(bool force);
|
||||||
unsigned short int _load_average;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern MyESP myESP;
|
extern MyESP myESP;
|
||||||
|
|||||||
Reference in New Issue
Block a user