mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-08 00:39:50 +03:00
support for web server and reset firmware option
This commit is contained in:
@@ -5,11 +5,13 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.8.1dev] 2019-06-20
|
||||
## [1.8.1dev] 2019-06-24
|
||||
|
||||
### Added
|
||||
|
||||
- Added back -DCRASH in Debug build target for capturing any ESP8266 stack dumps during crashes
|
||||
- Web Interface, for checking stats and setting wifi credentials. See wiki for more details.
|
||||
- reset firmware option. If the reset button on the ESP is pressed during boot up sequence (the LED is flashing very fast) all settings are erased and goes into AP mode.
|
||||
|
||||
### Fixed
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* MyESP - my ESP helper class to handle WiFi, MQTT and Telnet
|
||||
*
|
||||
* Paul Derbyshire - December 2018
|
||||
* Paul Derbyshire - first revision: December 2018
|
||||
*
|
||||
* Ideas borrowed from Espurna https://github.com/xoseperez/espurna
|
||||
*/
|
||||
@@ -14,9 +14,10 @@ EEPROM_Rotate EEPROMr;
|
||||
|
||||
union system_rtcmem_t {
|
||||
struct {
|
||||
uint8_t stability_counter;
|
||||
uint8_t reset_reason;
|
||||
uint16_t _reserved_;
|
||||
uint8_t stability_counter;
|
||||
uint8_t reset_reason;
|
||||
uint8_t boot_status;
|
||||
uint8_t _reserved_;
|
||||
} parts;
|
||||
uint32_t value;
|
||||
};
|
||||
@@ -41,10 +42,12 @@ MyESP::MyESP() {
|
||||
_fs_callback = NULL;
|
||||
_fs_settings_callback = NULL;
|
||||
|
||||
_web_callback = NULL;
|
||||
|
||||
_helpProjectCmds = NULL;
|
||||
_helpProjectCmds_count = 0;
|
||||
|
||||
_use_serial = false;
|
||||
_serial = false;
|
||||
_heartbeat = false;
|
||||
_mqtt_host = NULL;
|
||||
_mqtt_password = NULL;
|
||||
@@ -61,6 +64,8 @@ MyESP::MyESP() {
|
||||
_mqtt_last_connection = 0;
|
||||
_mqtt_connecting = false;
|
||||
|
||||
_firstInstall = false;
|
||||
|
||||
_wifi_password = NULL;
|
||||
_wifi_ssid = NULL;
|
||||
_wifi_callback = NULL;
|
||||
@@ -137,7 +142,7 @@ void MyESP::myDebug_P(PGM_P format_P, ...) {
|
||||
|
||||
// use Serial?
|
||||
bool MyESP::getUseSerial() {
|
||||
return (_use_serial);
|
||||
return (_serial);
|
||||
}
|
||||
|
||||
// heartbeat
|
||||
@@ -191,7 +196,7 @@ void MyESP::_wifiCallback(justwifi_messages_t code, char * parameter) {
|
||||
_wifi_connected = true;
|
||||
|
||||
// finally if we don't want Serial anymore, turn it off
|
||||
if (!_use_serial) {
|
||||
if (!_serial) {
|
||||
myDebug_P(PSTR("Disabling serial port communication."));
|
||||
SerialAndTelnet.flush(); // flush so all buffer is printed to serial
|
||||
SerialAndTelnet.setSerial(NULL);
|
||||
@@ -213,14 +218,6 @@ void MyESP::_wifiCallback(justwifi_messages_t code, char * parameter) {
|
||||
myDebug_P(PSTR("[WIFI] IP %s"), WiFi.softAPIP().toString().c_str());
|
||||
myDebug_P(PSTR("[WIFI] MAC %s"), WiFi.softAPmacAddress().c_str());
|
||||
|
||||
// we could be in panic mode so enable Serial again
|
||||
if (!_use_serial) {
|
||||
SerialAndTelnet.setSerial(&Serial);
|
||||
_use_serial = true;
|
||||
}
|
||||
|
||||
myDebug_P(PSTR("Enabling serial port output"));
|
||||
|
||||
// call any final custom settings
|
||||
if (_wifi_callback) {
|
||||
_wifi_callback();
|
||||
@@ -326,7 +323,7 @@ void MyESP::_mqttOnMessage(char * topic, char * payload, size_t len) {
|
||||
// Restart the device
|
||||
if (strcmp(topic, MQTT_TOPIC_RESTART) == 0) {
|
||||
myDebug_P(PSTR("[MQTT] Received restart command"), message);
|
||||
myESP.resetESP();
|
||||
resetESP();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -334,7 +331,7 @@ void MyESP::_mqttOnMessage(char * topic, char * payload, size_t len) {
|
||||
// for example with HA it sends the system time from the server
|
||||
if (strcmp(topic, MQTT_TOPIC_START) == 0) {
|
||||
myDebug_P(PSTR("[MQTT] Received boottime: %s"), message);
|
||||
myESP.setBoottime(message);
|
||||
setBoottime(message);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -487,7 +484,7 @@ void MyESP::_ota_setup() {
|
||||
ArduinoOTA.onStart([this]() { _OTACallback(); });
|
||||
ArduinoOTA.onEnd([this]() {
|
||||
myDebug_P(PSTR("[OTA] Done, restarting..."));
|
||||
_deferredReset(100, CUSTOM_RESET_OTA);
|
||||
_deferredReset(500, CUSTOM_RESET_OTA);
|
||||
});
|
||||
|
||||
ArduinoOTA.onProgress([this](unsigned int progress, unsigned int total) {
|
||||
@@ -551,13 +548,7 @@ void MyESP::_telnetConnected() {
|
||||
uint32_t crash_time;
|
||||
EEPROMr.get(SAVE_CRASH_EEPROM_OFFSET + SAVE_CRASH_CRASH_TIME, crash_time);
|
||||
if ((crash_time != 0) && (crash_time != 0xFFFFFFFF)) {
|
||||
crashDump();
|
||||
/*
|
||||
// clear crash data
|
||||
crash_time = 0xFFFFFFFF;
|
||||
EEPROMr.put(SAVE_CRASH_EEPROM_OFFSET + SAVE_CRASH_CRASH_TIME, crash_time);
|
||||
EEPROMr.commit();
|
||||
*/
|
||||
myDebug_P(PSTR("[SYSTEM] There is stack data available from the last system crash. Use 'crash dump' to view and 'crash clear' to reset"));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -692,7 +683,7 @@ void MyESP::_printSetCommands() {
|
||||
}
|
||||
|
||||
myDebug_P(PSTR("")); // newline
|
||||
myDebug_P(PSTR(" serial=%s"), (_use_serial) ? "on" : "off");
|
||||
myDebug_P(PSTR(" serial=%s"), (_serial) ? "on" : "off");
|
||||
myDebug_P(PSTR(" heartbeat=%s"), (_heartbeat) ? "on" : "off");
|
||||
|
||||
// print any custom settings
|
||||
@@ -704,7 +695,7 @@ void MyESP::_printSetCommands() {
|
||||
// reset / restart
|
||||
void MyESP::resetESP() {
|
||||
myDebug_P(PSTR("* Reboot ESP..."));
|
||||
_deferredReset(100, CUSTOM_RESET_TERMINAL);
|
||||
_deferredReset(500, CUSTOM_RESET_TERMINAL);
|
||||
end();
|
||||
#if defined(ARDUINO_ARCH_ESP32)
|
||||
ESP.restart();
|
||||
@@ -781,16 +772,16 @@ bool MyESP::_changeSetting(uint8_t wc, const char * setting, const char * value)
|
||||
ok = true;
|
||||
|
||||
} else if (strcmp(setting, "serial") == 0) {
|
||||
ok = true;
|
||||
_use_serial = false;
|
||||
ok = true;
|
||||
_serial = false;
|
||||
if (value) {
|
||||
if (strcmp(value, "on") == 0) {
|
||||
_use_serial = true;
|
||||
ok = true;
|
||||
_serial = true;
|
||||
ok = true;
|
||||
myDebug_P(PSTR("Reboot ESP to activate Serial mode."));
|
||||
} else if (strcmp(value, "off") == 0) {
|
||||
_use_serial = false;
|
||||
ok = true;
|
||||
_serial = false;
|
||||
ok = true;
|
||||
myDebug_P(PSTR("Reboot ESP to deactivate Serial mode."));
|
||||
} else {
|
||||
ok = false;
|
||||
@@ -842,10 +833,10 @@ void MyESP::setUseSerial(bool toggle) {
|
||||
|
||||
if (toggle) {
|
||||
SerialAndTelnet.setSerial(&Serial);
|
||||
_use_serial = true;
|
||||
_serial = true;
|
||||
} else {
|
||||
SerialAndTelnet.setSerial(NULL);
|
||||
_use_serial = false;
|
||||
_serial = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -987,12 +978,26 @@ unsigned long MyESP::_getUptime() {
|
||||
return uptime_seconds;
|
||||
}
|
||||
|
||||
// reason code
|
||||
// init RTC mem
|
||||
void MyESP::_rtcmemInit() {
|
||||
memset((uint32_t *)RTCMEM_ADDR, 0, sizeof(uint32_t) * RTCMEM_BLOCKS);
|
||||
Rtcmem->magic = RTCMEM_MAGIC;
|
||||
}
|
||||
|
||||
uint8_t MyESP::getSystemBootStatus() {
|
||||
system_rtcmem_t data;
|
||||
data.value = Rtcmem->sys;
|
||||
return data.parts.boot_status;
|
||||
}
|
||||
|
||||
void MyESP::_setSystemBootStatus(uint8_t status) {
|
||||
system_rtcmem_t data;
|
||||
data.value = Rtcmem->sys;
|
||||
data.parts.boot_status = status;
|
||||
Rtcmem->sys = data.value;
|
||||
// myDebug("*** setting boot status to %d", data.parts.boot_status);
|
||||
}
|
||||
|
||||
uint8_t MyESP::_getSystemStabilityCounter() {
|
||||
system_rtcmem_t data;
|
||||
data.value = Rtcmem->sys;
|
||||
@@ -1019,6 +1024,7 @@ void MyESP::_setSystemResetReason(uint8_t reason) {
|
||||
Rtcmem->sys = data.value;
|
||||
}
|
||||
|
||||
// system_get_rst_info() result is cached by the Core init for internal use
|
||||
uint32_t MyESP::getSystemResetReason() {
|
||||
return resetInfo.reason;
|
||||
}
|
||||
@@ -1034,14 +1040,26 @@ void MyESP::_setCustomResetReason(uint8_t reason) {
|
||||
_setSystemResetReason(reason);
|
||||
}
|
||||
|
||||
// returns false if not set and needs to be intialized
|
||||
// returns false if not set and needs to be intialized, causing all rtcmem data to be wiped
|
||||
bool MyESP::_rtcmemStatus() {
|
||||
bool readable;
|
||||
|
||||
switch (getSystemResetReason()) {
|
||||
case REASON_EXT_SYS_RST:
|
||||
case REASON_WDT_RST:
|
||||
case REASON_DEFAULT_RST:
|
||||
uint32_t reason = getSystemResetReason();
|
||||
|
||||
// the last reset could have been caused by manually pressing the reset button
|
||||
// so before wiping, capture the boot sequence
|
||||
if (reason == REASON_EXT_SYS_RST) { // external system reset
|
||||
if (getSystemBootStatus() == MYESP_BOOTSTATUS_BOOTING) {
|
||||
_setSystemBootStatus(MYESP_BOOTSTATUS_RESETNEEDED);
|
||||
} else {
|
||||
_setSystemBootStatus(MYESP_BOOTSTATUS_POWERON);
|
||||
}
|
||||
}
|
||||
|
||||
switch (reason) {
|
||||
//case REASON_EXT_SYS_RST: // external system reset
|
||||
case REASON_WDT_RST: // hardware watch dog reset
|
||||
case REASON_DEFAULT_RST: // normal startup by power on
|
||||
readable = false;
|
||||
break;
|
||||
default:
|
||||
@@ -1071,12 +1089,13 @@ uint8_t MyESP::_getCustomResetReason() {
|
||||
}
|
||||
|
||||
void MyESP::_deferredReset(unsigned long delaytime, uint8_t reason) {
|
||||
delay(delaytime);
|
||||
_setSystemBootStatus(MYESP_BOOTSTATUS_POWERON);
|
||||
_setCustomResetReason(reason);
|
||||
delay(delaytime);
|
||||
}
|
||||
|
||||
// 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 this method on boot with stable=true to reset the crash counter
|
||||
// Each call increments the counter
|
||||
// If the counter reaches SYSTEM_CHECK_MAX then the system is flagged as unstable
|
||||
void MyESP::_setSystemCheck(bool stable) {
|
||||
uint8_t value = 0;
|
||||
@@ -1095,17 +1114,25 @@ void MyESP::_setSystemCheck(bool stable) {
|
||||
if (++value > SYSTEM_CHECK_MAX) {
|
||||
_systemStable = false;
|
||||
value = 0; // system is unstable
|
||||
myDebug_P(PSTR("[SYSTEM] Warning, system UNSTABLE\n"));
|
||||
myDebug_P(PSTR("[SYSTEM] Warning, system UNSTABLE. Serial mode is enabled."));
|
||||
|
||||
// enable Serial again
|
||||
if (!_serial) {
|
||||
SerialAndTelnet.setSerial(&Serial);
|
||||
_serial = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_setSystemStabilityCounter(value);
|
||||
}
|
||||
|
||||
bool MyESP::getSystemCheck() {
|
||||
// return if system is stable (false=bad)
|
||||
bool MyESP::_getSystemCheck() {
|
||||
return _systemStable;
|
||||
}
|
||||
|
||||
// periodically check if system is stable
|
||||
void MyESP::_systemCheckLoop() {
|
||||
static bool checked = false;
|
||||
if (!checked && (millis() > SYSTEM_CHECK_TIME)) {
|
||||
@@ -1144,7 +1171,7 @@ void MyESP::showSystemStats() {
|
||||
|
||||
myDebug_P(PSTR(" [APP] System Load: %d%%"), getSystemLoadAverage());
|
||||
|
||||
if (!getSystemCheck()) {
|
||||
if (!_getSystemCheck()) {
|
||||
myDebug_P(PSTR(" [SYSTEM] Device is in SAFE MODE"));
|
||||
}
|
||||
|
||||
@@ -1158,6 +1185,12 @@ void MyESP::showSystemStats() {
|
||||
|
||||
myDebug_P(PSTR(" [WIFI] WiFi MAC: %s"), WiFi.macAddress().c_str());
|
||||
|
||||
if (isMQTTConnected()) {
|
||||
myDebug_P(PSTR(" [MQTT] connected (heartbeat %s)"), getHeartbeat() ? "enabled" : "disabled");
|
||||
} else {
|
||||
myDebug_P(PSTR(" [MQTT] disconnected"));
|
||||
}
|
||||
|
||||
#ifdef CRASH
|
||||
char output_str[80] = {0};
|
||||
char buffer[16] = {0};
|
||||
@@ -1195,7 +1228,7 @@ void MyESP::showSystemStats() {
|
||||
}
|
||||
myDebug_P(PSTR(" [SYSTEM] Restart count: %d"), _getSystemStabilityCounter());
|
||||
|
||||
myDebug_P(PSTR(" [SYSTEM] rtcmem status:%u blocks:%u addr:0x%p"), _rtcmemStatus(), RtcmemSize, Rtcmem);
|
||||
myDebug_P(PSTR(" [SYSTEM] rtcmem status: blocks:%u addr:0x%p"), RtcmemSize, Rtcmem);
|
||||
for (uint8_t block = 0; block < RtcmemSize; ++block) {
|
||||
myDebug_P(PSTR(" [SYSTEM] rtcmem %02u: %u"), block, reinterpret_cast<volatile uint32_t *>(RTCMEM_ADDR)[block]);
|
||||
}
|
||||
@@ -1289,7 +1322,7 @@ void MyESP::_telnetHandle() {
|
||||
if (charsRead > 0) {
|
||||
charsRead = 0; // is static, so have to reset
|
||||
_suspendOutput = false;
|
||||
if (_use_serial) {
|
||||
if (_serial) {
|
||||
SerialAndTelnet.serialPrint('\n'); // force newline if in Serial
|
||||
}
|
||||
_telnetCommand(_command);
|
||||
@@ -1378,14 +1411,14 @@ void MyESP::_mqttConnect() {
|
||||
// Setup everything we need
|
||||
void MyESP::setWIFI(const char * wifi_ssid, const char * wifi_password, wifi_callback_f callback) {
|
||||
// Check SSID too long or missing
|
||||
if (!wifi_ssid || *wifi_ssid == 0x00 || strlen(wifi_ssid) > 31) {
|
||||
if (!wifi_ssid || *wifi_ssid == 0x00 || strlen(wifi_ssid) > MAX_STR_LEN) {
|
||||
_wifi_ssid = NULL;
|
||||
} else {
|
||||
_wifi_ssid = strdup(wifi_ssid);
|
||||
}
|
||||
|
||||
// Check PASS too long
|
||||
if (!wifi_password || *wifi_ssid == 0x00 || strlen(wifi_password) > 31) {
|
||||
if (!wifi_password || *wifi_ssid == 0x00 || strlen(wifi_password) > MAX_STR_LEN) {
|
||||
_wifi_password = NULL;
|
||||
} else {
|
||||
_wifi_password = strdup(wifi_password);
|
||||
@@ -1501,15 +1534,21 @@ void MyESP::_fs_printConfig() {
|
||||
|
||||
// format File System
|
||||
void MyESP::_fs_eraseConfig() {
|
||||
myDebug_P(PSTR("[FS] Erasing settings, please wait a few seconds. ESP will "
|
||||
myDebug_P(PSTR("[FS] Erasing all settings, please wait a few seconds. ESP will "
|
||||
"automatically restart when finished."));
|
||||
|
||||
if (SPIFFS.format()) {
|
||||
if (SPIFFS.remove(MYEMS_CONFIG_FILE)) {
|
||||
delay(1000); // wait 1 second
|
||||
resetESP();
|
||||
SerialAndTelnet.flush();
|
||||
resetESP(); // hard reset
|
||||
}
|
||||
}
|
||||
|
||||
// custom callback for web info
|
||||
void MyESP::setWeb(web_callback_f callback_web) {
|
||||
_web_callback = callback_web;
|
||||
}
|
||||
|
||||
void MyESP::setSettings(fs_callback_f callback_fs, fs_settings_callback_f callback_settings_fs) {
|
||||
_fs_callback = callback_fs;
|
||||
_fs_settings_callback = callback_settings_fs;
|
||||
@@ -1525,8 +1564,6 @@ bool MyESP::_fs_loadConfig() {
|
||||
return false;
|
||||
} else if (size == 0) {
|
||||
myDebug_P(PSTR("[FS] Failed to open config file"));
|
||||
// file does not exist, so assume its the first install. Set serial to on
|
||||
_use_serial = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1558,10 +1595,13 @@ bool MyESP::_fs_loadConfig() {
|
||||
value = json["mqtt_password"];
|
||||
_mqtt_password = (value) ? strdup(value) : NULL;
|
||||
|
||||
_use_serial = (bool)json["use_serial"]; // defaults to off
|
||||
_serial = (bool)json["serial"]; // defaults to off
|
||||
|
||||
// _serial = true; // uncomment for debugging everything to serial
|
||||
|
||||
_heartbeat = (bool)json["heartbeat"]; // defaults to off
|
||||
|
||||
|
||||
// callback for loading custom settings
|
||||
// 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);
|
||||
@@ -1589,7 +1629,7 @@ bool MyESP::fs_saveConfig() {
|
||||
json["mqtt_host"] = _mqtt_host;
|
||||
json["mqtt_username"] = _mqtt_username;
|
||||
json["mqtt_password"] = _mqtt_password;
|
||||
json["use_serial"] = _use_serial;
|
||||
json["serial"] = _serial;
|
||||
json["heartbeat"] = _heartbeat;
|
||||
|
||||
// callback for saving custom settings
|
||||
@@ -1632,10 +1672,23 @@ void MyESP::_fs_setup() {
|
||||
return;
|
||||
}
|
||||
|
||||
// if its flagged as a first install, re-create the initial config file and quit function
|
||||
if (_firstInstall) {
|
||||
myDebug_P(PSTR("[FS] Re-creating config file for initial install"));
|
||||
fs_saveConfig();
|
||||
return;
|
||||
}
|
||||
|
||||
// load the config file. if it doesn't exist (function returns false) create it
|
||||
if (!_fs_loadConfig()) {
|
||||
//myDebug_P(PSTR("[FS] Re-creating config file"));
|
||||
fs_saveConfig();
|
||||
_firstInstall = true; // flag as a first install
|
||||
}
|
||||
|
||||
// assume if the wifi ssid is empty, its a fresh install too
|
||||
if ((_wifi_ssid == NULL)) {
|
||||
_firstInstall = true; // flag as a first install
|
||||
}
|
||||
|
||||
// _fs_printConfig(); // enable for debugging
|
||||
@@ -1671,14 +1724,6 @@ bool MyESP::isMQTTConnected() {
|
||||
}
|
||||
|
||||
// return true if wifi is connected
|
||||
// WL_NO_SHIELD = 255, // for compatibility with WiFi Shield library
|
||||
// WL_IDLE_STATUS = 0,
|
||||
// WL_NO_SSID_AVAIL = 1,
|
||||
// WL_SCAN_COMPLETED = 2,
|
||||
// WL_CONNECTED = 3,
|
||||
// WL_CONNECT_FAILED = 4,
|
||||
// WL_CONNECTION_LOST = 5,
|
||||
// WL_DISCONNECTED = 6
|
||||
bool MyESP::isWifiConnected() {
|
||||
return (_wifi_connected);
|
||||
}
|
||||
@@ -1855,6 +1900,9 @@ void MyESP::crashDump() {
|
||||
myDebug_P(PSTR("\nTo clean this dump use the command: %scrash clear%s\n"), COLOR_BOLD_ON, COLOR_BOLD_OFF);
|
||||
}
|
||||
|
||||
/*
|
||||
* Force some crashes to test if stack collection and debugging works
|
||||
*/
|
||||
void MyESP::crashTest(uint8_t t) {
|
||||
if (t == 1) {
|
||||
myDebug_P(PSTR("[CRASH] Attempting to divide by zero ..."));
|
||||
@@ -1906,20 +1954,188 @@ void MyESP::crashInfo() {
|
||||
}
|
||||
#endif
|
||||
|
||||
// default home web page
|
||||
void MyESP::_webRootPage() {
|
||||
char s[1000] = {0};
|
||||
|
||||
strlcpy(s, webCommonPage_start, sizeof(s));
|
||||
|
||||
strlcat(s, "<h1>", sizeof(s));
|
||||
strlcat(s, _app_name, sizeof(s));
|
||||
strlcat(s, " version ", sizeof(s));
|
||||
strlcat(s, _app_version, sizeof(s));
|
||||
strlcat(s, "</h1>", sizeof(s));
|
||||
|
||||
strlcat(s, "<p><b>System stats:</b><br>", sizeof(s));
|
||||
strlcat(s, isMQTTConnected() ? " MQTT is connected\n" : " MQTT is disconnected\n", sizeof(s));
|
||||
strlcat(s, "<br>", sizeof(s));
|
||||
|
||||
// uptime
|
||||
char buffer[200];
|
||||
uint32_t t = _getUptime(); // seconds
|
||||
uint32_t d = t / 86400L;
|
||||
uint32_t h = ((t % 86400L) / 3600L) % 60;
|
||||
uint32_t rem = t % 3600L;
|
||||
uint8_t m = rem / 60;
|
||||
uint8_t sec = rem % 60;
|
||||
sprintf(buffer, " System uptime: %d days %d hours %d minutes %d seconds<br>", d, h, m, sec);
|
||||
strlcat(s, buffer, sizeof(s));
|
||||
|
||||
// memory
|
||||
uint32_t total_memory = _getInitialFreeHeap();
|
||||
uint32_t free_memory = ESP.getFreeHeap();
|
||||
sprintf(buffer, " Memory: %d bytes free (%2u%%)<br>", free_memory, 100 * free_memory / total_memory);
|
||||
strlcat(s, buffer, sizeof(s));
|
||||
|
||||
strlcat(s, "<p>", sizeof(s));
|
||||
if (_web_callback) {
|
||||
char custom[MYESP_MAXCHARBUFFER];
|
||||
(_web_callback)(custom);
|
||||
strlcat(s, custom, sizeof(s));
|
||||
}
|
||||
strlcat(s, "</p><br>", sizeof(s));
|
||||
|
||||
// check why we're here
|
||||
if ((_firstInstall) || (_wifi_ssid == NULL)) {
|
||||
strlcat(s, "<p>Looks like a first install! Go <a href=/reset>here</a> to connect the System to your network.</p>", sizeof(s));
|
||||
} else {
|
||||
strlcat(s, "<p>Go <a href=/reset>here</a> to connect the System to your wireless network.</p>", sizeof(s));
|
||||
}
|
||||
|
||||
strlcat(s, webCommonPage_end, sizeof(s));
|
||||
webServer.sendHeader("Content-Length", String(strlen(s)));
|
||||
webServer.send(200, "text/html", s);
|
||||
}
|
||||
|
||||
// Creates a webpage that allows the user to change the SSID and Password from the browser
|
||||
void MyESP::_webResetPage() {
|
||||
char s[1000] = {0};
|
||||
|
||||
strlcpy(s, webCommonPage_start, sizeof(s));
|
||||
|
||||
strlcat(s, "<h1>", sizeof(s));
|
||||
strlcat(s, _app_name, sizeof(s));
|
||||
strlcat(s, " version ", sizeof(s));
|
||||
strlcat(s, _app_version, sizeof(s));
|
||||
strlcat(s, "</h1>", sizeof(s));
|
||||
|
||||
// Check to see if we've been sent any arguments and instantly return if not
|
||||
if (webServer.args() == 0) {
|
||||
strlcat(s, "<p>", sizeof(s));
|
||||
|
||||
if (_wifi_ssid != NULL) {
|
||||
strlcat(s, "Current wifi SSID is ", sizeof(s));
|
||||
strlcat(s, _wifi_ssid, sizeof(s));
|
||||
strlcat(s, ".<br>", sizeof(s));
|
||||
}
|
||||
|
||||
strlcat(s, "<br>Please enter your new wifi credentials below.</p>", sizeof(s));
|
||||
|
||||
strlcat(s, webResetPage_form, sizeof(s));
|
||||
strlcat(s, webCommonPage_end, sizeof(s));
|
||||
webServer.sendHeader("Content-Length", String(strlen(s)));
|
||||
webServer.send(200, "text/html", s);
|
||||
|
||||
} else {
|
||||
// Create a string containing all the arguments
|
||||
// Check to see if there are new values (also doubles to check the length of the new value is long enough)
|
||||
if (webServer.arg("newssid").length() < MAX_STR_LEN) {
|
||||
if (webServer.arg("newssid").length() == 0) {
|
||||
_wifi_ssid = NULL;
|
||||
} else {
|
||||
_wifi_ssid = strdup(webServer.arg("newssid").c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (webServer.arg("newpassword").length() < MAX_STR_LEN) {
|
||||
if (webServer.arg("newpassword").length() == 0) {
|
||||
_wifi_password = NULL;
|
||||
} else {
|
||||
_wifi_password = strdup(webServer.arg("newpassword").c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Store the new settings
|
||||
fs_saveConfig();
|
||||
|
||||
// Reply with a web page to indicate success or failure
|
||||
strlcat(s, webResetPage_post, sizeof(s));
|
||||
strlcat(s, webCommonPage_end, sizeof(s));
|
||||
webServer.sendHeader("Content-Length", String(strlen(s)));
|
||||
webServer.send(200, "text/html", s);
|
||||
|
||||
delay(500);
|
||||
resetESP();
|
||||
}
|
||||
}
|
||||
|
||||
// reset all settings
|
||||
void MyESP::_webResetAllPage() {
|
||||
char s[1000] = {0};
|
||||
|
||||
strlcpy(s, webCommonPage_start, sizeof(s));
|
||||
|
||||
strlcat(s, "<h1>", sizeof(s));
|
||||
strlcat(s, _app_name, sizeof(s));
|
||||
strlcat(s, " version ", sizeof(s));
|
||||
strlcat(s, _app_version, sizeof(s));
|
||||
strlcat(s, "</h1>", sizeof(s));
|
||||
|
||||
// Check to see if we've been sent any arguments and instantly return if not
|
||||
if (webServer.args() == 0) {
|
||||
strlcat(s,
|
||||
"<p>Are you absolutely sure you want to erase all settings?<br>Typing 'yes' will restart the System and you'll need to reconnect to the wifi "
|
||||
"Access Point called ems-esp.</p>",
|
||||
sizeof(s));
|
||||
|
||||
strlcat(s, webResetAllPage_form, sizeof(s));
|
||||
strlcat(s, webCommonPage_end, sizeof(s));
|
||||
webServer.sendHeader("Content-Length", String(strlen(s)));
|
||||
webServer.send(200, "text/html", s);
|
||||
} else {
|
||||
// delete all settings
|
||||
if (webServer.arg("confirm") == "yes") {
|
||||
_fs_eraseConfig();
|
||||
delay(1000); // wait 1 sec
|
||||
resetESP();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set up web server
|
||||
void MyESP::_webserver_setup() {
|
||||
webServer.on("/", [this]() { _webRootPage(); });
|
||||
webServer.on("/reset", [this]() { _webResetPage(); });
|
||||
webServer.on("/resetall", [this]() { _webResetAllPage(); });
|
||||
|
||||
webServer.begin();
|
||||
}
|
||||
|
||||
// setup MyESP
|
||||
void MyESP::begin(const char * app_hostname, const char * app_name, const char * app_version) {
|
||||
_app_hostname = strdup(app_hostname);
|
||||
_app_name = strdup(app_name);
|
||||
_app_version = strdup(app_version);
|
||||
|
||||
_getInitialFreeHeap(); // get initial free mem
|
||||
// set up onboard LED
|
||||
pinMode(LED_BUILTIN, OUTPUT);
|
||||
digitalWrite(LED_BUILTIN, HIGH);
|
||||
|
||||
_getInitialFreeHeap(); // get initial free mem
|
||||
_telnet_setup(); // Telnet setup, called first to set Serial
|
||||
_rtcmemSetup(); // rtc internal mem setup
|
||||
|
||||
if (getSystemBootStatus() == MYESP_BOOTSTATUS_RESETNEEDED) {
|
||||
myDebug_P(PSTR("** resetting all settings"));
|
||||
_firstInstall = true; // flag as an initial install so the config file will be recreated
|
||||
}
|
||||
|
||||
_rtcmemSetup(); // rtc internal mem setup
|
||||
_telnet_setup(); // Telnet setup, called first to set Serial
|
||||
_eeprom_setup(); // set up EEPROM for storing crash data, if compiled with -DCRASH
|
||||
_fs_setup(); // SPIFFS setup, do this first to get values
|
||||
_wifi_setup(); // WIFI setup
|
||||
_ota_setup(); // init OTA
|
||||
|
||||
_fs_setup(); // SPIFFS setup, do this first to get values
|
||||
_wifi_setup(); // WIFI setup
|
||||
_ota_setup(); // init OTA
|
||||
_webserver_setup(); // init web server
|
||||
|
||||
// print a welcome message
|
||||
myDebug_P(PSTR("\n* %s version %s"), _app_name, _app_version);
|
||||
@@ -1929,6 +2145,36 @@ void MyESP::begin(const char * app_hostname, const char * app_name, const char *
|
||||
_heartbeatCheck(true); // force heartbeat
|
||||
}
|
||||
|
||||
// bootup sequence
|
||||
// quickly flash LED until we get a Wifi connection, or AP established
|
||||
// fast way is to use WRITE_PERI_REG(PERIPHS_GPIO_BASEADDR + (state ? 4 : 8), (1 << EMSESP_Status.led_gpio)); // 4 is on, 8 is off
|
||||
void MyESP::_bootupSequence() {
|
||||
uint8_t boot_status = getSystemBootStatus();
|
||||
|
||||
if ((boot_status == MYESP_BOOTSTATUS_BOOTED) || (millis() <= MYESP_BOOTUP_DELAY)) {
|
||||
return; // already booted, or still starting up
|
||||
}
|
||||
|
||||
// only kick in after a few seconds
|
||||
if (boot_status == MYESP_BOOTSTATUS_POWERON) {
|
||||
_setSystemBootStatus(MYESP_BOOTSTATUS_BOOTING);
|
||||
}
|
||||
|
||||
static uint32_t last_bootupflash = 0;
|
||||
|
||||
// flash LED quickly
|
||||
if ((millis() - last_bootupflash > MYESP_BOOTUP_FLASHDELAY)) {
|
||||
last_bootupflash = millis();
|
||||
int state = digitalRead(LED_BUILTIN);
|
||||
digitalWrite(LED_BUILTIN, !state);
|
||||
}
|
||||
|
||||
if (isWifiConnected()) {
|
||||
_setSystemBootStatus(MYESP_BOOTSTATUS_BOOTED); // completed, reset flag
|
||||
digitalWrite(LED_BUILTIN, LOW); // turn off LED
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Loop. This is called as often as possible and it handles wifi, telnet, mqtt etc
|
||||
*/
|
||||
@@ -1936,8 +2182,14 @@ void MyESP::loop() {
|
||||
_calculateLoad();
|
||||
_systemCheckLoop();
|
||||
_heartbeatCheck();
|
||||
_bootupSequence();
|
||||
webServer.handleClient(); // web server client requests
|
||||
|
||||
// if we're in AP mode, use the web server, otherwise switch to telnet
|
||||
if (!isAPmode()) {
|
||||
_telnetHandle();
|
||||
}
|
||||
|
||||
_telnetHandle();
|
||||
jw.loop(); // WiFi
|
||||
ArduinoOTA.handle(); // OTA
|
||||
_mqttConnect(); // MQTT
|
||||
|
||||
@@ -9,20 +9,23 @@
|
||||
#ifndef MyEMS_h
|
||||
#define MyEMS_h
|
||||
|
||||
#define MYESP_VERSION "1.1.17"
|
||||
#define MYESP_VERSION "1.1.18"
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <ArduinoOTA.h>
|
||||
#include <AsyncMqttClient.h> // https://github.com/marvinroger/async-mqtt-client and for ESP32 see https://github.com/marvinroger/async-mqtt-client/issues/127
|
||||
#include <DNSServer.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
#include <FS.h>
|
||||
#include <JustWifi.h> // https://github.com/xoseperez/justwifi
|
||||
#include <TelnetSpy.h> // modified from https://github.com/yasheena/telnetspy
|
||||
|
||||
#ifdef CRASH
|
||||
#include <EEPROM_Rotate.h>
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
void custom_crash_callback(struct rst_info *, uint32_t, uint32_t);
|
||||
#include "user_interface.h"
|
||||
void custom_crash_callback(struct rst_info *, uint32_t, uint32_t);
|
||||
extern struct rst_info resetInfo;
|
||||
}
|
||||
|
||||
@@ -38,10 +41,10 @@ extern struct rst_info resetInfo;
|
||||
|
||||
#define MYEMS_CONFIG_FILE "/config.json"
|
||||
|
||||
#define LOADAVG_INTERVAL 30000 // Interval between calculating load average (in ms)
|
||||
#define LOADAVG_INTERVAL 30000 // Interval between calculating load average (in ms) = 30 seconds
|
||||
|
||||
// WIFI
|
||||
#define WIFI_CONNECT_TIMEOUT 10000 // Connecting timeout for WIFI in ms
|
||||
#define WIFI_CONNECT_TIMEOUT 10000 // Connecting timeout for WIFI in ms (10 seconds)
|
||||
#define WIFI_RECONNECT_INTERVAL 600000 // If could not connect to WIFI, retry after this time in ms. 10 minutes
|
||||
|
||||
// MQTT
|
||||
@@ -143,12 +146,11 @@ PROGMEM const char * const custom_reset_string[] = {custom_reset_hardware, cus
|
||||
#define RTCMEM_OFFSET 32u
|
||||
#define RTCMEM_ADDR (RTCMEM_ADDR_BASE + (RTCMEM_OFFSET * 4u))
|
||||
#define RTCMEM_BLOCKS 96u
|
||||
#define RTCMEM_MAGIC 0x45535075
|
||||
#define RTCMEM_MAGIC 0x45535076
|
||||
|
||||
struct RtcmemData {
|
||||
uint32_t magic; // RTCMEM_MAGIC
|
||||
uint32_t sys; // system reset reason (1-4)
|
||||
uint32_t energy; // store energy count
|
||||
uint32_t magic; // RTCMEM_MAGIC
|
||||
uint32_t sys; // system details
|
||||
};
|
||||
|
||||
static_assert(sizeof(RtcmemData) <= (RTCMEM_BLOCKS * 4u), "RTCMEM struct is too big");
|
||||
@@ -165,6 +167,13 @@ typedef struct {
|
||||
|
||||
typedef enum { MYESP_FSACTION_SET, MYESP_FSACTION_LIST, MYESP_FSACTION_SAVE, MYESP_FSACTION_LOAD } MYESP_FSACTION;
|
||||
|
||||
typedef enum {
|
||||
MYESP_BOOTSTATUS_POWERON = 0,
|
||||
MYESP_BOOTSTATUS_BOOTED = 1,
|
||||
MYESP_BOOTSTATUS_BOOTING = 2,
|
||||
MYESP_BOOTSTATUS_RESETNEEDED = 3
|
||||
} MYESP_BOOTSTATUS; // boot messages
|
||||
|
||||
typedef std::function<void(unsigned int, const char *, const char *)> mqtt_callback_f;
|
||||
typedef std::function<void()> wifi_callback_f;
|
||||
typedef std::function<void()> ota_callback_f;
|
||||
@@ -172,6 +181,7 @@ typedef std::function<void(uint8_t, const char *)>
|
||||
typedef std::function<void(uint8_t)> telnet_callback_f;
|
||||
typedef std::function<bool(MYESP_FSACTION, const JsonObject json)> fs_callback_f;
|
||||
typedef std::function<bool(MYESP_FSACTION, uint8_t, const char *, const char *)> fs_settings_callback_f;
|
||||
typedef std::function<void(char *)> web_callback_f;
|
||||
|
||||
// calculates size of an 2d array at compile time
|
||||
template <typename T, size_t N>
|
||||
@@ -181,12 +191,46 @@ constexpr size_t ArraySize(T (&)[N]) {
|
||||
|
||||
#define UPTIME_OVERFLOW 4294967295 // Uptime overflow value
|
||||
|
||||
// web min and max length of wifi ssid and password
|
||||
#define MAX_STR_LEN 16
|
||||
|
||||
#define MYESP_BOOTUP_FLASHDELAY 50 // flash duration for LED at bootup sequence
|
||||
#define MYESP_BOOTUP_DELAY 2000 // time before we open the window to reset. This is to stop resetting values when uploading firmware via USB
|
||||
|
||||
// max size of char buffer for storing web page
|
||||
#define MYESP_MAXCHARBUFFER 800
|
||||
|
||||
// Holds the admin webpage in the program memory
|
||||
const char webCommonPage_start[] = "<html>"
|
||||
"<head>"
|
||||
"<style>input {font-size: 1.2em; width: 100%; max-width: 350px; display: block; margin: 5px auto; }"
|
||||
"body {background-color: #FFA500;font: normal 18px Verdana, Arial, sans-serif;} </style>"
|
||||
"<meta http-equiv='refresh' content='5'>"
|
||||
"</head><body>";
|
||||
|
||||
const char webCommonPage_end[] = "</body></html>";
|
||||
|
||||
const char webResetPage_form[] = "<form id='form' action='/reset' method='post'>"
|
||||
"<input name='newssid' type='text' maxlength='16' placeholder='SSID'>"
|
||||
"<input name='newpassword' id='password1' type='password' maxlength='16' placeholder='Password'>"
|
||||
"<input type='submit' value='Save and reboot'>"
|
||||
"</form>";
|
||||
|
||||
const char webResetPage_post[] = "<p>New wifi credentials set. System is now rebooting. Please wait a few seconds and then reconnect via telnet or browser to its new IP given address.</p>";
|
||||
|
||||
const char webResetAllPage_form[] = "<form id='resetform' action='/resetall' method='post'>"
|
||||
"<input name='confirm' type='text' minlength='3' maxlength='16' placeholder='yes'>"
|
||||
"<input type='submit' value='Reset All'>"
|
||||
"</form>";
|
||||
|
||||
// class definition
|
||||
class MyESP {
|
||||
public:
|
||||
MyESP();
|
||||
~MyESP();
|
||||
|
||||
ESP8266WebServer webServer; // Web server on port 80
|
||||
|
||||
// wifi
|
||||
void setWIFICallback(void (*callback)());
|
||||
void setWIFI(const char * wifi_ssid, const char * wifi_password, wifi_callback_f callback);
|
||||
@@ -224,6 +268,9 @@ class MyESP {
|
||||
void setSettings(fs_callback_f callback, fs_settings_callback_f fs_settings_callback);
|
||||
bool fs_saveConfig();
|
||||
|
||||
// Web
|
||||
void setWeb(web_callback_f callback_web);
|
||||
|
||||
// Crash
|
||||
void crashClear();
|
||||
void crashDump();
|
||||
@@ -241,6 +288,7 @@ class MyESP {
|
||||
bool getHeartbeat();
|
||||
uint32_t getSystemLoadAverage();
|
||||
uint32_t getSystemResetReason();
|
||||
uint8_t getSystemBootStatus();
|
||||
|
||||
private:
|
||||
// mqtt
|
||||
@@ -269,7 +317,6 @@ class MyESP {
|
||||
bool _rtcmem_status;
|
||||
|
||||
// wifi
|
||||
DNSServer dnsServer; // For Access Point (AP) support
|
||||
void _wifiCallback(justwifi_messages_t code, char * parameter);
|
||||
void _wifi_setup();
|
||||
wifi_callback_f _wifi_callback;
|
||||
@@ -314,35 +361,45 @@ class MyESP {
|
||||
fs_settings_callback_f _fs_settings_callback;
|
||||
void _printSetCommands();
|
||||
|
||||
// web
|
||||
web_callback_f _web_callback;
|
||||
|
||||
// general
|
||||
char * _app_hostname;
|
||||
char * _app_name;
|
||||
char * _app_version;
|
||||
char * _boottime;
|
||||
bool _suspendOutput;
|
||||
bool _use_serial;
|
||||
bool _serial;
|
||||
bool _heartbeat;
|
||||
unsigned long _getUptime();
|
||||
String _buildTime();
|
||||
bool _firstInstall;
|
||||
|
||||
// reset reason and rtcmem
|
||||
bool _rtcmemStatus();
|
||||
void _rtcmemInit();
|
||||
void _rtcmemSetup();
|
||||
void _deferredReset(unsigned long delay, uint8_t reason);
|
||||
bool _rtcmemStatus();
|
||||
bool _getRtcmemStatus();
|
||||
|
||||
void _rtcmemInit();
|
||||
void _rtcmemSetup();
|
||||
|
||||
void _deferredReset(unsigned long delay, uint8_t reason);
|
||||
|
||||
uint8_t _getSystemStabilityCounter();
|
||||
void _setSystemStabilityCounter(uint8_t counter);
|
||||
|
||||
void _setSystemResetReason(uint8_t reason);
|
||||
uint8_t _getCustomResetReason();
|
||||
void _setCustomResetReason(uint8_t reason);
|
||||
bool _systemStable;
|
||||
|
||||
// rtcmem and reset reason
|
||||
bool _getRtcmemStatus();
|
||||
uint8_t _getSystemResetReason();
|
||||
bool getSystemCheck();
|
||||
void _systemCheckLoop();
|
||||
void _setSystemCheck(bool stable);
|
||||
|
||||
void _setSystemBootStatus(uint8_t status);
|
||||
|
||||
bool _systemStable;
|
||||
void _bootupSequence();
|
||||
bool _getSystemCheck();
|
||||
void _systemCheckLoop();
|
||||
void _setSystemCheck(bool stable);
|
||||
|
||||
// load average (0..100) and heap ram
|
||||
void _calculateLoad();
|
||||
@@ -352,6 +409,12 @@ class MyESP {
|
||||
|
||||
// heartbeat
|
||||
void _heartbeatCheck(bool force);
|
||||
|
||||
// webserver
|
||||
void _webserver_setup();
|
||||
void _webRootPage();
|
||||
void _webResetPage();
|
||||
void _webResetAllPage();
|
||||
};
|
||||
|
||||
extern MyESP myESP;
|
||||
|
||||
@@ -573,6 +573,7 @@ void TelnetSpy::handle() {
|
||||
return;
|
||||
}
|
||||
if (!listening) {
|
||||
|
||||
if ((WiFi.status() == WL_DISCONNECTED) && (WiFi.getMode() & WIFI_AP)) {
|
||||
if (usedSer) {
|
||||
usedSer->println("[TELNET] in AP mode"); // added by Proddy
|
||||
|
||||
@@ -41,7 +41,7 @@ DS18 ds18;
|
||||
Ticker publishValuesTimer;
|
||||
Ticker publishSensorValuesTimer;
|
||||
|
||||
#define SYSTEMCHECK_TIME 10 // every 10 seconds check if EMS can be reached
|
||||
#define SYSTEMCHECK_TIME 30 // every 30 seconds check if EMS can be reached
|
||||
Ticker systemCheckTimer;
|
||||
|
||||
#define REGULARUPDATES_TIME 60 // every minute a call is made to fetch data from EMS devices manually
|
||||
@@ -888,15 +888,15 @@ void do_publishSensorValues() {
|
||||
// call PublishValues without forcing, so using CRC to see if we really need to publish
|
||||
void do_publishValues() {
|
||||
// don't publish if we're not connected to the EMS bus
|
||||
if ((ems_getBusConnected()) && (!myESP.getUseSerial()) && myESP.isMQTTConnected() && EMSESP_Status.publish_time != 0) {
|
||||
if ((ems_getBusConnected()) && myESP.isMQTTConnected() && EMSESP_Status.publish_time != 0) {
|
||||
publishValues(true); // force publish
|
||||
}
|
||||
}
|
||||
|
||||
// callback to light up the LED, called via Ticker every second
|
||||
// fast way is to use WRITE_PERI_REG(PERIPHS_GPIO_BASEADDR + (state ? 4 : 8), (1 << EMSESP_Status.led_gpio)); // 4 is on, 8 is off
|
||||
// when ESP is booting up, ignore this as the LED is being used for something else
|
||||
void do_ledcheck() {
|
||||
if (EMSESP_Status.led) {
|
||||
if ((EMSESP_Status.led) && (myESP.getSystemBootStatus() == MYESP_BOOTSTATUS_BOOTED)) {
|
||||
if (ems_getBusConnected()) {
|
||||
digitalWrite(EMSESP_Status.led_gpio, (EMSESP_Status.led_gpio == LED_BUILTIN) ? LOW : HIGH); // light on. For onboard LED high=off
|
||||
} else {
|
||||
@@ -908,7 +908,7 @@ void do_ledcheck() {
|
||||
|
||||
// Thermostat scan
|
||||
void do_scanThermostat() {
|
||||
if ((ems_getBusConnected()) && (!myESP.getUseSerial())) {
|
||||
if (ems_getBusConnected()) {
|
||||
myDebug_P(PSTR("> Scanning thermostat message type #0x%02X..."), scanThermostat_count);
|
||||
ems_doReadCommand(scanThermostat_count, EMS_Thermostat.device_id);
|
||||
scanThermostat_count++;
|
||||
@@ -917,7 +917,7 @@ void do_scanThermostat() {
|
||||
|
||||
// do a system health check every now and then to see if we all connections
|
||||
void do_systemCheck() {
|
||||
if ((!ems_getBusConnected()) && (!myESP.getUseSerial())) {
|
||||
if (!ems_getBusConnected()) {
|
||||
myDebug_P(PSTR("Error! Unable to read the EMS bus."));
|
||||
}
|
||||
}
|
||||
@@ -925,7 +925,7 @@ void do_systemCheck() {
|
||||
// force calls to get data from EMS for the types that aren't sent as broadcasts
|
||||
// only if we have a EMS connection
|
||||
void do_regularUpdates() {
|
||||
if ((ems_getBusConnected()) && (!myESP.getUseSerial())) {
|
||||
if (ems_getBusConnected()) {
|
||||
myDebugLog("Requesting scheduled EMS device data");
|
||||
ems_getThermostatValues();
|
||||
ems_getBoilerValues();
|
||||
@@ -954,7 +954,7 @@ void do_scanDevices() {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((ems_getBusConnected()) && (!myESP.getUseSerial())) {
|
||||
if (ems_getBusConnected()) {
|
||||
ems_doReadCommand(EMS_TYPE_Version, scanDevices_count++); // ask for version
|
||||
}
|
||||
}
|
||||
@@ -1526,6 +1526,40 @@ void MQTTCallback(unsigned int type, const char * topic, const char * message) {
|
||||
}
|
||||
}
|
||||
|
||||
// web information for diagnostics
|
||||
void WebCallback(char * body) {
|
||||
strlcpy(body, "<b>EMS stats:</b><br>", MYESP_MAXCHARBUFFER);
|
||||
|
||||
if (ems_getBusConnected()) {
|
||||
char s[10];
|
||||
strlcat(body, "EMS Bus is connected<br>", MYESP_MAXCHARBUFFER);
|
||||
strlcat(body, "Rx: # successful read requests=", MYESP_MAXCHARBUFFER);
|
||||
strlcat(body, itoa(EMS_Sys_Status.emsRxPgks, s, 10), MYESP_MAXCHARBUFFER);
|
||||
strlcat(body, ", # CRC errors=", MYESP_MAXCHARBUFFER);
|
||||
strlcat(body, itoa(EMS_Sys_Status.emxCrcErr, s, 10), MYESP_MAXCHARBUFFER);
|
||||
if (ems_getTxCapable()) {
|
||||
strlcat(body, "<br>Tx: # successful write requests=", MYESP_MAXCHARBUFFER);
|
||||
strlcat(body, itoa(EMS_Sys_Status.emsTxPkgs, s, 10), MYESP_MAXCHARBUFFER);
|
||||
} else {
|
||||
strlcat(body, "<br>Tx: no signal<br><br>", MYESP_MAXCHARBUFFER);
|
||||
}
|
||||
|
||||
// show device list
|
||||
strlcpy(body, "<b>EMS devices found:</b><br>", MYESP_MAXCHARBUFFER);
|
||||
|
||||
char buffer[MYESP_MAXCHARBUFFER] = {0};
|
||||
uint8_t num_devices = ems_printDevices_s(buffer, MYESP_MAXCHARBUFFER);
|
||||
if (num_devices == 0) {
|
||||
strlcat(body, "no compatible EMS devices detected yet. (wait a few seconds)", MYESP_MAXCHARBUFFER);
|
||||
} else {
|
||||
strlcat(body, buffer, MYESP_MAXCHARBUFFER);
|
||||
}
|
||||
|
||||
} else {
|
||||
strlcat(body, "Unable to establish a connection to the EMS Bus.", MYESP_MAXCHARBUFFER);
|
||||
}
|
||||
}
|
||||
|
||||
// Init callback, which is used to set functions and call methods after a wifi connection has been established
|
||||
void WIFICallback() {
|
||||
// This is where we enable the UART service to scan the incoming serial Tx/Rx bus signals
|
||||
@@ -1641,11 +1675,11 @@ void setup() {
|
||||
// call ems.cpp's init function to set all the internal params
|
||||
ems_init();
|
||||
|
||||
systemCheckTimer.attach(SYSTEMCHECK_TIME, do_systemCheck); // check if Boiler is online
|
||||
systemCheckTimer.attach(SYSTEMCHECK_TIME, do_systemCheck); // check if EMS is reachable
|
||||
|
||||
// set up myESP for Wifi, MQTT, MDNS and Telnet
|
||||
myESP.setTelnet(project_cmds, ArraySize(project_cmds), TelnetCommandCallback, TelnetCallback); // set up Telnet commands
|
||||
myESP.setWIFI(NULL, NULL, WIFICallback);
|
||||
myESP.setWIFI(NULL, NULL, WIFICallback); // empty ssid and password as we take this from the config file
|
||||
|
||||
// MQTT host, username and password taken from the SPIFFS settings
|
||||
myESP.setMQTT(
|
||||
@@ -1657,6 +1691,9 @@ void setup() {
|
||||
// custom settings in SPIFFS
|
||||
myESP.setSettings(FSCallback, SettingsCallback);
|
||||
|
||||
// web custom settings
|
||||
myESP.setWeb(WebCallback);
|
||||
|
||||
// start up all the services
|
||||
myESP.begin(APP_HOSTNAME, APP_NAME, APP_VERSION);
|
||||
|
||||
|
||||
17
src/ems.cpp
17
src/ems.cpp
@@ -2170,6 +2170,23 @@ void ems_printDevices() {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* prints the device list to a string for html parsing
|
||||
*/
|
||||
uint8_t ems_printDevices_s(char * buffer, uint16_t len) {
|
||||
if (Devices.size() == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char s[100];
|
||||
for (std::list<_Generic_Type>::iterator it = Devices.begin(); it != Devices.end(); it++) {
|
||||
sprintf(s, "%s (DeviceID:0x%02X ProductID:%d Version:%s)<br>", (it)->model_string, (it)->device_id, (it)->product_id, (it)->version);
|
||||
strlcat(buffer, s, len);
|
||||
}
|
||||
|
||||
return Devices.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a command to UART Tx to Read from another device
|
||||
* Read commands when sent must respond by the destination (target) immediately (or within 10ms)
|
||||
|
||||
25
src/ems.h
25
src/ems.h
@@ -23,9 +23,9 @@
|
||||
|
||||
#define EMS_PRODUCTID_HEATRONICS 95 // ProductID for a Junkers Heatronic3 device
|
||||
|
||||
#define EMS_PRODUCTID_SM10 73 // ProductID for SM10 solar module
|
||||
#define EMS_PRODUCTID_SM10 73 // ProductID for SM10 solar module
|
||||
#define EMS_PRODUCTID_SM100 163 // ProductID for SM10 solar module
|
||||
#define EMS_PRODUCTID_ISM1 101 // ProductID for SM10 solar module
|
||||
#define EMS_PRODUCTID_ISM1 101 // ProductID for SM10 solar module
|
||||
|
||||
|
||||
#define EMS_MIN_TELEGRAM_LENGTH 6 // minimal length for a validation telegram, including CRC
|
||||
@@ -255,12 +255,12 @@ typedef struct { // UBAParameterWW
|
||||
|
||||
// Other ems devices than solar modules, thermostats and boilers
|
||||
typedef struct {
|
||||
bool HP; // set true if there is a Heat Pump available
|
||||
bool HP; // set true if there is a Heat Pump available
|
||||
uint8_t HPModulation; // heatpump modulation in %
|
||||
uint8_t HPSpeed; // speed 0-100 %
|
||||
uint8_t device_id; // the device ID of the Solar Module / Heat Pump (e.g. 0x30)
|
||||
uint8_t model_id; // Solar Module / Heat Pump model (e.g. 3 > EMS_MODEL_OTHER )
|
||||
uint8_t product_id; // (e.g. 101)
|
||||
uint8_t device_id; // the device ID of the Solar Module / Heat Pump (e.g. 0x30)
|
||||
uint8_t model_id; // Solar Module / Heat Pump model (e.g. 3 > EMS_MODEL_OTHER )
|
||||
uint8_t product_id; // (e.g. 101)
|
||||
char version[10];
|
||||
} _EMS_Other;
|
||||
|
||||
@@ -273,8 +273,8 @@ typedef struct {
|
||||
int16_t EnergyLastHour;
|
||||
int16_t EnergyToday;
|
||||
int16_t EnergyTotal;
|
||||
uint8_t device_id; // the device ID of the Solar Module / Heat Pump (e.g. 0x30)
|
||||
uint8_t model_id; // Solar Module / Heat Pump model (e.g. 3 > EMS_MODEL_OTHER )
|
||||
uint8_t device_id; // the device ID of the Solar Module / Heat Pump (e.g. 0x30)
|
||||
uint8_t model_id; // Solar Module / Heat Pump model (e.g. 3 > EMS_MODEL_OTHER )
|
||||
uint8_t product_id; // (e.g. 101)
|
||||
char version[10];
|
||||
} _EMS_SolarModule;
|
||||
@@ -323,6 +323,7 @@ void ems_sendRawTelegram(char * telegram);
|
||||
void ems_scanDevices();
|
||||
void ems_printAllDevices();
|
||||
void ems_printDevices();
|
||||
uint8_t ems_printDevices_s(char * buffer, uint16_t len);
|
||||
void ems_printTxQueue();
|
||||
void ems_testTelegram(uint8_t test_num);
|
||||
void ems_startupTelegrams();
|
||||
@@ -375,8 +376,8 @@ bool _ems_setModel(uint8_t model_id);
|
||||
void _removeTxQueue();
|
||||
|
||||
// global so can referenced in other classes
|
||||
extern _EMS_Sys_Status EMS_Sys_Status;
|
||||
extern _EMS_Boiler EMS_Boiler;
|
||||
extern _EMS_Thermostat EMS_Thermostat;
|
||||
extern _EMS_Sys_Status EMS_Sys_Status;
|
||||
extern _EMS_Boiler EMS_Boiler;
|
||||
extern _EMS_Thermostat EMS_Thermostat;
|
||||
extern _EMS_SolarModule EMS_SolarModule;
|
||||
extern _EMS_Other EMS_Other;
|
||||
extern _EMS_Other EMS_Other;
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
#pragma once
|
||||
|
||||
#define APP_NAME "EMS-ESP"
|
||||
#define APP_VERSION "1.8.1b2"
|
||||
#define APP_VERSION "1.8.1b4"
|
||||
#define APP_HOSTNAME "ems-esp"
|
||||
|
||||
Reference in New Issue
Block a user