diff --git a/lib/MyESP/MyESP.cpp b/lib/MyESP/MyESP.cpp index e3303ba9e..1e5266b9e 100644 --- a/lib/MyESP/MyESP.cpp +++ b/lib/MyESP/MyESP.cpp @@ -8,9 +8,19 @@ #include "MyESP.h" -#ifdef CRASH EEPROM_Rotate EEPROMr; -#endif + +union system_rtcmem_t { + struct { + uint8_t stability_counter; + uint8_t reset_reason; + uint16_t _reserved_; + } parts; + uint32_t value; +}; + +uint8_t RtcmemSize = (sizeof(RtcmemData) / 4u); +auto Rtcmem = reinterpret_cast(RTCMEM_ADDR); // constructor MyESP::MyESP() { @@ -57,6 +67,9 @@ MyESP::MyESP() { _ota_post_callback = NULL; _suspendOutput = false; + + _rtcmem_status = false; + _systemStable = true; } MyESP::~MyESP() { @@ -361,7 +374,6 @@ void MyESP::setOTA(ota_callback_f OTACallback_pre, ota_callback_f OTACallback_po void MyESP::_OTACallback() { myDebug_P(PSTR("[OTA] Start")); -#ifdef CRASH // If we are not specifically reserving the sectors we are using as // EEPROM in the memory layout then any OTA upgrade will overwrite // all but the last one. @@ -374,7 +386,6 @@ void MyESP::_OTACallback() { // See onError callback below. EEPROMr.rotate(false); EEPROMr.commit(); -#endif if (_ota_pre_callback) { (_ota_pre_callback)(); // call custom function @@ -391,7 +402,11 @@ void MyESP::_ota_setup() { ArduinoOTA.setHostname(_app_hostname); ArduinoOTA.onStart([this]() { _OTACallback(); }); - ArduinoOTA.onEnd([this]() { myDebug_P(PSTR("[OTA] Done, restarting...")); }); + ArduinoOTA.onEnd([this]() { + myDebug_P(PSTR("[OTA] Done, restarting...")); + _deferredReset(100, CUSTOM_RESET_OTA); + }); + ArduinoOTA.onProgress([this](unsigned int progress, unsigned int total) { static unsigned int _progOld; unsigned int _prog = (progress / (total / 100)); @@ -413,10 +428,8 @@ void MyESP::_ota_setup() { else if (error == OTA_END_ERROR) myDebug_P(PSTR("[OTA] End Failed")); -#ifdef CRASH // There's been an error, reenable rotation EEPROMr.rotate(true); -#endif }); } @@ -430,10 +443,8 @@ void MyESP::setBoottime(const char * boottime) { // eeprom void MyESP::_eeprom_setup() { -#ifdef CRASH EEPROMr.size(4); EEPROMr.begin(SPI_FLASH_SEC_SIZE); -#endif } // Set callback of sketch function to process project messages @@ -449,7 +460,6 @@ void MyESP::_telnetConnected() { _consoleShowHelp(); // Show the initial message // show crash dump if just restarted after a fatal crash -#ifdef CRASH uint32_t crash_time; EEPROMr.get(SAVE_CRASH_EEPROM_OFFSET + SAVE_CRASH_CRASH_TIME, crash_time); if ((crash_time != 0) && (crash_time != 0xFFFFFFFF)) { @@ -461,7 +471,6 @@ void MyESP::_telnetConnected() { EEPROMr.commit(); */ } -#endif // call callback if (_telnet_callback) { @@ -505,9 +514,7 @@ void MyESP::_consoleShowHelp() { myDebug_P(PSTR("* Commands:")); myDebug_P(PSTR("* ?=help, CTRL-D/quit=exit telnet session")); myDebug_P(PSTR("* set, system, reboot")); -#ifdef CRASH myDebug_P(PSTR("* crash ")); -#endif // print custom commands if available. Taken from progmem if (_telnetcommand_callback) { @@ -601,6 +608,7 @@ void MyESP::_printSetCommands() { // reset / restart void MyESP::resetESP() { myDebug_P(PSTR("* Reboot ESP...")); + _deferredReset(100, CUSTOM_RESET_TERMINAL); end(); #if defined(ARDUINO_ARCH_ESP32) ESP.restart(); @@ -792,8 +800,7 @@ void MyESP::_telnetCommand(char * commandLine) { } -// crash command -#ifdef CRASH + // crash command if ((strcmp(ptrToCommandName, "crash") == 0) && (wc == 2)) { char * cmd = _telnet_readWord(false); if (strcmp(cmd, "dump") == 0) { @@ -805,7 +812,6 @@ void MyESP::_telnetCommand(char * commandLine) { } return; // don't call custom command line callback } -#endif // call callback function (_telnetcommand_callback)(wc, commandLine); @@ -864,6 +870,137 @@ unsigned long MyESP::_getUptime() { return uptime_seconds; } +// reason code +void MyESP::_rtcmemInit() { + memset((uint32_t *)RTCMEM_ADDR, 0, sizeof(uint32_t) * RTCMEM_BLOCKS); + Rtcmem->magic = RTCMEM_MAGIC; +} + +uint8_t MyESP::_getSystemStabilityCounter() { + system_rtcmem_t data; + data.value = Rtcmem->sys; + return data.parts.stability_counter; +} + +void MyESP::_setSystemStabilityCounter(uint8_t counter) { + system_rtcmem_t data; + data.value = Rtcmem->sys; + data.parts.stability_counter = counter; + Rtcmem->sys = data.value; +} + +uint8_t MyESP::_getSystemResetReason() { + system_rtcmem_t data; + data.value = Rtcmem->sys; + return data.parts.reset_reason; +} + +void MyESP::_setSystemResetReason(uint8_t reason) { + system_rtcmem_t data; + data.value = Rtcmem->sys; + data.parts.reset_reason = reason; + Rtcmem->sys = data.value; +} + +uint32_t MyESP::getSystemResetReason() { + return resetInfo.reason; +} + +void MyESP::_rtcmemSetup() { + _rtcmem_status = _rtcmemStatus(); + if (!_rtcmem_status) { + _rtcmemInit(); + } +} + +void MyESP::_setCustomResetReason(uint8_t reason) { + _setSystemResetReason(reason); +} + +// Treat memory as dirty on cold boot, hardware wdt reset and rst pin +bool MyESP::_rtcmemStatus() { + bool readable; + + switch (getSystemResetReason()) { + case REASON_EXT_SYS_RST: + case REASON_WDT_RST: + case REASON_DEFAULT_RST: + readable = false; + break; + default: + readable = true; + } + + readable = readable and (RTCMEM_MAGIC == Rtcmem->magic); + + return readable; +} + +bool MyESP::rtcmemStatus() { + return _rtcmem_status; +} + +unsigned char MyESP::_getCustomResetReason() { + static unsigned char status = 255; + if (status == 255) { + if (_rtcmemStatus()) + status = _getSystemResetReason(); + if (status > 0) + _setCustomResetReason(0); + if (status > CUSTOM_RESET_MAX) + status = 0; + } + return status; +} + +void MyESP::_deferredReset(unsigned long delaytime, unsigned char reason) { + delay(delaytime); + _setCustomResetReason(reason); +} + +// 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 +// 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) { + uint8_t value = 0; + + if (stable) { + value = 0; // system is ok + // myDebug_P(PSTR("[SYSTEM] System OK\n")); + } else { + if (!rtcmemStatus()) { + _setSystemStabilityCounter(1); + return; + } + + value = _getSystemStabilityCounter(); + + if (++value > SYSTEM_CHECK_MAX) { + _systemStable = false; + value = 0; // system is unstable + myDebug_P(PSTR("[SYSTEM] Warning, system UNSTABLE\n")); + } + } + + _setSystemStabilityCounter(value); +} + +bool MyESP::getSystemCheck() { + return _systemStable; +} + +void MyESP::_systemCheckLoop() { + static bool checked = false; + if (!checked && (millis() > SYSTEM_CHECK_TIME)) { + _setSystemCheck(true); // Flag system as stable + checked = true; + } +} + + // print out ESP system stats // for battery power is ESP.getVcc() void MyESP::showSystemStats() { @@ -892,6 +1029,10 @@ void MyESP::showSystemStats() { myDebug_P(PSTR(" [APP] System Load: %d%%"), getSystemLoadAverage()); + if (!getSystemCheck()) { + myDebug_P(PSTR(" [SYSTEM] Device is in SAFE MODE")); + } + if (isAPmode()) { myDebug_P(PSTR(" [WIFI] Device is in AP mode with SSID %s"), jw.getAPSSID().c_str()); } else { @@ -902,7 +1043,6 @@ void MyESP::showSystemStats() { myDebug_P(PSTR(" [WIFI] WiFi MAC: %s"), WiFi.macAddress().c_str()); -#ifdef CRASH char output_str[80] = {0}; char buffer[16] = {0}; myDebug_P(PSTR(" [EEPROM] EEPROM size: %u"), EEPROMr.reserved() * SPI_FLASH_SEC_SIZE); @@ -914,7 +1054,6 @@ void MyESP::showSystemStats() { strlcat(output_str, " ", sizeof(output_str)); } myDebug(output_str); -#endif #ifdef ARDUINO_BOARD myDebug_P(PSTR(" [SYSTEM] Board: %s"), ARDUINO_BOARD); @@ -929,6 +1068,21 @@ void MyESP::showSystemStats() { myDebug_P(PSTR(" [SYSTEM] Boot version: %d"), ESP.getBootVersion()); myDebug_P(PSTR(" [SYSTEM] Boot mode: %d"), ESP.getBootMode()); //myDebug_P(PSTR("[SYSTEM] Firmware MD5: %s"), (char *)ESP.getSketchMD5().c_str()); + unsigned char reason = _getCustomResetReason(); + if (reason > 0) { + char buffer[32]; + strcpy_P(buffer, custom_reset_string[reason - 1]); + myDebug_P(PSTR(" [SYSTEM] Last reset reason: %s"), buffer); + } else { + myDebug_P(PSTR(" [SYSTEM] Last reset reason: %s"), (char *)ESP.getResetReason().c_str()); + myDebug_P(PSTR(" [SYSTEM] Last reset info: %s"), (char *)ESP.getResetInfo().c_str()); + } + myDebug_P(PSTR(" [SYSTEM] Restart count: %d"), _getSystemStabilityCounter()); + + myDebug_P(PSTR(" [SYSTEM] rtcmem status:%u blocks:%u addr:0x%p"), _rtcmemStatus(), RtcmemSize, Rtcmem); + for (uint8_t block = 0; block < RtcmemSize; ++block) { + myDebug_P(PSTR(" [SYSTEM] rtcmem %02u: %u"), block, reinterpret_cast(RTCMEM_ADDR)[block]); + } #endif FlashMode_t mode = ESP.getFlashChipMode(); @@ -942,10 +1096,10 @@ void MyESP::showSystemStats() { #endif myDebug_P(PSTR(" [FLASH] Flash size (SDK): %d"), ESP.getFlashChipSize()); myDebug_P(PSTR(" [FLASH] Flash Reserved: %d"), 1 * SPI_FLASH_SEC_SIZE); - myDebug_P(PSTR(" [MEM] Firmware size: %d"), ESP.getSketchSize()); + //myDebug_P(PSTR(" [MEM] Firmware size: %d"), ESP.getSketchSize()); // TODO: commented out because it causes a crash with 2.5.2 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] Free Heap: %d"), ESP.getFreeHeap()); + myDebug_P(PSTR(" [MEM] Free Heap: %d"), ESP.getFreeHeap()); myDebug_P(PSTR("")); } @@ -1385,7 +1539,6 @@ int MyESP::getWifiQuality() { return 2 * (dBm + 100); } -#ifdef CRASH /** * Save crash information in EEPROM * This function is called automatically if ESP8266 suffers an exception @@ -1534,14 +1687,6 @@ void MyESP::crashDump() { myDebug_P(PSTR("<< #include @@ -18,13 +18,14 @@ #include #include // https://github.com/xoseperez/justwifi #include // modified from https://github.com/yasheena/telnetspy +#include -#ifdef CRASH #include 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; } -#endif #if defined(ARDUINO_ARCH_ESP32) //#include @@ -86,6 +87,18 @@ void custom_crash_callback(struct rst_info *, uint32_t, uint32_t); #define COLOR_BRIGHT_CYAN "\x1B[0;96m" #define COLOR_BRIGHT_WHITE "\x1B[0;97m" +// reset reason codes +PROGMEM const char custom_reset_hardware[] = "Hardware button"; +PROGMEM const char custom_reset_terminal[] = "Reboot from terminal"; +PROGMEM const char custom_reset_mqtt[] = "Reboot from MQTT"; +PROGMEM const char custom_reset_ota[] = "Reboot after successful OTA update"; +PROGMEM const char * const custom_reset_string[] = {custom_reset_hardware, custom_reset_terminal, custom_reset_mqtt, custom_reset_ota}; +#define CUSTOM_RESET_HARDWARE 1 // Reset from hardware button +#define CUSTOM_RESET_TERMINAL 2 // Reset from terminal +#define CUSTOM_RESET_MQTT 3 // Reset via MQTT +#define CUSTOM_RESET_OTA 4 // Reset after successful OTA update +#define CUSTOM_RESET_MAX 4 + // SPIFFS #define SPIFFS_MAXSIZE 800 // https://arduinojson.org/v6/assistant/ @@ -119,6 +132,30 @@ void custom_crash_callback(struct rst_info *, uint32_t, uint32_t); #define SAVE_CRASH_STACK_END 0x1E // 4 bytes #define SAVE_CRASH_STACK_TRACE 0x22 // variable +// Base address of USER RTC memory +// https://github.com/esp8266/esp8266-wiki/wiki/Memory-Map#memmory-mapped-io-registers +#define RTCMEM_ADDR_BASE (0x60001200) + +// RTC memory is accessed using blocks of 4 bytes. +// Blocks 0..63 are reserved by the SDK, 64..192 are available to the user. +// Blocks 64..96 are reserved by the eboot 'struct eboot_command' (128 -> (128 / 4) -> 32): +// https://github.com/esp8266/Arduino/blob/master/bootloaders/eboot/eboot_command.h +#define RTCMEM_OFFSET 32u +#define RTCMEM_ADDR (RTCMEM_ADDR_BASE + (RTCMEM_OFFSET * 4u)) +#define RTCMEM_BLOCKS 96u +#define RTCMEM_MAGIC 0x45535075 + +struct RtcmemData { + uint32_t magic; // RTCMEM_MAGIC + uint32_t sys; // system reset reason (1-4) + uint32_t energy; // store energy count +}; + +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_MAX 5 // After this many crashes on boot + typedef struct { bool set; // is it a set command char key[50]; @@ -202,6 +239,10 @@ class MyESP { int getWifiQuality(); void showSystemStats(); + // rtcmem and reset reason + bool rtcmemStatus(); + uint32_t getSystemResetReason(); + private: // mqtt AsyncMqttClient mqttClient; @@ -226,6 +267,7 @@ class MyESP { char * _mqtt_topic; unsigned long _mqtt_last_connection; bool _mqtt_connecting; + bool _rtcmem_status; // wifi DNSServer dnsServer; // For Access Point (AP) support @@ -283,6 +325,29 @@ class MyESP { unsigned long _getUptime(); String _buildTime(); + // reset reason and rtcmem + bool _rtcmemStatus(); + void _rtcmemInit(); + void _rtcmemSetup(); + + void _deferredReset(unsigned long delay, uint8_t reason); + + uint8_t _getSystemStabilityCounter(); + void _setSystemStabilityCounter(uint8_t counter); + + uint8_t _getSystemResetReason(); + void _setSystemResetReason(uint8_t reason); + + unsigned char _getCustomResetReason(); + void _setCustomResetReason(unsigned char reason); + + bool _systemStable; + + bool getSystemCheck(); + void _systemCheckLoop(); + void _setSystemCheck(bool stable); + + // load average (0..100) void _calculateLoad(); unsigned short int _load_average; diff --git a/platformio.ini-example b/platformio.ini-example index 63c3f546c..08594e61a 100644 --- a/platformio.ini-example +++ b/platformio.ini-example @@ -32,7 +32,7 @@ upload_protocol = espota upload_port = ems-esp.local [env:debug] -build_flags = -g -Wall -Wextra -Werror -Wno-missing-field-initializers -Wno-unused-parameter -Wno-unused-variable -DCRASH -DTESTS ${common.wifi_settings} +build_flags = -g -Wall -Wextra -Werror -Wno-missing-field-initializers -Wno-unused-parameter -Wno-unused-variable -DTESTS -DNO_GLOBAL_EEPROM ${common.wifi_settings} [env:clean] extra_scripts = pre:scripts/clean_fw.py @@ -42,5 +42,5 @@ build_flags = -g -w extra_scripts = pre:scripts/rename_fw.py [env:checkcode] -build_flags = -g -Wall -Wextra -Werror -Wno-missing-field-initializers -Wno-unused-parameter -Wno-unused-variable -DCRASH -DTESTS +build_flags = -g -w extra_scripts = scripts/checkcode.py diff --git a/scripts/stackdmp.txt b/scripts/stackdmp.txt index 4ad1b06cd..a2d78e823 100644 --- a/scripts/stackdmp.txt +++ b/scripts/stackdmp.txt @@ -1,33 +1,43 @@ >>>stack>>> -3ffffdd0: 3ffffe30 3fff43c8 3fff89bc 40211340 -3ffffde0: 3fff89bc 3fff43c8 3fff461c 4022cd7a -3ffffdf0: 3ffffe70 402096cb 3fff461c 4022cd68 -3ffffe00: 3ffffe30 3fff43c8 3fff89bc 402096dc -3ffffe10: 454d5b20 4f205d4d 52204154 72657365 -3ffffe20: 00000000 00642520 4025ce70 40209684 -3ffffe30: 3ffffe70 3ffffe40 00000008 00000001 -3ffffe40: 00000002 3fff4770 3fff43c8 40246b28 -3ffffe50: 3fff89b8 ff000000 00068d0c 4020f650 -3ffffe60: 0000001b 00003340 400205e9 4024f545 -3ffffe70: 4020cd64 3fff4770 3fff43c8 00000001 -3ffffe80: 00000002 3fff4770 3fff43c8 4020a640 -3ffffe90: 45455b20 4d4f5250 4545205d 4d4f5250 -3ffffea0: 63655320 20726f74 6c6f6f70 7a697320 -3ffffeb0: 73692065 202c3420 20646e61 75206e69 -3ffffec0: 61206573 203a6572 39313031 31303120 -3ffffed0: 30312038 31203731 20363130 00000000 -3ffffee0: 36313031 00000000 00000000 00000000 -3ffffef0: 00000000 00000000 ff000007 4020f5ae -3fffff00: 00000000 00000007 3ffe8304 402274e7 -3fffff10: 0000000d 00000001 3fff43c8 3fff46a0 -3fffff20: 3fff8c4c 00000001 3fff43c8 4020c5b9 -3fffff30: 3fffdad0 3fff4630 0000000d 40217c8e -3fffff40: 3fffdad0 3fff4630 3fff461c 3fff46a0 -3fffff50: 0000000d 3fff461c 3fff43c8 4020cb89 -3fffff60: 0000000d 3fff4908 3ffe97ac 3fff48ac -3fffff70: 3fff43c8 3fff29cc 3fff43b8 3fff48ac -3fffff80: 3fffdad0 3fff29cc 3fff43c8 4020cc58 -3fffff90: 3fffdad0 00000000 3fff29cc 40205ccc -3fffffa0: 3fffdad0 00000000 3fff487c 4020ebd4 -3fffffb0: feefeffe feefeffe 3ffe97ac 401006f1 +3ffffd30: 40105346 00000030 0000001e ffffffff +3ffffd40: 40105928 00000000 60000200 04000002 +3ffffd50: 3feffe00 fffffeff 00000000 00000000 +3ffffd60: 00000003 00000000 0000002d 3ffffe50 +3ffffd70: 00000008 00001008 00000000 00000030 +3ffffd80: 34303936 34343139 3ffe3131 40223f98 +3ffffd90: 3fff46dc 00000001 3ffec7b9 40223f98 +3ffffda0: 3fff46dc 0000001c 3fff8b18 3ffffe20 +3ffffdb0: 3fff4a0c 0000073d 0000073d 40211340 +3ffffdc0: 3ffffe20 3fff4488 3fff8afc 40211768 +3ffffdd0: 3fff8afc 3fff4488 3fff46dc 4022d182 +3ffffde0: 3ffffe60 402097ff 3fff46dc 4022d170 +3ffffdf0: 3ffffe20 3fff4488 3fff8afc 40209810 +3ffffe00: 00000000 400042db 616c4620 52206873 +3ffffe10: 40004b31 3ffffe58 00000008 00001000 +3ffffe20: 40105934 3ffffe30 00000008 00000000 +3ffffe30: 40100289 3ffffe50 00000000 3fff482c +3ffffe40: 3fff8c18 ff000000 00001008 4020d3cc +3ffffe50: 00000000 00000000 400205e9 40100250 +3ffffe60: 400201e9 001640ef 3fff4488 00000003 +3ffffe70: 00000002 3fff4834 3fff4488 4020a9b9 +3ffffe80: 45455b20 4d4f5250 4545205d 4d4f5250 +3ffffe90: 63655320 20726f74 6c6f6f70 7a697320 +3ffffea0: 73692065 202c3420 20646e61 75206e69 +3ffffeb0: 61206573 203a6572 39313031 31303120 +3ffffec0: 30312038 31203731 20363130 00000000 +3ffffed0: 6f626552 6620746f 206d6f72 6d726574 +3ffffee0: 6c616e69 4020f900 00000007 40211584 +3ffffef0: 36313031 00000000 00000000 00000000 +3fffff00: 00000000 00000007 3ffe8304 402278ef +3fffff10: 0000000d 00000001 3fff4488 3fff4760 +3fffff20: 3fff800c 00000001 3fff4488 4020c95d +3fffff30: 0000006d 3fff46f0 0000000d 402180b6 +3fffff40: 0000006d 3fff46f0 3fff46dc 3fff4760 +3fffff50: 0000000d 3fff46dc 3fff4488 4020cf39 +3fffff60: 0000000d 3fff49cc 3ffe97b8 3fff4970 +3fffff70: 3fff4488 3fff2a8c 3fff4478 3fff4970 +3fffff80: 3fffdad0 3fff2a8c 3fff4488 4020d008 +3fffff90: 3fffdad0 00000000 3fff2a8c 40205cd4 +3fffffa0: 3fffdad0 00000000 3fff4940 4020f000 +3fffffb0: feefeffe feefeffe 3ffe97b8 401006f1 << is detected * At end of receive we re-enable Rx-INT and send a Tx-BRK in loopback mode. */ - ETS_UART_INTR_DISABLE(); // disable rx interrupt - // clear Rx status register - https://github.com/proddy/EMS-ESP/issues/103#issuecomment-495605798 + // clear Rx status register USC0(EMSUART_UART) |= (1 << UCRXRST); // reset uart rx fifo emsuart_flush_fifos(); @@ -254,9 +253,9 @@ void ICACHE_FLASH_ATTR emsuart_tx_buffer(uint8_t * buf, uint8_t len) { } } - // we got the whole telegram in Rx buffer - // on Rx-BRK (bus collision), we simply enable Rx and leave - // otherwise, we send the final Tx-BRK in loopback and enable Rx-INT. + // we got the whole telegram in the Rx buffer + // on Rx-BRK (bus collision), we simply enable Rx and leave it + // otherwise we send the final Tx-BRK in the loopback and re=enable Rx-INT. // worst case, we'll see an additional Rx-BRK... if (!(USIS(EMSUART_UART) & (1 << UIBD))) { phantomBrk = 1; // tell Rx to expect a phantom BRK @@ -276,7 +275,6 @@ void ICACHE_FLASH_ATTR emsuart_tx_buffer(uint8_t * buf, uint8_t len) { } // w/o flushing the Rx FIFO we will get a trailing \0 from our loopback BRK - // emsuart_flush_fifos(); // flush Rx buffer to be sure ETS_UART_INTR_ENABLE(); // receive anything from FIFO... } } diff --git a/src/version.h b/src/version.h index ca1d2ef86..ff296e1aa 100644 --- a/src/version.h +++ b/src/version.h @@ -6,5 +6,5 @@ #pragma once #define APP_NAME "EMS-ESP" -#define APP_VERSION "1.8.0b11" +#define APP_VERSION "1.8.0b12" #define APP_HOSTNAME "ems-esp"