diff --git a/include/DebugTrace.h b/include/DebugTrace.h new file mode 100644 index 00000000..c8aff58a --- /dev/null +++ b/include/DebugTrace.h @@ -0,0 +1,20 @@ +#pragma once +// +#include "Global.h" + +#define RESTART_DEBUG_INFO +#if defined(RESTART_DEBUG_INFO) && defined(ESP32) && !defined(esp32c3m_4mb) +#define CONFIG_RESTART_DEBUG_STACK_DEPTH 15 +typedef struct { + size_t heap_total; + size_t heap_free; + size_t heap_free_min; + time_t heap_min_time; + uint32_t backtrace[CONFIG_RESTART_DEBUG_STACK_DEPTH]; +} re_restart_debug_t; + +#endif // RESTART_DEBUG_INFO +extern "C" void __real_esp_panic_handler(void*); +void printDebugTrace(); +void sendDebugTraceAndFreeMemory(bool); +void IRAM_ATTR debugUpdate(); diff --git a/platformio.ini b/platformio.ini index 82d089cd..db541216 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,5 +1,5 @@ [platformio] -default_envs = esp8266_4mb +default_envs = esp32_4mb3f data_dir = data_svelte [common_env_data] @@ -185,9 +185,10 @@ lib_deps = ${common_env_data.lib_deps_external} ${env:esp32_4mb_fromitems.lib_deps} build_flags = -Desp32_4mb="esp32_4mb" + -Wl,--wrap=esp_panic_handler framework = arduino board = esp32dev -platform = espressif32 @5.1.1 +platform = espressif32 @6.6.0 monitor_filters = esp32_exception_decoder upload_speed = 921600 monitor_speed = 115200 @@ -206,9 +207,10 @@ lib_deps = ${common_env_data.lib_deps_external} ${env:esp32_4mb3f_fromitems.lib_deps} build_flags = -Desp32_4mb="esp32_4mb" + -Wl,--wrap=esp_panic_handler framework = arduino board = esp32dev -platform = espressif32 @5.1.1 +platform = espressif32 @6.6.0 monitor_filters = esp32_exception_decoder upload_speed = 921600 monitor_speed = 115200 @@ -231,9 +233,10 @@ build_flags = -Desp32cam_4mb="esp32cam_4mb" -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue + -Wl,--wrap=esp_panic_handler framework = arduino board = esp32cam -platform = espressif32 @5.1.1 +platform = espressif32 @6.6.0 monitor_filters = esp32_exception_decoder upload_speed = 921600 monitor_speed = 115200 @@ -251,13 +254,13 @@ extra_scripts = pre:tools/patch32_ws.py lib_deps = ${common_env_data.lib_deps_external} ${env:esp32s2_4mb_fromitems.lib_deps} -build_flags = - -Desp32s2_4mb="esp32s2_4mb" +build_flags = -Desp32s2_4mb="esp32s2_4mb" -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_MODE=0 + -Wl,--wrap=esp_panic_handler framework = arduino board = lolin_s2_mini -platform = espressif32 @6.3.1 +platform = espressif32 @6.6.0 monitor_filters = esp32_exception_decoder upload_speed = 921600 monitor_speed = 115200 @@ -275,11 +278,11 @@ extra_scripts = pre:tools/patch32_ws.py lib_deps = ${common_env_data.lib_deps_external} ${env:esp32c3m_4mb_fromitems.lib_deps} -build_flags = - -Desp32c3m_4mb="esp32c3m_4mb" +build_flags = -Desp32c3m_4mb="esp32c3m_4mb" + -Wl,--wrap=esp_panic_handler framework = arduino board = lolin_c3_mini -platform = espressif32 @6.3.1 +platform = espressif32 @6.6.0 monitor_filters = esp32_exception_decoder upload_speed = 921600 monitor_speed = 115200 @@ -300,10 +303,11 @@ lib_deps = ${env:esp32s3_16mb_fromitems.lib_deps} build_flags = -Desp32s3_16mb="esp32s3_16mb" + -Wl,--wrap=esp_panic_handler framework = arduino board = esp32-s3-devkitc-1 board_build.mcu = esp32s3 -platform = espressif32 @6.3.1 +platform = espressif32 @6.6.0 monitor_filters = esp32_exception_decoder upload_speed = 921600 monitor_speed = 115200 @@ -324,9 +328,10 @@ lib_deps = ${common_env_data.lib_deps_external} ${env:esp32_16mb_fromitems.lib_deps} build_flags = -Desp32_16mb="esp32_16mb" + -Wl,--wrap=esp_panic_handler framework = arduino board = esp32dev -platform = espressif32 @5.1.1 +platform = espressif32 @6.6.0 monitor_filters = esp32_exception_decoder upload_port = COM11 upload_speed = 921600 diff --git a/src/DebugTrace.cpp b/src/DebugTrace.cpp new file mode 100644 index 00000000..b2183023 --- /dev/null +++ b/src/DebugTrace.cpp @@ -0,0 +1,287 @@ +#include "DebugTrace.h" +#if defined(RESTART_DEBUG_INFO) && defined(ESP32) && !defined(esp32c3m_4mb) +//#ifdef RESTART_DEBUG_INFO +__NOINIT_ATTR static re_restart_debug_t _debug_info; + +#include "esp_debug_helpers.h" +#include "esp_types.h" +#include "esp_attr.h" +#include "esp_err.h" +#include "soc/soc_memory_layout.h" +#include "soc/cpu.h" + +// RU: Размер буфера для конвертации даты и времeни в строку +#define CONFIG_FORMAT_STRFTIME_BUFFER_SIZE 32 +#define CONFIG_FORMAT_STRFTIME_DTS_BUFFER_SIZE 20 // YYYY.MM.DD HH:NN:SS + \n + +// RU: Форматы даты и времени +#define CONFIG_FORMAT_DTS "%d.%m.%Y %H:%M:%S" + +void IRAM_ATTR debugHeapUpdate() +{ + _debug_info.heap_total = heap_caps_get_total_size(MALLOC_CAP_DEFAULT); + _debug_info.heap_free = heap_caps_get_free_size(MALLOC_CAP_DEFAULT); + size_t _new_free_min = heap_caps_get_minimum_free_size(MALLOC_CAP_DEFAULT); + if ((_debug_info.heap_free_min == 0) || (_new_free_min < _debug_info.heap_free_min)) + { + _debug_info.heap_free_min = _new_free_min; + _debug_info.heap_min_time = time(nullptr); + }; +} + +void IRAM_ATTR debugBacktraceUpdate() +{ + esp_backtrace_frame_t stk_frame; + esp_backtrace_get_start(&(stk_frame.pc), &(stk_frame.sp), &(stk_frame.next_pc)); + _debug_info.backtrace[0] = esp_cpu_process_stack_pc(stk_frame.pc); + + bool corrupted = (esp_stack_ptr_is_sane(stk_frame.sp) && + esp_ptr_executable((void *)esp_cpu_process_stack_pc(stk_frame.pc))) + ? false + : true; + + uint8_t i = CONFIG_RESTART_DEBUG_STACK_DEPTH; + while (i-- > 0 && stk_frame.next_pc != 0 && !corrupted) + { + if (!esp_backtrace_get_next_frame(&stk_frame)) + { + corrupted = true; + }; + _debug_info.backtrace[CONFIG_RESTART_DEBUG_STACK_DEPTH - i] = esp_cpu_process_stack_pc(stk_frame.pc); + }; +} + +void IRAM_ATTR debugUpdate() +{ + debugHeapUpdate(); + debugBacktraceUpdate(); +} + +extern "C" void __wrap_esp_panic_handler(void *info) +{ + + debugHeapUpdate(); + debugBacktraceUpdate(); + // Call the original panic handler function to finish processing this error (creating a core dump for example...) + __real_esp_panic_handler(info); +} + +re_restart_debug_t debugGet() +{ + re_restart_debug_t ret; + memset(&ret, 0, sizeof(re_restart_debug_t)); + esp_reset_reason_t esp_reason = esp_reset_reason(); + if ((esp_reason != ESP_RST_UNKNOWN) && (esp_reason != ESP_RST_POWERON)) + { + uint8_t i = CONFIG_RESTART_DEBUG_STACK_DEPTH; + ret = _debug_info; + if (_debug_info.heap_total > heap_caps_get_total_size(MALLOC_CAP_DEFAULT)) + { + memset(&ret, 0, sizeof(re_restart_debug_t)); + }; + }; + memset(&_debug_info, 0, sizeof(re_restart_debug_t)); + return ret; +} + +#define CONFIG_MESSAGE_TG_VERSION_DEF "! Устройство запущено\n\nИмя устройства: %s\nПричина перезапуска: %s\nCPU0: %s\nCPU1: %s" +#define CONFIG_MESSAGE_TG_VERSION_HEAP "! Устройство аварийно перезапущено !\n\nИмя устройства: %s\nПричина перезапуска: %s\nCPU0: %s\nCPU1: %s\nHEAP: %s" +#define CONFIG_MESSAGE_TG_VERSION_TRACE "! Устройство аварийно перезапущено !\n\nИмя устройства: %s\nПричина перезапуска: %s\nCPU0: %s\nCPU1: %s\nHEAP: %s\nTRACE: %s" + +char *malloc_stringf(const char *format, ...) +{ + char *ret = nullptr; + if (format != nullptr) + { + // get the list of arguments + va_list args1, args2; + va_start(args1, format); + va_copy(args2, args1); + // calculate length of resulting string + int len = vsnprintf(nullptr, 0, format, args1); + va_end(args1); + // allocate memory for string + if (len > 0) + { +#if USE_ESP_MALLOC + ret = (char *)esp_malloc(len + 1); +#else + ret = (char *)malloc(len + 1); +#endif + if (ret != nullptr) + { + memset(ret, 0, len + 1); + vsnprintf(ret, len + 1, format, args2); + } + else + { + // rlog_e(tagHEAP, "Failed to format string: out of memory!"); + }; + }; + va_end(args2); + }; + return ret; +} + +static char *statesGetDebugHeap(re_restart_debug_t *debug) +{ + if ((debug->heap_total > 0) && (debug->heap_total > debug->heap_free)) + { + struct tm timeinfo; + localtime_r(&debug->heap_min_time, &timeinfo); + char time_buffer[CONFIG_FORMAT_STRFTIME_DTS_BUFFER_SIZE]; + memset(&time_buffer, 0, CONFIG_FORMAT_STRFTIME_DTS_BUFFER_SIZE); + strftime(time_buffer, CONFIG_FORMAT_STRFTIME_DTS_BUFFER_SIZE, CONFIG_FORMAT_DTS, &timeinfo); + + double heapTotal = (double)debug->heap_total / 1024; + double heapFree = (double)debug->heap_free / 1024; + double heapFreeMin = (double)debug->heap_free_min / 1024; + + return malloc_stringf("Total %.1fkB ; Free %.1fkB (%.1f%%) ; FreeMin %.1fkB (%.1f%%) %s", + heapTotal, + heapFree, 100.0 * (heapFree / heapTotal), + heapFreeMin, 100.0 * (heapFreeMin / heapTotal), time_buffer); + }; + return nullptr; +} + +static char *statesGetDebugTrace(re_restart_debug_t *debug) +{ + char *backtrace = nullptr; + char *item = nullptr; + char *temp = nullptr; + for (uint8_t i = 0; i < CONFIG_RESTART_DEBUG_STACK_DEPTH; i++) + { + if (debug->backtrace[i] != 0) + { + item = malloc_stringf("0x%08x", debug->backtrace[i]); + if (item) + { + if (backtrace) + { + temp = backtrace; + backtrace = malloc_stringf("%s %s", temp, item); + free(item); + free(temp); + } + else + { + backtrace = item; + }; + item = nullptr; + }; + } + else + { + break; + } + }; + return backtrace; +} + +void printDebugTrace() +{ + // esp_register_shutdown_handler(debugUpdate); + re_restart_debug_t debug = debugGet(); + char *debug_heap = statesGetDebugHeap(&debug); + char *debug_trace = nullptr; + if (debug_heap) + { + debug_trace = statesGetDebugTrace(&debug); + if (debug_trace) + { + Serial.printf(CONFIG_MESSAGE_TG_VERSION_TRACE, + jsonReadStr(settingsFlashJson, F("name")), ESP_getResetReason().c_str(), ESP32GetResetReason(0).c_str(), ESP32GetResetReason(1).c_str(), + debug_heap, debug_trace); + // free(debug_trace); + } + else + { + Serial.printf(CONFIG_MESSAGE_TG_VERSION_HEAP, + jsonReadStr(settingsFlashJson, F("name")), ESP_getResetReason().c_str(), ESP32GetResetReason(0).c_str(), ESP32GetResetReason(1).c_str(), + debug_heap); + }; + // free(debug_heap); + } + else + { + // Serial.println("DEVICE START"); + Serial.printf(CONFIG_MESSAGE_TG_VERSION_DEF, + jsonReadStr(settingsFlashJson, F("name")), ESP_getResetReason().c_str(), ESP32GetResetReason(0).c_str(), ESP32GetResetReason(1).c_str()); + } + +} + +void sendDebugTraceAndFreeMemory( bool postMsg) +{ + // esp_register_shutdown_handler(debugUpdate); + re_restart_debug_t debug = debugGet(); + char *debug_heap = statesGetDebugHeap(&debug); + char *debug_trace = nullptr; + + if (debug_heap) + { + if (isNetworkActive() && postMsg) + { + debug_trace = statesGetDebugTrace(&debug); + if (debug_trace) + { + if (tlgrmItem) + { + char *msg; + msg = malloc_stringf(CONFIG_MESSAGE_TG_VERSION_TRACE, + jsonReadStr(settingsFlashJson, F("name")).c_str(), ESP_getResetReason().c_str(), ESP32GetResetReason(0).c_str(), ESP32GetResetReason(1).c_str(), + debug_heap, debug_trace); + tlgrmItem->sendTelegramMsg(false, String(msg)); + tlgrmItem->sendTelegramMsg(false, String("Подробности /helpDebug в Telegram_v2")); + free(msg); + } + free(debug_trace); + } + else + { + /* + Serial.printf(CONFIG_MESSAGE_TG_VERSION_HEAP, + jsonReadStr(settingsFlashJson, F("name")), ESP_getResetReason().c_str(), ESP32GetResetReason(0).c_str(), ESP32GetResetReason(1).c_str(), + debug_heap); + */ + if (tlgrmItem) + { + char *msg; + msg = malloc_stringf(CONFIG_MESSAGE_TG_VERSION_HEAP, + jsonReadStr(settingsFlashJson, F("name")).c_str(), ESP_getResetReason().c_str(), ESP32GetResetReason(0).c_str(), ESP32GetResetReason(1).c_str(), + debug_heap); + tlgrmItem->sendTelegramMsg(false, String(msg)); + tlgrmItem->sendTelegramMsg(false, String("Подробности /helpDebug в Telegram_v2")); + free(msg); + } + } + } + free(debug_heap); + } +/* else + { + // Serial.println("DEVICE START"); + // Serial.printf(CONFIG_MESSAGE_TG_VERSION_DEF, + // FIRMWARE_VERSION, ESP_getResetReason().c_str(), ESP32GetResetReason(0).c_str(), ESP32GetResetReason(1).c_str()); + if (tlgrmItem && isNetworkActive()) + { + char *msg; + msg = malloc_stringf(CONFIG_MESSAGE_TG_VERSION_DEF, + WiFi.localIP().toString(), FIRMWARE_VERSION, ESP_getResetReason().c_str(), ESP32GetResetReason(0).c_str(), ESP32GetResetReason(1).c_str()); + tlgrmItem->sendTelegramMsg(false, String(msg)); + free(msg); + } + };*/ +} + +#else +void printDebugTrace() {} +void sendDebugTraceAndFreeMemory(bool) {} +void IRAM_ATTR debugUpdate() {} +extern "C" void __wrap_esp_panic_handler(void *info) +{ + // Call the original panic handler function to finish processing this error (creating a core dump for example...) + __real_esp_panic_handler(info); +} +#endif // RESTART_DEBUG_INFO diff --git a/src/EspFileSystem.cpp b/src/EspFileSystem.cpp index fc4892c5..5b9bbf2c 100644 --- a/src/EspFileSystem.cpp +++ b/src/EspFileSystem.cpp @@ -79,7 +79,7 @@ uint32_t ESP_getChipId(void) #endif } -// устарела используем новую функцию ниже +/*// устарела используем новую функцию ниже #if !defined(esp32s2_4mb) && !defined(esp32c3m_4mb) && !defined(esp32s3_16mb) //#ifndef esp32s2_4mb uint32_t ESP_getFlashChipId(void) @@ -93,6 +93,7 @@ uint32_t ESP_getFlashChipId(void) #endif } #endif +*/ // https://github.com/espressif/arduino-esp32/issues/6945#issuecomment-1199900892 // получение flash ch id из проекта esp easy diff --git a/src/Main.cpp b/src/Main.cpp index 7f338d53..c0e60c27 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -4,6 +4,7 @@ #include "utils/Statistic.h" #include "classes/IoTBench.h" #include +#include "DebugTrace.h" #if defined(esp32s2_4mb) || defined(esp32s3_16mb) #include #endif @@ -97,6 +98,12 @@ void setup() { Serial.begin(115200); Serial.flush(); + //----------- Отладка EXCEPTION (функции с заглушками для отключения) --------- + //Привязка коллбэк функции для вызова при перезагрузке + esp_register_shutdown_handler(debugUpdate); + // Печать или оправка отладочной информации + printDebugTrace(); + Serial.println(); Serial.println(F("--------------started----------------")); @@ -179,6 +186,10 @@ void setup() { stopErrorMarker(SETUPINET_ERRORMARKER); + bool postMsgTelegram; + if (!jsonRead(settingsFlashJson, "debugTrace", postMsgTelegram, false)) postMsgTelegram = 1; + sendDebugTraceAndFreeMemory(postMsgTelegram); + initErrorMarker(SETUPLAST_ERRORMARKER); elementsLoop(); diff --git a/src/PeriodicTasks.cpp b/src/PeriodicTasks.cpp index f9fa9c9d..c82d49ce 100644 --- a/src/PeriodicTasks.cpp +++ b/src/PeriodicTasks.cpp @@ -61,7 +61,22 @@ String ESP_getResetReason(void) { #endif #if defined(esp32s2_4mb) || defined(esp32s3_16mb) || defined(esp32c3m_4mb) String ESP_getResetReason(void) { - return ESP32GetResetReason(0); // CPU 0 + // return ESP32GetResetReason(0); // CPU 0 + esp_reset_reason_t esp_reason = esp_reset_reason(); + switch (esp_reason) { + case ESP_RST_UNKNOWN: return "UNKNOWN"; + case ESP_RST_POWERON: return "POWER ON"; + case ESP_RST_EXT: return "EXTERNAL PIN"; + case ESP_RST_SW: return "SOFTWARE RESET"; + case ESP_RST_PANIC: return "EXCEPTION / PANIC"; + case ESP_RST_INT_WDT: return "INTERRUPT WATCHDOG"; + case ESP_RST_TASK_WDT: return "TASK WATCHDOG"; + case ESP_RST_WDT: return "WATCHDOGS"; + case ESP_RST_DEEPSLEEP: return "EXITING DEEP SLLEP MODE"; + case ESP_RST_BROWNOUT: return "BROWNOUT"; + case ESP_RST_SDIO: return "SDIO"; + default : return "NO MEAN"; + }; } String ESP32GetResetReason(uint32_t cpu_no) { // tools\sdk\include\esp32\rom\rtc.h @@ -103,7 +118,22 @@ String ESP32GetResetReason(uint32_t cpu_no) { #endif #if defined(esp32_4mb) || defined(esp32_16mb) || defined(esp32cam_4mb) String ESP_getResetReason(void) { - return ESP32GetResetReason(0); // CPU 0 + // return ESP32GetResetReason(0); // CPU 0 + esp_reset_reason_t esp_reason = esp_reset_reason(); + switch (esp_reason) { + case ESP_RST_UNKNOWN: return "UNKNOWN"; + case ESP_RST_POWERON: return "POWER ON"; + case ESP_RST_EXT: return "EXTERNAL PIN"; + case ESP_RST_SW: return "SOFTWARE RESET"; + case ESP_RST_PANIC: return "EXCEPTION / PANIC"; + case ESP_RST_INT_WDT: return "INTERRUPT WATCHDOG"; + case ESP_RST_TASK_WDT: return "TASK WATCHDOG"; + case ESP_RST_WDT: return "WATCHDOGS"; + case ESP_RST_DEEPSLEEP: return "EXITING DEEP SLLEP MODE"; + case ESP_RST_BROWNOUT: return "BROWNOUT"; + case ESP_RST_SDIO: return "SDIO"; + default : return "NO MEAN"; + }; } String ESP32GetResetReason(uint32_t cpu_no) { // tools\sdk\include\esp32\rom\rtc.h diff --git a/src/modules/display/Nextion/ESPNexUpload.cpp b/src/modules/display/Nextion/ESPNexUpload.cpp index 1eaf5403..c0927713 100644 --- a/src/modules/display/Nextion/ESPNexUpload.cpp +++ b/src/modules/display/Nextion/ESPNexUpload.cpp @@ -22,66 +22,22 @@ * along with this program. If not, see . * */ +#ifdef CORE_DEBUG_LEVEL +#undef CORE_DEBUG_LEVEL +#endif + +#define CORE_DEBUG_LEVEL 3 +#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG -#define DEBUG_SERIAL_ENABLE #include "ESPNexUpload.h" -#ifdef DEBUG_SERIAL_ENABLE -#define dbSerialPrint(a) Serial.print(a) -#define dbSerialPrintHex(a) Serial.print(a, HEX) -#define dbSerialPrintln(a) Serial.println(a) -#define dbSerialBegin(a) Serial.begin(a) -#else -#define dbSerialPrint(a) \ - do \ - { \ - } while (0) -#define dbSerialPrintHex(a) \ - do \ - { \ - } while (0) -#define dbSerialPrintln(a) \ - do \ - { \ - } while (0) -#define dbSerialBegin(a) \ - do \ - { \ - } while (0) -#endif +static const char *TAG = "nextion upload"; -ESPNexUpload::ESPNexUpload(uint32_t upload_baudrate, int line, int rx, int tx) +ESPNexUpload::ESPNexUpload(uart_port_t uart_num, uint32_t baud_rate, gpio_num_t tx_io_num, gpio_num_t rx_io_num) { - _upload_baudrate = upload_baudrate; - _rx = rx; - _tx = tx; - _line = line; - -#if defined ESP8266 - nexSerial = new SoftwareSerial(_rx, _tx); -#else - if (line >= 0) { - nexSerial = new HardwareSerial(line); - // ((HardwareSerial*)nexSerial)->begin(_upload_baudrate, SERIAL_8N1, _rx, _tx); - } else { - nexSerial = new SoftwareSerial(_rx, _tx); - // ((SoftwareSerial*)nexSerial)->begin(_upload_baudrate); - } -#endif - -} - -void ESPNexUpload::nexSerialBegin(uint32_t _speed, int _line, int _rx, int _tx) -{ -#if defined ESP8266 - nexSerial->begin(_speed); -#else - if (_line >= 0) { - ((HardwareSerial*)nexSerial)->begin(_speed, SERIAL_8N1, _rx, _tx); - } else { - ((SoftwareSerial*)nexSerial)->begin(_speed); - } -#endif + _upload_uart_lock = xSemaphoreCreateMutex(); + _uart_diver_installed = false; + setBaudrate(uart_num, baud_rate, tx_io_num, rx_io_num); } bool ESPNexUpload::connect() @@ -90,13 +46,11 @@ bool ESPNexUpload::connect() yield(); #endif - dbSerialBegin(115200); - _printInfoLine(F("serial tests & connect")); + ESP_LOGI(TAG, "serial tests & connect"); if (_getBaudrate() == 0) { - statusMessage = F("get baudrate error"); - _printInfoLine(statusMessage); + ESP_LOGE(TAG, "get baudrate error"); return false; } @@ -104,31 +58,31 @@ bool ESPNexUpload::connect() if (!_echoTest("mystop_yesABC")) { - statusMessage = F("echo test failed"); - _printInfoLine(statusMessage); + ESP_LOGE(TAG, "echo test failed"); return false; } if (!_handlingSleepAndDim()) { - statusMessage = F("handling sleep and dim settings failed"); - _printInfoLine(statusMessage); + ESP_LOGE(TAG, "handling sleep and dim settings failed"); return false; } if (!_setPrepareForFirmwareUpdate(_upload_baudrate)) { - statusMessage = F("modifybaudrate error"); - _printInfoLine(statusMessage); + ESP_LOGE(TAG, "modifybaudrate error"); return false; } return true; } -bool ESPNexUpload::prepareUpload(uint32_t file_size) +bool ESPNexUpload::prepareUpload(uint32_t file_size, bool prot) { + protv2 = prot; _undownloadByte = file_size; + ESP_LOGD(TAG, "prepareUpload: %" PRIu32, file_size); + vTaskDelay(5 / portTICK_PERIOD_MS); return this->connect(); } @@ -142,74 +96,73 @@ uint16_t ESPNexUpload::_getBaudrate(void) if (_searchBaudrate(baudrate_array[i])) { _baudrate = baudrate_array[i]; - _printInfoLine(F("baudrate determined")); + ESP_LOGI(TAG, "baudrate determined: %i", _baudrate); break; } - delay(1500); // wait for 1500 ms + vTaskDelay(1500 / portTICK_PERIOD_MS); // wait for 1500 ms } return _baudrate; } -bool ESPNexUpload::_searchBaudrate(uint32_t baudrate) +bool ESPNexUpload::_searchBaudrate(int baudrate) { #if defined ESP8266 yield(); #endif - String response = String(""); - _printInfoLine(); - dbSerialPrint(F("init nextion serial interface on baudrate: ")); - dbSerialPrintln(baudrate); + std::string response = ""; + ESP_LOGD(TAG, "init nextion serial interface on baudrate: %i", baudrate); - nexSerialBegin(baudrate, _line, _rx, _tx); - _printInfoLine(F("ESP baudrate established, try to connect to display")); + setBaudrate(_upload_uart_num, + baudrate, + _upload_tx_io_num, + _upload_rx_io_num); + + ESP_LOGD(TAG, "ESP baudrate established, try to connect to display"); const char _nextion_FF_FF[3] = {0xFF, 0xFF, 0x00}; - this->sendCommand("DRAKJHSUYDGBNCJHGJKSHBDN"); - this->sendCommand("", true, true); // 0x00 0xFF 0xFF 0xFF - + this->sendCommand("DRAKJHSUYDGBNCJHGJKSHBDN", true, true); // 0x00 0xFF 0xFF 0xFF this->recvRetString(response); if (response[0] != 0x1A) { - _printInfoLine(F("first indication that baudrate is wrong")); + ESP_LOGW(TAG, "first indication that baudrate is wrong"); } else { - _printInfoLine(F("first respone from display, first indication that baudrate is correct")); + ESP_LOGI(TAG, "first respone from display, first indication that baudrate is correct"); } this->sendCommand("connect"); // first connect attempt - this->recvRetString(response); - if (response.indexOf(F("comok")) == -1) + if (response.find("comok") == -1) { - _printInfoLine(F("display doesn't accept the first connect request")); + ESP_LOGW(TAG, "display doesn't accept the first connect request"); } else { - _printInfoLine(F("display accept the first connect request")); + ESP_LOGI(TAG, "display accept the first connect request"); } - response = String(""); - delay(110); // based on serial analyser from Nextion editor V0.58 to Nextion display NX4024T032_011R + vTaskDelay(110 / portTICK_PERIOD_MS); // based on serial analyser from Nextion editor V0.58 to Nextion display NX4024T032_011R this->sendCommand(_nextion_FF_FF, false); this->sendCommand("connect"); // second attempt this->recvRetString(response); - if (response.indexOf(F("comok")) == -1 && response[0] != 0x1A) + + if (response.find("comok") == -1 && response[0] != 0x1A) { - _printInfoLine(F("display doesn't accept the second connect request")); - _printInfoLine(F("conclusion, wrong baudrate")); - return 0; + ESP_LOGW(TAG, "display doesn't accept the second connect request"); + ESP_LOGW(TAG, "conclusion, wrong baudrate"); + return false; } else { - _printInfoLine(F("display accept the second connect request")); - _printInfoLine(F("conclusion, correct baudrate")); + ESP_LOGI(TAG, "display accept the second connect request"); + ESP_LOGI(TAG, "conclusion, correct baudrate"); } - return 1; + return true; } void ESPNexUpload::sendCommand(const char *cmd, bool tail, bool null_head) @@ -221,25 +174,25 @@ void ESPNexUpload::sendCommand(const char *cmd, bool tail, bool null_head) if (null_head) { - ((HardwareSerial*)nexSerial)->write(0x00); + uartWrite(0x00); } - while (nexSerial->available()) + while (uartAvailable()) { - nexSerial->read(); + uartRead(); } - nexSerial->print(cmd); + uartWriteBuf(cmd, strlen(cmd)); + if (tail) { - nexSerial->write(0xFF); - nexSerial->write(0xFF); - nexSerial->write(0xFF); + uartWrite(0xFF); + uartWrite(0xFF); + uartWrite(0xFF); } - _printSerialData(true, cmd); } -uint16_t ESPNexUpload::recvRetString(String &response, uint32_t timeout, bool recv_flag) +uint16_t ESPNexUpload::recvRetString(std::string &response, uint32_t timeout, bool recv_flag) { #if defined ESP8266 @@ -252,18 +205,19 @@ uint16_t ESPNexUpload::recvRetString(String &response, uint32_t timeout, bool re long start; bool exit_flag = false; bool ff_flag = false; + response = ""; if (timeout != 500) - _printInfoLine("timeout setting serial read: " + String(timeout)); + ESP_LOGD(TAG, "timeout setting serial read: %" PRIu32, timeout); - start = millis(); + start = (unsigned long)(esp_timer_get_time() / 1000ULL); - while (millis() - start <= timeout) + while ((unsigned long)(esp_timer_get_time() / 1000ULL) - start <= timeout) { - while (nexSerial->available()) + while (uartAvailable()) { - c = nexSerial->read(); + c = uartRead(); if (c == 0) { continue; @@ -284,7 +238,7 @@ uint16_t ESPNexUpload::recvRetString(String &response, uint32_t timeout, bool re if (recv_flag) { - if (response.indexOf(0x05) != -1) + if (response.find(0x05) != -1) { exit_flag = true; } @@ -295,16 +249,12 @@ uint16_t ESPNexUpload::recvRetString(String &response, uint32_t timeout, bool re break; } } - _printSerialData(false, response); - - // if the exit flag and the ff flag are both not found, than there is a timeout - // if(!exit_flag && !ff_flag) - // _printInfoLine(F("recvRetString: timeout")); if (ff_flag) - response = response.substring(0, response.length() - 3); // Remove last 3 0xFF + response = response.substr(0, response.length() - 3); // Remove last 3 0xFF ret = response.length(); + return ret; } @@ -315,18 +265,19 @@ bool ESPNexUpload::_setPrepareForFirmwareUpdate(uint32_t upload_baudrate) yield(); #endif - String response = String(""); - String cmd = String(""); + std::string response = ""; + std::string cmd = ""; - cmd = F("00"); + cmd = "00"; this->sendCommand(cmd.c_str()); - delay(0.1); - + vTaskDelay(10 / portTICK_PERIOD_MS); this->recvRetString(response, 800, true); // normal response time is 400ms - - String filesize_str = String(_undownloadByte, 10); - String baudrate_str = String(upload_baudrate); - cmd = "whmi-wri " + filesize_str + "," + baudrate_str + ",0"; + ESP_LOGD(TAG, "response (00): %s", response.c_str()); + if (protv2) + cmd = "whmi-wris " + std::to_string(_undownloadByte) + "," + std::to_string(upload_baudrate) + ",1"; + else + cmd = "whmi-wri " + std::to_string(_undownloadByte) + "," + std::to_string(upload_baudrate) + ",0"; + ESP_LOGI(TAG, "cmd: %s", cmd.c_str()); this->sendCommand(cmd.c_str()); @@ -334,136 +285,215 @@ bool ESPNexUpload::_setPrepareForFirmwareUpdate(uint32_t upload_baudrate) // because switching to another baudrate (nexSerialBegin command) has an higher prio. // The ESP will first jump to the new 'upload_baudrate' and than process the serial 'transmit buffer' // The flush command forced the ESP to wait until the 'transmit buffer' is empty - nexSerial->flush(); - - nexSerialBegin(upload_baudrate, _line, _rx, _tx); - _printInfoLine(F("changing upload baudrate...")); - _printInfoLine(String(upload_baudrate)); + // nexSerial.flush(); + uartFlushTxOnly(); this->recvRetString(response, 800, true); // normal response time is 400ms + ESP_LOGD(TAG, "response (01): %s", response.c_str()); + // The Nextion display will, if it's ready to accept data, send a 0x05 byte. - if (response.indexOf(0x05) != -1) + if (response.find(0x05) != -1) { - _printInfoLine(F("preparation for firmware update done")); - return 1; + ESP_LOGI(TAG, "preparation for firmware update done"); + return true; } else { - _printInfoLine(F("preparation for firmware update failed")); - return 0; + ESP_LOGE(TAG, "preparation for firmware update failed"); + return false; } } -void ESPNexUpload::setUpdateProgressCallback(THandlerFunction value) -{ - _updateProgressCallback = value; -} - -bool ESPNexUpload::upload(const uint8_t *file_buf, size_t buf_size) +bool ESPNexUpload::upload(const uint8_t *file_buf, size_t file_size) { #if defined ESP8266 yield(); #endif - uint8_t c; + // uint8_t c; uint8_t timeout = 0; - String string = String(""); + std::string response = ""; + uint8_t sent_bulk_counter = 0; + // uint8_t buff[4096] = {0}; + uint32_t offset = 0; + int remainingBlocks = ceil(file_size / 4096); + int blockSize = 4096; - for (uint16_t i = 0; i < buf_size; i++) + while (remainingBlocks > 0) { - - // Users must split the .tft file contents into 4096 byte sized packets with the final partial packet size equal to the last remaining bytes (<4096 bytes). - if (_sent_packets == 4096) + if (remainingBlocks == 1) { - - // wait for the Nextion to return its 0x05 byte confirming reception and readiness to receive the next packets - this->recvRetString(string, 500, true); - if (string.indexOf(0x05) != -1) - { - - // reset sent packets counter - _sent_packets = 0; - - // reset receive String - string = ""; - } - else - { - if (timeout >= 8) - { - statusMessage = F("serial connection lost"); - _printInfoLine(statusMessage); - return false; - } - - timeout++; - } - - // delay current byte - i--; + blockSize = file_size - offset; } + uartWriteBuf((char*)file_buf[offset], blockSize); + // wait for the Nextion to return its 0x05 byte confirming reception and readiness to receive the next packets + this->recvRetString(response, 2000, true); + + if (response[0] == 0x08 && response.size() == 5) + { // handle partial upload request + remainingBlocks -= 1; + _sent_packets_total += blockSize; + sent_bulk_counter++; + if (sent_bulk_counter % 10 == 0) + { + ESP_LOGI(TAG, "bulk: %i, total bytes %" PRIu32 ", response: %s", sent_bulk_counter, _sent_packets_total, response.c_str()); + } + + ESP_LOGE(TAG, "response [%s]", + format_hex_pretty(reinterpret_cast(response.data()), response.size()).c_str()); + + for (int j = 0; j < 4; ++j) + { + offset += static_cast(response[j + 1]) << (8 * j); + ESP_LOGI(TAG, "Offset : %" PRIu32, offset); + } + if (offset) + remainingBlocks = ceil((file_size - offset) / blockSize); + } + else if (response[0] == 0x05) + { + remainingBlocks -= 1; + _sent_packets_total += blockSize; + sent_bulk_counter++; + if (sent_bulk_counter % 10 == 0) + { + ESP_LOGI(TAG, "bulk: %i, total bytes %" PRIu32 ", response: %s", sent_bulk_counter, _sent_packets_total, response.c_str()); + } + + ESP_LOGE(TAG, "response [%s]", + format_hex_pretty(reinterpret_cast(response.data()), response.size()).c_str()); + offset += 4096; + } + else { - - // read buffer - c = file_buf[i]; - - // write byte to nextion over serial - nexSerial->write(c); - - // update sent packets counter - _sent_packets++; + if (timeout >= 2) + { + ESP_LOGE(TAG, "upload failed, no valid response from display, total bytes send : %" PRIu32, _sent_packets_total); + sent_bulk_counter = 0; + return false; + } + timeout++; } } - + ESP_LOGI(TAG, "upload send last bytes %" PRIu32 ", response: %s", _sent_packets_total, response.c_str()); + // ESP_LOGI(TAG,"upload finished, total bytes send : %"PRIu32, _sent_packets_total); + sent_bulk_counter = 0; return true; } bool ESPNexUpload::upload(Stream &myFile) { + #if defined ESP8266 yield(); #endif - - // create buffer for read - uint8_t buff[4096] = {0}; - - // read all data from server - while (_undownloadByte > 0 || _undownloadByte == -1) + // uint8_t c; + uint8_t timeout = 0; + std::string response = ""; + uint8_t sent_bulk_counter = 0; + uint8_t file_buf[4096] = {0}; + uint32_t offset = 0; + uint32_t _seekByte = 0; + uint32_t _packets_total_byte = 0; + // get available data size + size_t file_size = myFile.available(); + if (file_size) { + int remainingBlocks = ceil(file_size / 4096); + int blockSize = 4096; - // get available data size - size_t size = myFile.available(); - - if (size) + while (remainingBlocks > 0) { - // read up to 2048 byte into the buffer - int c = myFile.readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size)); - - // Write the buffered bytes to the nextion. If this fails, return false. - if (!this->upload(buff, c)) + file_size = myFile.available(); + // read up to 4096 byte into the buffer + if (_seekByte > 0) { - return false; + if (file_size > _seekByte) + { + blockSize = myFile.readBytes(file_buf, _seekByte); + file_size = myFile.available(); + ESP_LOGI(TAG, "Seek file: %" PRIu32 ", left bytes %" PRIu32, _seekByte, file_size); + } + else + { + ESP_LOGE(TAG, "File failed seek"); + return false; + } + blockSize = myFile.readBytes(file_buf, ((file_size > sizeof(file_buf)) ? sizeof(file_buf) : file_size)); } else - { - if (_updateProgressCallback) + blockSize = myFile.readBytes(file_buf, ((file_size > sizeof(file_buf)) ? sizeof(file_buf) : file_size)); + + uartWriteBuf((char*)file_buf, blockSize); + // wait for the Nextion to return its 0x05 byte confirming reception and readiness to receive the next packets + this->recvRetString(response, 2000, true); + + if (response[0] == 0x08 && response.size() == 5) + { // handle partial upload request + remainingBlocks -= 1; + _sent_packets_total += blockSize; + _packets_total_byte += blockSize; + sent_bulk_counter++; + if (sent_bulk_counter % 10 == 0) { - _updateProgressCallback(); + ESP_LOGI(TAG, "bulk: %i, total bytes %" PRIu32 ", response: %s", sent_bulk_counter, _sent_packets_total, response.c_str()); + } + + ESP_LOGE(TAG, "response [%s]", + format_hex_pretty(reinterpret_cast(response.data()), response.size()).c_str()); + + for (int j = 0; j < 4; ++j) + { + offset += static_cast(response[j + 1]) << (8 * j); + ESP_LOGI(TAG, "Offset : %" PRIu32, offset); + } + if (offset) + { + remainingBlocks = ceil((file_size - offset) / blockSize); + _seekByte = offset - _packets_total_byte; + _packets_total_byte += _seekByte; } } - - if (_undownloadByte > 0) + else if (response[0] == 0x05) { - _undownloadByte -= c; + remainingBlocks -= 1; + _sent_packets_total += blockSize; + _packets_total_byte += blockSize; + sent_bulk_counter++; + if (sent_bulk_counter % 10 == 0) + { + ESP_LOGI(TAG, "bulk: %i, total bytes %" PRIu32 ", response: %s", sent_bulk_counter, _sent_packets_total, response.c_str()); + } + + ESP_LOGE(TAG, "response [%s]", + format_hex_pretty(reinterpret_cast(response.data()), response.size()).c_str()); + offset += 4096; + } + + else + { + if (timeout >= 2) + { + ESP_LOGE(TAG, "upload failed, no valid response from display, total bytes send : %" PRIu32, _sent_packets_total); + sent_bulk_counter = 0; + return false; + } + timeout++; } } - delay(1); + ESP_LOGI(TAG, "upload send last bytes %" PRIu32 ", response: %s", _sent_packets_total, response.c_str()); + // ESP_LOGI(TAG,"upload finished, total bytes send : %"PRIu32, _sent_packets_total); + sent_bulk_counter = 0; + return true; + } + else + { + ESP_LOGE(TAG, "File failed available"); + return false; } - - return true; } void ESPNexUpload::softReset(void) @@ -475,139 +505,129 @@ void ESPNexUpload::softReset(void) void ESPNexUpload::end() { + if (_upload_uart_num == NULL) + { + return; + } + // wait for the nextion to finish internal processes - delay(1600); + vTaskDelay(1600 / portTICK_PERIOD_MS); // soft reset the nextion this->softReset(); // end Serial connection - ((HardwareSerial*)nexSerial)->end(); + uart_mutex_lock(); + ESP_ERROR_CHECK(uart_driver_delete(_upload_uart_num)); + uart_mutex_unlock(); // reset sent packets counter - _sent_packets = 0; + //_sent_packets = 0; + _sent_packets_total = 0; - statusMessage = F("upload ok"); - _printInfoLine(statusMessage + F("\r\n")); + ESP_LOGI(TAG, "serial connection closed"); } void ESPNexUpload::_setRunningMode(void) { - String cmd = String(""); - delay(100); - cmd = F("runmod=2"); - this->sendCommand(cmd.c_str()); - delay(60); + vTaskDelay(100 / portTICK_PERIOD_MS); + this->sendCommand("runmod=2"); + vTaskDelay(60 / portTICK_PERIOD_MS); } -bool ESPNexUpload::_echoTest(String input) +bool ESPNexUpload::_echoTest(std::string input) { - String cmd = String(""); - String response = String(""); - cmd = "print \"" + input + "\""; + std::string response = ""; + std::string cmd = "print \"" + input + "\""; + this->sendCommand(cmd.c_str()); - uint32_t duration_ms = calculateTransmissionTimeMs(cmd) * 2 + 10; // times 2 (send + receive) and 10 ms extra this->recvRetString(response, duration_ms); - return (response.indexOf(input) != -1); + return (response.find(input) != -1); } bool ESPNexUpload::_handlingSleepAndDim(void) { - String cmd = String(""); - String response = String(""); + + std::string response = ""; bool set_sleep = false; bool set_dim = false; - cmd = F("get sleep"); - this->sendCommand(cmd.c_str()); - + this->sendCommand("get sleep"); this->recvRetString(response); if (response[0] != 0x71) { - statusMessage = F("unknown response from 'get sleep' request"); - _printInfoLine(statusMessage); + ESP_LOGE(TAG, "unknown response from 'get sleep' request"); return false; } if (response[1] != 0x00) { - _printInfoLine(F("sleep enabled")); + ESP_LOGD(TAG, "sleep enabled"); set_sleep = true; } else { - _printInfoLine(F("sleep disabled")); + ESP_LOGD(TAG, "sleep disabled"); } - response = String(""); - cmd = F("get dim"); - this->sendCommand(cmd.c_str()); - + this->sendCommand("get dim"); this->recvRetString(response); if (response[0] != 0x71) { - statusMessage = F("unknown response from 'get dim' request"); - _printInfoLine(statusMessage); + ESP_LOGE(TAG, "unknown response from 'get dim' request"); return false; } if (response[1] == 0x00) { - _printInfoLine(F("dim is 0%, backlight from display is turned off")); + ESP_LOGD(TAG, "dim is 0%%, backlight from display is turned off"); set_dim = true; } else { - _printInfoLine(); - dbSerialPrint(F("dim ")); - dbSerialPrint((uint8_t)response[1]); - dbSerialPrintln(F("%")); + ESP_LOGD(TAG, "dim %i%%", (uint8_t)response[1]); } if (!_echoTest("ABC")) { - statusMessage = F("echo test in 'handling sleep and dim' failed"); - _printInfoLine(statusMessage); + ESP_LOGE(TAG, "echo test in 'handling sleep and dim' failed"); return false; } if (set_sleep) { - cmd = F("sleep=0"); - this->sendCommand(cmd.c_str()); + this->sendCommand("sleep=0"); // Unfortunately the display doesn't send any respone on the wake up request (sleep=0) // Let the ESP wait for one second, this is based on serial analyser from Nextion editor V0.58 to Nextion display NX4024T032_011R // This gives the Nextion display some time to wake up - delay(1000); + vTaskDelay(1000 / portTICK_PERIOD_MS); } if (set_dim) { - cmd = F("dim=100"); - this->sendCommand(cmd.c_str()); - delay(15); + this->sendCommand("dim=100"); + vTaskDelay(15 / portTICK_PERIOD_MS); } return true; } -void ESPNexUpload::_printSerialData(bool esp_request, String input) +void ESPNexUpload::_printSerialData(bool esp_request, std::string input) { - char c; if (esp_request) - dbSerialPrint(F("ESP request: ")); + ESP_LOGI(TAG, "ESP request: "); else - dbSerialPrint(F("Nextion respone: ")); + ESP_LOGI(TAG, "Nextion respone: "); if (input.length() == 0) { - dbSerialPrintln(F("none")); + ESP_LOGW(TAG, "none"); return; } @@ -616,18 +636,15 @@ void ESPNexUpload::_printSerialData(bool esp_request, String input) c = input[i]; if ((uint8_t)c >= 0x20 && (uint8_t)c <= 0x7E) - dbSerialPrint(c); + printf("%c", c); else { - dbSerialPrint(F("0x")); - dbSerialPrintHex(c); - dbSerialPrint(F(" ")); + printf("0x\\%02hhx", c); } } - dbSerialPrintln(); } -uint32_t ESPNexUpload::calculateTransmissionTimeMs(String message) +uint32_t ESPNexUpload::calculateTransmissionTimeMs(std::string message) { // In general, 1 second (s) = 1000 (10^-3) millisecond (ms) or // 1 second (s) = 1000 000 (10^-6) microsecond (us). @@ -645,13 +662,160 @@ uint32_t ESPNexUpload::calculateTransmissionTimeMs(String message) uint32_t duration_message_us = nr_of_bytes * duration_one_byte_us; uint32_t return_value_ms = duration_message_us / 1000; - _printInfoLine("calculated transmission time: " + String(return_value_ms) + " ms"); + ESP_LOGD(TAG, "calculated transmission time: %" PRIu32 " ms", return_value_ms); return return_value_ms; } -void ESPNexUpload::_printInfoLine(String line) +uint32_t ESPNexUpload::uartAvailable() { - dbSerialPrint(F("Status info: ")); - if (line.length() != 0) - dbSerialPrintln(line); + if (_upload_uart_num == NULL) + { + return 0; + } + + uart_mutex_lock(); + size_t available; + uart_get_buffered_data_len(_upload_uart_num, &available); + if (_upload_uart_has_peek) + available++; + uart_mutex_unlock(); + return available; +} + +uint8_t ESPNexUpload::uartRead() +{ + if (_upload_uart_num == NULL) + { + return 0; + } + uint8_t c = 0; + + uart_mutex_lock(); + if (_upload_uart_has_peek) + { + _upload_uart_has_peek = false; + c = _upload_uart_peek_byte; + } + else + { + + int len = uart_read_bytes(_upload_uart_num, &c, 1, 20 / portTICK_RATE_MS); + if (len == 0) + { + c = 0; + } + } + uart_mutex_unlock(); + return c; +} + +void ESPNexUpload::uartWrite(uint8_t c) +{ + if (_upload_uart_num == NULL) + { + return; + } + uart_mutex_lock(); + uart_write_bytes(_upload_uart_num, &c, 1); + uart_mutex_unlock(); +} + +void ESPNexUpload::uartWriteBuf(const char *data, size_t len) +{ + if (_upload_uart_num == NULL || data == NULL || !len) + { + return; + } + + uart_mutex_lock(); + uart_write_bytes(_upload_uart_num, data, len); + uart_mutex_unlock(); +} + +void ESPNexUpload::uartFlushTxOnly() +{ + + bool txOnly = true; + if (_upload_uart_num == NULL) + { + return; + } + + uart_mutex_lock(); + ESP_ERROR_CHECK(uart_wait_tx_done(_upload_uart_num, portMAX_DELAY)); + + if (!txOnly) + { + ESP_ERROR_CHECK(uart_flush_input(_upload_uart_num)); + } + uart_mutex_unlock(); +} + +void ESPNexUpload::setBaudrate(uart_port_t uart_num, uint32_t baud_rate, gpio_num_t tx_io_num, gpio_num_t rx_io_num) +{ + + ESP_LOGD(TAG, "installing driver on uart %d with baud rate %d", uart_num, baud_rate); + + _upload_uart_num = uart_num; + _upload_baudrate = baud_rate; + _upload_tx_io_num = tx_io_num; + _upload_rx_io_num = rx_io_num; + + const uart_config_t uart_config = { + .baud_rate = (int)baud_rate, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE}; + + // Do not change the UART initialization order. + // This order was gotten from: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/uart.html + + ESP_ERROR_CHECK(uart_param_config(uart_num, &uart_config)); + if (_uart_diver_installed == true) + { + ESP_LOGD(TAG, "baud rate changed"); + return; + } + + ESP_ERROR_CHECK(uart_set_pin(uart_num, tx_io_num, rx_io_num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE)); + ESP_ERROR_CHECK(uart_driver_install(uart_num, + CONFIG_NEX_UART_RECV_BUFFER_SIZE, // Receive buffer size. + 0, // Transmit buffer size. + 10, // Queue size. + NULL, // Queue pointer. + 0)); // Allocation flags. + ESP_LOGD(TAG, "driver installed"); + _uart_diver_installed = true; +} + +std::string ESPNexUpload::str_snprintf(const char *fmt, size_t len, ...) { + std::string str; + va_list args; + + str.resize(len); + va_start(args, len); + size_t out_length = vsnprintf(&str[0], len + 1, fmt, args); + va_end(args); + + if (out_length < len) + str.resize(out_length); + + return str; +} +char ESPNexUpload::format_hex_pretty_char(uint8_t v) { return v >= 10 ? 'A' + (v - 10) : '0' + v; } +std::string ESPNexUpload::format_hex_pretty(const uint8_t *data, size_t length) { + if (length == 0) + return ""; + std::string ret; + ret.resize(3 * length - 1); + for (size_t i = 0; i < length; i++) { + ret[3 * i] = format_hex_pretty_char((data[i] & 0xF0) >> 4); + ret[3 * i + 1] = format_hex_pretty_char(data[i] & 0x0F); + if (i != length - 1) + ret[3 * i + 2] = '.'; + } + if (length > 4) + return ret + " (" + str_snprintf("%u", 32, length) + ")"; + return ret; } \ No newline at end of file diff --git a/src/modules/display/Nextion/ESPNexUpload.h b/src/modules/display/Nextion/ESPNexUpload.h index 3a22ac15..497f25c9 100644 --- a/src/modules/display/Nextion/ESPNexUpload.h +++ b/src/modules/display/Nextion/ESPNexUpload.h @@ -1,8 +1,17 @@ /** * @file NexUpload.h - * The definition of class NexUpload. - * - * + * The definition of class NexUpload. + * + * 1 - Removed all the Arduino code and replaced it by ESP-IDF + * 2 - Removed hard-coded UART configuration, see ESPNexUpload constructor + * 3 - Removed statusMessage and the function _printInfoLine + * 4 - Removed call-back functionality + * 5 - Removed one out of two upload functions + * 6 - BugFix in upload function + * @author Machiel Mastenbroek (machiel.mastenbroek@gmail.com) + * @date 2022/08/14 + * @version 0.6.0 + * * 1 - BugFix when display baudrate is diffrent from initial ESP baudrate * 2 - Improved debug information * 3 - Make delay commands dependent on the baudrate @@ -10,12 +19,12 @@ * @date 2019/11/04 * @version 0.5.5 * - * Stability improvement, Nextion display doesn’t freeze after the seconds 4096 trance of firmware bytes. - * Now the firmware upload process is stabled without the need of a hard Display power off-on intervention. - * Undocumented features (not mentioned in nextion-hmi-upload-protocol-v1-1 specification) are added. - * This implementation is based in on a reverse engineering with a UART logic analyser between + * Stability improvement, Nextion display doesn’t freeze after the seconds 4096 trance of firmware bytes. + * Now the firmware upload process is stabled without the need of a hard Display power off-on intervention. + * Undocumented features (not mentioned in nextion-hmi-upload-protocol-v1-1 specification) are added. + * This implementation is based in on a reverse engineering with a UART logic analyser between * the Nextion editor v0.58 and a NX4024T032_011R Display. - * + * * @author Machiel Mastenbroek (machiel.mastenbroek@gmail.com) * @date 2019/10/24 * @version 0.5.0 @@ -24,7 +33,7 @@ * @author Onno Dirkzwager (onno.dirkzwager@gmail.com) * @date 2018/12/26 * @version 0.3.0 - * + * * Modified to work with ESP8266 and SoftwareSerial * @author Ville Vilpas (psoden@gmail.com) * @date 2018/2/3 @@ -33,44 +42,46 @@ * Original version (a part of https://github.com/itead/ITEADLIB_Arduino_Nextion) * @author Chen Zengpeng (email:) * @date 2016/3/29 - * @copyright + * @copyright * Copyright (C) 2014-2015 ITEAD Intelligent Systems Co., Ltd. * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3. * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License + * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef __ESPNEXUPLOAD_H__ #define __ESPNEXUPLOAD_H__ -#include +//#include +#include /* printf, scanf, NULL */ + +//#include +#include "esp_log.h" +//#include "freertos/FreeRTOS.h" +//#include "freertos/task.h" +//#include "driver/gpio.h" +#include "driver/uart.h" + +//#include "hal/uart_types.h" #include #include -#ifdef ESP8266 -#include -#else -#include -#include -#endif +#define CONFIG_NEX_UART_RECV_BUFFER_SIZE 256 /** - * @addtogroup CoreAPI - * @{ + * @addtogroup CoreAPI + * @{ */ -// callback template definition -typedef std::function THandlerFunction; - /** * * Provides the API for nextion to upload the ftf file. @@ -78,81 +89,68 @@ typedef std::function THandlerFunction; class ESPNexUpload { public: /* methods */ - // callback template definition - typedef std::function THandlerFunction; - + /** - * Constructor. - * - * @param uint32_t upload_baudrate - set upload baudrate. + * Constructor. + * */ - ESPNexUpload(uint32_t upload_baudrate, int line, int rx, int tx); - + ESPNexUpload(uart_port_t uart_num, uint32_t baud_rate, gpio_num_t tx_io_num, gpio_num_t rx_io_num); + /** - * destructor. - * + * destructor. + * */ - ~ESPNexUpload() {} - + ~ESPNexUpload(){} + /** * Connect to Nextion over serial * * @return true or false. */ bool connect(); - + /** * prepare upload. Set file size & Connect to Nextion over serial * * @return true if success, false for failure. */ - bool prepareUpload(uint32_t file_size); - + bool prepareUpload(uint32_t file_size, bool prot); + /** - * set Update Progress Callback. (What to do during update progress) - * - * @return none - */ - void setUpdateProgressCallback(THandlerFunction value); - - /** - * start update tft file to nextion. - * + * start update tft file to nextion. + * * @param const uint8_t *file_buf * @param size_t buf_size * @return true if success, false for failure. */ bool upload(const uint8_t *file_buf, size_t buf_size); - /** - * start update tft file to nextion. - * - * @param Stream &myFile - * @return true if success, false for failure. - */ bool upload(Stream &myFile); - /** * Send reset command to Nextion over serial * * @return none. */ - void softReset(void); + void softReset(void); /** * Send reset, end serial, reset _sent_packets & update status message * * @return none. */ - void end(void); - -public: /* data */ - String statusMessage = ""; - + void end(void); + private: /* methods */ /* - * get communicate baudrate. + * Semaphore construction to prevent double UART actions * + */ + void uart_mutex_lock(void) {do {} while (xSemaphoreTake(_upload_uart_lock, portMAX_DELAY) != pdPASS);}; + void uart_mutex_unlock(void) {xSemaphoreGive(_upload_uart_lock);}; + + /* + * get communicate baudrate. + * * @return communicate baudrate. * */ @@ -162,127 +160,167 @@ private: /* methods */ * search communicate baudrate. * * @param baudrate - communicate baudrate. - * - * @return true if success, false for failure. + * + * @return true if success, false for failure. */ - bool _searchBaudrate(uint32_t baudrate); + bool _searchBaudrate(int baudrate); /* * set download baudrate. * * @param baudrate - set download baudrate. - * - * @return true if success, false for failure. + * + * @return true if success, false for failure. */ bool _setPrepareForFirmwareUpdate(uint32_t upload_baudrate); /* * set Nextion running mode. * - * Undocumented feature of the Nextion protocol. - * It's used by the 'upload to Nextion device' feature of the Nextion Editor V0.58 - * - * The nextion display doesn't send any response - * + * Undocumented feature of the Nextion protocol. + * It's used by the 'upload to Nextion device' feature of the Nextion Editor V0.58 + * + * The nextion display doesn't send any response + * */ void _setRunningMode(void); /* - * Test UART nextion connection availability - * - * @param input - echo string, - * - * @return true when the 'echo string' that is send is equal to the received string - * - * This test is used by the 'upload to Nextion device' feature of the Nextion Editor V0.58 + * Test UART nextion connection availability * + * @param input - echo string, + * + * @return true when the 'echo string' that is send is equal to the received string + * + * This test is used by the 'upload to Nextion device' feature of the Nextion Editor V0.58 + * */ - bool _echoTest(String input); - + bool _echoTest(std::string input); + /* * This function get the sleep and dim value from the Nextion display. * - * If sleep = 1 meaning: sleep is enabled + * If sleep = 1 meaning: sleep is enabled * action : sleep will be disabled - * If dim = 0, meaning: the display backlight is turned off - * action : dim will be set to 100 (percent) - * + * If dim = 0, meaning: the display backlight is turned off + * action : dim will be set to 100 (percent) + * */ bool _handlingSleepAndDim(void); - + /* * This function (debug) print the Nextion response to a human readable string * - * @param esp_request - true: request message from esp to nextion - * false: response message from nextion to esp - * - * @param input - string to print - * + * @param esp_request - true: request message from esp to nextion + * false: response message from nextion to esp + * + * @param input - string to print + * */ - void _printSerialData(bool esp_request, String input); - - /* - * This function print a prefix debug line - * - * @param line: optional debug/ info line - */ - void _printInfoLine(String line = ""); - + void _printSerialData(bool esp_request, std::string input); + /* * Send command to Nextion. * * @param cmd - the string of command. - * @param tail - end the string with tripple 0xFF byte - * @param null_head - start the string with a single 0x00 byte + * @param tail - end the string with tripple 0xFF byte + * @param null_head - start the string with a single 0x00 byte * * @return none. */ - void sendCommand(const char *cmd, bool tail = true, bool null_head = false); + void sendCommand(const char* cmd, bool tail = true, bool null_head = false); /* - * Receive string data. - * - * @param buffer - save string data. - * @param timeout - set timeout time. + * Receive string data. + * + * @param buffer - save string data. + * @param timeout - set timeout time. * @param recv_flag - if recv_flag is true,will braak when receive 0x05. * * @return the length of string buffer. * - */ - uint16_t recvRetString(String &string, uint32_t timeout = 500, bool recv_flag = false); + */ + uint16_t recvRetString(std::string &string, uint32_t timeout = 500,bool recv_flag = false); /* - * - * This function calculates the transmission time, the transmission time + * + * This function calculates the transmission time, the transmission time * is based on the length of the message and the baudrate. - * - * @param message - only used to determine the length of the message + * + * @param message - only used to determine the length of the message * * @return time in us length of string buffer. * */ - uint32_t calculateTransmissionTimeMs(String message); + uint32_t calculateTransmissionTimeMs(std::string message); + + /* + * Setup UART for communication with display + * + * @param uart_num - UART number + * @param baud_rate - baud rate speed + * @param tx_io_num - GPIO TX pin + * @param rx_io_num - GPIO RX pin + * + */ + void setBaudrate(uart_port_t uart_num, uint32_t baud_rate, gpio_num_t tx_io_num, gpio_num_t rx_io_num); - void nexSerialBegin(uint32_t upload_baudrate, int line, int rx, int tx); + /* + * Check is UART is avaialble + */ + uint32_t uartAvailable(); -private: /* data */ - uint32_t _baudrate; /* nextion serail baudrate */ - uint32_t _undownloadByte; /* undownload byte of tft file */ - uint32_t _upload_baudrate; /* upload baudrate */ - uint16_t _sent_packets = 0; /* upload baudrate */ - uint8_t _rx; - uint8_t _tx; - uint8_t _line; - THandlerFunction _updateProgressCallback; + /* + * Read one RX byte + * + * @return one received UART byte + */ + uint8_t uartRead(); -#ifdef ESP8266 - SoftwareSerial* nexSerial; -#else - Stream* nexSerial; -#endif + /* + * Write one TX byte + * + * @param c - one byte + * + */ + void uartWrite(uint8_t c); + + /* + * Write char string + * + * @param data - char string of data to send + * @param len - length of the string + * + */ + void uartWriteBuf(const char * data, size_t len); + + /* + * Clear TX UART buffer + */ + void uartFlushTxOnly(); + +private: /* data */ + bool protv2; + uint32_t _baudrate; /* nextion serail baudrate */ + uint32_t _undownloadByte; /* undownload byte of tft file */ + uart_port_t _upload_uart_num; /* upload uart port number */ + uint32_t _upload_baudrate; /* upload baudrate */ + gpio_num_t _upload_tx_io_num; /* upload gpio TX */ + gpio_num_t _upload_rx_io_num; /* upload gpio RX */ + xSemaphoreHandle _upload_uart_lock; /* semaphore to prevent double UART actions */ + bool _upload_uart_has_peek; /* UART RX peek flag */ + uint8_t _upload_uart_peek_byte; /* UART RX peek byte */ + //uint16_t _sent_packets = 0; /* _sent_packets till 4096 bytes */ + uint32_t _sent_packets_total = 0; /* total number of uploaded display firmware bytes */ + bool _uart_diver_installed; /* flag, if true UART is installed */ + +std::string str_snprintf(const char *fmt, size_t len, ...); + /// Format the byte array \p data of length \p len in pretty-printed, human-readable hex. +std::string format_hex_pretty(const uint8_t *data, size_t length); +static char format_hex_pretty_char(uint8_t v); }; /** * @} */ -#endif /* #ifndef __ESPNEXUPLOAD_H__ */ \ No newline at end of file +#endif /* #ifndef __ESPNEXUPLOAD_H__ */ diff --git a/src/modules/display/Nextion/Nextion.cpp b/src/modules/display/Nextion/Nextion.cpp index a1a25c33..08bc5354 100644 --- a/src/modules/display/Nextion/Nextion.cpp +++ b/src/modules/display/Nextion/Nextion.cpp @@ -14,6 +14,7 @@ private: bool _UpTelegram; char _inc; String _inStr = ""; // буфер приема строк в режимах 0, 1, 2 + bool _protv2; // Выводим русские буквы на экран Nextion (преобразуем в кодировку ISO-8859-5) String convertRUS(String text) @@ -65,6 +66,7 @@ public: jsonRead(parameters, "speed", _speed); jsonRead(parameters, "line", _line); jsonRead(parameters, "uploadTelegram", _UpTelegram); + jsonRead(parameters, "protv2", _protv2); } IoTValue execute(String command, std::vector ¶m) @@ -211,10 +213,9 @@ public: if (!updated) { SerialPrint("I", F("NextionUpdate"), "connecting to " + (String)_host); - HTTPClient http; + HTTPClient http; #if defined ESP8266 - WiFiClient client; - if (!http.begin(client, _host, 80, _url)) + if (!http.begin(_host, 80, _url)) SerialPrint("I", F("NextionUpdate"), "connection failed "); #elif defined ESP32 if (!http.begin(String("http://") + _host + _url)) @@ -269,29 +270,31 @@ public: int contentLength = http.getSize(); SerialPrint("I", F("NextionUpdate"), "File received. Update Nextion... "); bool result; - ESPNexUpload nexUp(_speed, _line, _rx, _tx); - nexUp.setUpdateProgressCallback([]() - { SerialPrint("I", F("NextionUpdate"), "... "); }); + ESPNexUpload nexUp(_line, _speed, (gpio_num_t)_tx, (gpio_num_t)_rx); + // nexUp.setUpdateProgressCallback([]() + // { SerialPrint("I", F("NextionUpdate"), "... "); }); - result = nexUp.prepareUpload(contentLength); + result = nexUp.prepareUpload(contentLength, _protv2); if (!result) { - SerialPrint("I", F("NextionUpdate"), "Error: " + (String)nexUp.statusMessage); + SerialPrint("I", F("NextionUpdate"), "Error Connect in prepare upload"); } else { + updated = true; SerialPrint("I", F("NextionUpdate"), "Start upload. File size is: " + (String)contentLength); result = nexUp.upload(*http.getStreamPtr()); if (result) { - updated = true; + SerialPrint("I", F("NextionUpdate"), "Succesfully updated Nextion! "); } else { - SerialPrint("I", F("NextionUpdate"), "Error updating Nextion: " + (String)nexUp.statusMessage); + SerialPrint("I", F("NextionUpdate"), "Error updating Nextion" ); } nexUp.end(); + updated = false; } } //---------------------NEXTION-UPDATE---END------------------------ diff --git a/src/modules/display/Nextion/modinfo.json b/src/modules/display/Nextion/modinfo.json index 7168875d..05aa73df 100644 --- a/src/modules/display/Nextion/modinfo.json +++ b/src/modules/display/Nextion/modinfo.json @@ -17,6 +17,7 @@ "rx": 16, "line": 2, "speed": 9600, + "protv2": 1, "uploadTelegram": 1 } ], @@ -28,8 +29,8 @@ "moduleName": "Nextion", "moduleVersion": "2.0", "usedRam": { - "esp32_4mb": 15, - "esp8266_4mb": 15 + "esp32_4mb": 152, + "esp8266_4mb": 152 }, "title": "Nextion", "moduleDesc": "загрузка прошивки в дисплей Nextion. Команда для запуска обновления дисплея: Nextion.Update(); ", @@ -40,6 +41,7 @@ "line": "Актуально только для ESP32: номер линии hardUART. =2 rx=16 tx=17, для SoftwarwSerial в ESP32 line = -1", "host": "Сервер обновления. Можно использовать LiveServer из VisualCode, указывать ip адрес", "url": "файл прошивки экрана, указывать с расширением, например nextion.tft или iotm/test.tft", + "protv2": "1-использует быстрый протоколо прошивки v1.2, 0-использует оффициальный протокол прошивки", "uploadTelegram": "1 - разрешает прошивать экран через модуль Telegram_v2", "btn-uploadServer": "Кнопка загрузки прошивки с сервера LiveServer или другого по ip" }, @@ -87,7 +89,12 @@ }, "defActive": false, "usedLibs": { - "esp32*": [], - "esp82*": [] + "esp32_4mb": [], + "esp32_4mb3f": [], + "esp8266_4mb": [], + "esp8266_1mb": [], + "esp8266_1mb_ota": [], + "esp8285_1mb": [], + "esp8285_1mb_ota": [] } } \ No newline at end of file diff --git a/src/modules/exec/BrokerMQTT/BrokerMQTT.cpp b/src/modules/exec/BrokerMQTT/BrokerMQTT.cpp index 9d62d553..1c6201c8 100644 --- a/src/modules/exec/BrokerMQTT/BrokerMQTT.cpp +++ b/src/modules/exec/BrokerMQTT/BrokerMQTT.cpp @@ -125,6 +125,8 @@ namespace _Broker } } + bool _debug; + class BrokerMQTT : public IoTItem { private: @@ -132,7 +134,7 @@ namespace _Broker int _port = 0; String _user; String _pass; - bool _debug; + //bool _debug; bool _brige; String _server; String _srvUser; @@ -152,19 +154,6 @@ namespace _Broker jsonRead(parameters, "srvUser", _srvUser); jsonRead(parameters, "srvPass", _srvPass); jsonRead(parameters, "srvPort", _srvPort); - - if (_brige) - { - clientMqtt = new PicoMQTT::Client(_server.c_str(), _srvPort, nullptr, _srvUser.c_str(), _srvPass.c_str()); - if (_debug) - { - SerialPrint("i", F("BrigeMQTT"), "Bridge mode : ON"); - SerialPrint("i", F("BrigeMQTT"), "Bridge server: " + _server); - SerialPrint("i", F("BrigeMQTT"), "Bridge port: " + String(_srvPort)); - SerialPrint("i", F("BrigeMQTT"), "Bridge user: " + _srvUser); - SerialPrint("i", F("BrigeMQTT"), "Bridge pass: " + _srvPass); - } - } } void doByInterval() @@ -178,11 +167,30 @@ namespace _Broker picoMqtt->begin(); picoMqtt->setDebug(_debug); picoMqtt->setAuth(_user, _pass); + if (_brige) + { + clientMqtt = new PicoMQTT::Client(_server.c_str(), _srvPort, chipId.c_str(), _srvUser.c_str(), _srvPass.c_str()); + clientMqtt->begin(); + if (_debug) + { + SerialPrint("i", F("BrigeMQTT"), "Bridge mode : ON"); + SerialPrint("i", F("BrigeMQTT"), "Bridge server: " + _server); + SerialPrint("i", F("BrigeMQTT"), "Bridge port: " + String(_srvPort)); + SerialPrint("i", F("BrigeMQTT"), "Bridge user: " + _srvUser); + SerialPrint("i", F("BrigeMQTT"), "Bridge pass: " + _srvPass); + } + } if (_brige && picoMqtt && clientMqtt) { picoMqtt->subscribe("#", [](const char *topic, const char *message) { clientMqtt->publish(topic, message); - SerialPrint("i", F("BrigeMQTT"), "client publish, topic: " + String(topic) + " msg: " + String(message) ); }); + if (_debug) + SerialPrint("i", F("BrigeMQTT"), "Client publish, topic: " + String(topic) + " msg: " + String(message) ); }); + + clientMqtt->subscribe("#", [](const char *topic, const char *message) + { picoMqtt->publish(topic, message); + if (_debug) + SerialPrint("i", F("BrigeMQTT"), "Server publish, topic: " + String(topic) + " msg: " + String(message) ); }); } // picoMqtt.begin(); xTaskCreatePinnedToCore( diff --git a/src/modules/exec/Telegram_v2/Telegram_v2.cpp b/src/modules/exec/Telegram_v2/Telegram_v2.cpp index b872730a..2581471e 100644 --- a/src/modules/exec/Telegram_v2/Telegram_v2.cpp +++ b/src/modules/exec/Telegram_v2/Telegram_v2.cpp @@ -686,7 +686,7 @@ public: { _myBot->sendMessage("ID: " + chipId, _chatID); _myBot->sendMessage("chatID: " + _chatID, _chatID); - _myBot->sendMessage("Command: /help - this text \n /all - inline menu get all values \n /allMenu - bottom menu get all values \n /menu - bottom USER menu from scenario \n /get_id - get value by ID \n /set_id_value - set value in ID \n /file_name_type - take file from esp \n /file_type - support file type \n /reboot - reboot esp \n\n send file and write download - \"download\" file to esp \n\n send *.tft file - flash Nextion \n\n send firmware.bin or littltfs.bin - firmware ESP ", _chatID); + _myBot->sendMessage("Command: /help - this text \n /all - inline menu get all values \n /allMenu - bottom menu get all values \n /menu - bottom USER menu from scenario \n /get_id - get value by ID \n /set_id_value - set value in ID \n /file_/path/name_type - take file from esp \n /file_type - support file type \n /reboot - reboot esp \n\n send file and write download - \"download\" file to esp \n\n send *.tft file - flash Nextion \n\n send firmware.bin or littltfs.bin - firmware ESP ", _chatID); } } else if (msg.text.indexOf("/reboot") != -1)