From dfe009f5c304e52ded5c0355ef8afb55d5ea038c Mon Sep 17 00:00:00 2001 From: Proddy Date: Thu, 8 Oct 2026 23:28:40 +0200 Subject: [PATCH 1/9] Add s3_16M_P_custom env to trial a trimmed framework via custom_sdkconfig --- platformio.ini | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/platformio.ini b/platformio.ini index 9fa302349..73646abfb 100644 --- a/platformio.ini +++ b/platformio.ini @@ -136,6 +136,20 @@ board = s_16M_P extends = espressif32_base_16M board = s3_16M_P +[env:s3_16M_P_custom] +; same as s3_16M_P, but the Arduino/IDF libs are rebuilt with HybridCompile using the +; custom_sdkconfig below, to compare flash size and free heap against stock s3_16M_P. +; first build takes 10-20 min (IDF compiled from source), later builds are cached. +; building s3_16M_P and this env alternately triggers a rebuild each time. +extends = env:s3_16M_P +custom_sdkconfig = + '# CONFIG_BT_ENABLED is not set' + '# CONFIG_LWIP_PPP_SUPPORT is not set' + '# CONFIG_LWIP_IP_FORWARD is not set' + '# CONFIG_LWIP_IPV4_NAPT is not set' + '# CONFIG_ULP_COPROC_ENABLED is not set' + CONFIG_LWIP_LOCAL_HOSTNAME="ems-esp" + [env:s3_32M_P] ; 16MB ESP32-S3 - with PSRAM and 32MB flash extends = espressif32_base_32M From 07514260f4cbdeb8f46277ce0c807e4d5a8f6633 Mon Sep 17 00:00:00 2001 From: Proddy Date: Thu, 8 Oct 2026 23:32:23 +0200 Subject: [PATCH 2/9] Move custom_sdkconfig trial env from s3_16M_P to s_16M_P --- platformio.ini | 329 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 329 insertions(+) diff --git a/platformio.ini b/platformio.ini index 73646abfb..02997c5b5 100644 --- a/platformio.ini +++ b/platformio.ini @@ -73,6 +73,335 @@ board_upload.flash_size = 16MB board_build.app_partition_name = app0 platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.05.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.8 based on IDF 5.5.4.260407 ; platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.09.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.12 based on IDF 5.5.5 +; PlatformIO Project Configuration File for EMS-ESP +; +; override any settings with your own local ones in pio_local.ini (see example pio_local.ini_example) +; The firmware is renamed following this convention, and placed in the build/firmware folder: +; EMS-ESP32---[+ if PSRAM onboard].bin +; For example: EMS-ESP-3_7_0-dev_31-esp32-16MB+.bin +; +; boards can be found in https://github.com/pioarduino/platform-espressif32/tree/main/boards + +[platformio] +; build all supported targets. Use pio_local.ini to override and build specific targets. +default_envs = s_4M, s_16M, s_16M_P, s3_16M_P + +description = EMS-ESP Firmware for the ESP32 +src_dir = src +lib_dir = lib +boards_dir = boards + +extra_configs = + factory_settings.ini + pio_local.ini + +[common] +core_build_flags = -std=gnu++20 -O3 -flto=auto -Wno-type-limits -Wall -Wextra -Wno-unused-parameter -Wno-unused-variable -Wno-format -Wno-missing-field-initializers +core_unbuild_flags = -std=gnu++11 -std=gnu++14 -std=gnu++17 -fno-lto + +my_build_flags = + +build_flags = + ${common.core_build_flags} + ${factory_settings.build_flags} + ${common.my_build_flags} + -I./src/core + -D ARDUINO_ARCH_ESP32 + -D ONEWIRE_CRC16=0 + -D CONFIG_UART_ISR_IN_IRAM + ; AsyncTCP + -D CONFIG_ASYNC_TCP_MAX_ACK_TIME=5000 ; default + -D CONFIG_ASYNC_TCP_PRIORITY=10 ; default + -D CONFIG_ASYNC_TCP_QUEUE_SIZE=64 ; default + -D CONFIG_ASYNC_TCP_RUNNING_CORE=1 ; force async_tcp task to be on same core as Arduino app (default is any core) + -D CONFIG_ASYNC_TCP_STACK_SIZE=8192 ; default is 16KB/8192*2 + ; ESPAsyncWebServer + ; -D WS_MAX_QUEUED_MESSAGES=0 ; not used, default 8 + ; -D SSE_MAX_QUEUED_MESSAGES=1 ; for log messages, default 32 + ; ESP_SSLClient + -D ENABLE_PSRAM ; take the ~17KB of TLS buffers from PSRAM instead of the internal heap. The library + ; needs BOARD_HAS_PSRAM too, so boards without PSRAM keep using the internal heap + -D EMSESP_MQTT_RUNNING_CORE=1 ; default 1 + ; -D EMSESP_MQTT_STACKSIZE=5120 ; default + -D EMSESP_UART_RUNNING_CORE=1 ; default any core + ; -D EMSESP_UART_STACKSIZE=2560 ; default + ; -D EMSESP_GATEWAY_TX_HOLD ; hold Tx while a K30RF/KM200 gateway is syncing, see issue #3094 + ; -D EMSESP_TX_PRE_DELAY=1000 ; wait this many us before each Tx so we don't overlap the master's break, see issue #3094 + +unbuild_flags = + ${common.core_unbuild_flags} + +; 4MB Flash variants +[espressif32_base_4M] +framework = arduino +board_build.partitions = partitions/esp32_partition_4M.csv +board_upload.flash_size = 4MB +board_build.app_partition_name = app0 +platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.05.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.8 based on IDF 5.5.4.260407 +; platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.09.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.12 based on IDF 5.5.5 + +; 16MB Flash variants +[espressif32_base_16M] +framework = arduino +board_build.partitions = partitions/esp32_partition_16M.csv +board_upload.flash_size = 16MB +board_build.app_partition_name = app0 +platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.05.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.8 based on IDF 5.5.4.260407 +; platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.09.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.12 based on IDF 5.5.5 + +; 32MB Flash variants +[espressif32_base_32M] +framework = arduino +board_build.partitions = partitions/esp32_partition_32M.csv +board_upload.flash_size = 32MB +board_build.app_partition_name = app0 +platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.05.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.8 based on IDF 5.5.4.260407 +; platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.09.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.12 based on IDF 5.5.5 + +[env] +build_flags = + ${common.build_flags} +build_unflags = + ${common.unbuild_flags} +extra_scripts = + post:scripts/rename_fw.py ; renames the firmware .bin file +monitor_speed = 115200 +monitor_filters = direct, esp32_exception_decoder +build_type = release +board_build.filesystem = littlefs +board_build.littlefs_version = 2.0 +lib_deps = + bblanchon/ArduinoJson @ 7.4.3 + ESP32Async/AsyncTCP @ 3.5.0 + ESP32Async/ESPAsyncWebServer @ 3.12.1 + https://github.com/mobizt/ReadyMail.git @ 0.4.2 + https://github.com/mobizt/ESP_SSLClient.git @ 3.1.3 + ; https://github.com/emsesp/EMS-ESP-Modules.git @ 1.0.8 + +; builds the web interface only, not the firmware +[env:build-webUI] +platform = native +targets = build +extra_scripts = pre:scripts/build_interface.py +; Exclude all source files so the C code doesn't build +build_src_filter = -<*> + +; +; Builds for different board types +; If you're building for a single target environment, we recommend creating a pio_local.ini (see example file) +; + +[env:s_4M] +; 4MB ESP32 - no SSL, no PSRAM - like a BBQKees older S32 and E32 models +extends = espressif32_base_4M +board = s_4M + +[env:s_16M] +; 16MB ESP32 - no PSRAM - like a BBQKees later S32 V2 models +extends = espressif32_base_16M +board = s_16M + +[env:s_16M_P] +; 16MB ESP32 - with PSRAM - like a BBQKees E32V2 +extends = espressif32_base_16M +board = s_16M_P + +[env:s_16M_P_custom] +; same as s_16M_P, but the Arduino/IDF libs are rebuilt with HybridCompile using the +; custom_sdkconfig below, to compare flash size and free heap against stock s_16M_P. +; first build takes 10-20 min (IDF compiled from source), later builds are cached. +; building s_16M_P and this env alternately triggers a rebuild each time. +extends = env:s_16M_P +custom_sdkconfig = + '# CONFIG_BT_ENABLED is not set' + '# CONFIG_LWIP_PPP_SUPPORT is not set' + '# CONFIG_LWIP_IP_FORWARD is not set' + '# CONFIG_LWIP_IPV4_NAPT is not set' + '# CONFIG_ULP_COPROC_ENABLED is not set' + CONFIG_LWIP_LOCAL_HOSTNAME="ems-esp" + +[env:s3_16M_P] +; 16MB ESP32-S3 - with PSRAM - like a BBQKees S3 +extends = espressif32_base_16M +board = s3_16M_P + +[env:s3_32M_P] +; 16MB ESP32-S3 - with PSRAM and 32MB flash +extends = espressif32_base_32M +board = s3_32M_P + +[env:s2_4M_P] +; based on lolin_s2_mini 4MB with 2MB PSRAM +extends = espressif32_base_4M +board = s2_4M_P + +[env:c3_mini_4M] +; based on lolin_c3_mini 4MB, no PSRAM +extends = espressif32_base_4M +board = c3_mini_4M + +; lolin C3 mini v1 needs special wifi initialization +; https://www.wemos.cc/en/latest/c3/c3_mini_1_0_0.html#about-wifi +[env:c3_miniv1_4M] +extends = espressif32_base_4M +board = c3_mini_4M +build_flags = + ${common.build_flags} + -DBOARD_C3_MINI_V1 + +; XIAO ESP32C - 512KB SRAM & 4MB Flash - https://wiki.seeedstudio.com/xiao_esp32c6_getting_started/ +[env:c6_xiao_4M] +extends = espressif32_base_4M +board_build.app_partition_name = app0 +board = seeed_xiao_esp32c6 +build_flags = + ${common.build_flags} + -DEMSESP_EN_ONLY + +; foundation for building and testing natively, standalone without an ESP32 +; use the `standalone` environment instead of `native` for testing +[env:native] +platform = native +build_type = debug +build_flags = +build_src_flags = + -DEMSESP_STANDALONE -DEMSESP_TEST + -DARDUINOJSON_ENABLE_ARDUINO_STRING=1 + -std=gnu++20 -Og -ggdb + -Wall -Wextra + -Wno-unused-parameter -Wno-sign-compare -Wno-missing-braces + -Wno-vla-cxx-extension -Wno-tautological-constant-out-of-range-compare + -I./src/core + -I./lib_standalone + -I./lib/uuid-common/src + -I./lib/uuid-console/src + -I./lib/uuid-log/src + -I./lib/PButton + -I./lib/espMqttClient/src + -I./lib/espMqttClient/src/Transport +build_src_filter = + +<../src> + +<../lib> + +<../lib_standalone> + -<../src/ESP32React> + -<../lib/uuid-telnet> + -<../lib/uuid-syslog> + -<../lib/eModbus> + -<../lib/OneWire> + -<../src/core/ModuleLibrary.cpp> +lib_ldf_mode = off +lib_deps = + +; unit tests +; The test code is in ./test/test_api.cpp and the test_api.h file is generated by the native-test-create environment. +; to run use `platformio run -e native-test -t exec`. All tests should PASS. +; tip: use https://jsondiff.com/ to compare the expected and actual responses. +; to update the test output run `python3 scripts/generate_test_api.py` +[env:native-test] +platform = native +test_build_src = true +build_flags = +build_type = debug +build_src_flags = + -DEMSESP_STANDALONE -DEMSESP_TEST + -DEMSESP_UNITY + -DARDUINOJSON_ENABLE_ARDUINO_STRING=1 + -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\" + -std=gnu++20 -Og -ggdb + -Wall -Wextra + -Wno-unused-parameter -Wno-sign-compare -Wno-missing-braces + -Wno-vla-cxx-extension -Wno-tautological-constant-out-of-range-compare + -I./src/core + -I./lib_standalone + -I./lib/uuid-common/src + -I./lib/uuid-console/src + -I./lib/uuid-log/src + -I./lib/PButton + -I./lib/espMqttClient/src + -I./lib/espMqttClient/src/Transport + -I./test/api + -I./test +build_src_filter = + +<../src> + +<../test> + +<../lib> + +<../lib_standalone> + -<../src/ESP32React> + -<../lib/uuid-telnet> + -<../lib/uuid-syslog> + -<../lib/eModbus> + -<../lib/OneWire> + -<../src/core/ModuleLibrary.cpp> +lib_ldf_mode = off +lib_deps = Unity +test_testing_command = + ${platformio.build_dir}/${this.__env__}/program + +; builds the test cases and creates the test_api.h file +; run with `pio run -e native-test-create -t exec` and capture the output between "START - CUT HERE" and "END - CUT HERE" and paste it into the test_api.h file +[env:native-test-create] +extends = env:native-test +build_flags = + -DEMSESP_UNITY_CREATE +; +; Building and testing locally on OS, which we call "standalone" without an ESP32. +; See https://docs.platformio.org/en/latest/platforms/native.html +; +; It will generate an executable which when run will show the EMS-ESP Console where you can run tests using the `test` command. +; +; See https://docs.platformio.org/en/latest/core/installation/shell-commands.html#piocore-install-shell-commands +; +; to build and run directly on linux: pio run -e standalone -t exec +; +; to build and run on Windows, it needs winsock for the console input so: +; - For the first time, install Msys2 (https://www.msys2.org/) and the GCC compiler with `run pacman -S mingw-w64-ucrt-x86_64-gcc` +; - Then, build with `pio run -e standalone` to create the program.exe file +; - run by calling the executable from the Mysys shell e.g. `C:/msys64/msys2_shell.cmd -defterm -here -no-start -ucrt64 -c /.pio/build/native/program.exe` +; - or use with Windows Terminal https://www.msys2.org/docs/terminals/ +; +[env:standalone] +extends = env:native +build_flags = + -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\" + +; Modbus +; Creates the file modbus_entity_parameters.hpp +; Creating the modbus registers is a multi-step process. Before it was in a shell script called generate_csv_and_headers.sh +; but now moved to pio so everything is in python and cross-platform. The logic is as follows: +; 1. create a dummy modbus_entity_parameters.hpp file so the first pass compiles +; 2. compile the EMS-ESPcode with the EMSESP_MODBUS flag set +; 3. run the entity_dump test command and generate the dump_entities.csv file +; 4. use the dump_entities.csv file with update_modbus_registers.py script to generate the modbus_entity_parameters.hpp file +; 5. clean up everything and start again with the EMSESP_STANDALONE flag set +; 6. run the entity_dump test command again to create the real dump_entities.csv file +; +; To use, run: +; pio run -e build_modbus -t clean -t build +[env:build_modbus] +extends = env:native +targets = build +extra_scripts = + pre:scripts/build_modbus_entity_parameters_pre.py + post:scripts/build_run_test.py +build_flags = -DEMSESP_MODBUS +custom_test_command = entity_dump +custom_output_file = dump_entities.csv +custom_post_script = scripts/build_modbus_entity_parameters_post.py + +; builds the real dump_entities.csv and dump_telegrams.csv files +; and also the Modbus-Entity-Registers.md file which is used in the emsesp.org documentation +; +; To use, run after build_modbus environment, with: +; pio run -e build_standalone -t clean -t build +[env:build_standalone] +extends = env:native +targets = build +extra_scripts = + post:scripts/build_run_test.py +build_flags = -DEMSESP_STANDALONE +custom_test_command = entity_dump +custom_output_file = dump_entities.csv +custom_post_script = scripts/build_standalone_post.py ; 32MB Flash variants [espressif32_base_32M] From 46f2090b30123c261d51a319564a964538d26b39 Mon Sep 17 00:00:00 2001 From: Proddy Date: Thu, 8 Oct 2026 23:34:11 +0200 Subject: [PATCH 3/9] Fix platformio.ini: remove duplicated content from previous commit --- platformio.ini | 329 ------------------------------------------------- 1 file changed, 329 deletions(-) diff --git a/platformio.ini b/platformio.ini index 02997c5b5..db0340026 100644 --- a/platformio.ini +++ b/platformio.ini @@ -65,81 +65,6 @@ board_build.app_partition_name = app0 platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.05.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.8 based on IDF 5.5.4.260407 ; platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.09.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.12 based on IDF 5.5.5 -; 16MB Flash variants -[espressif32_base_16M] -framework = arduino -board_build.partitions = partitions/esp32_partition_16M.csv -board_upload.flash_size = 16MB -board_build.app_partition_name = app0 -platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.05.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.8 based on IDF 5.5.4.260407 -; platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.09.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.12 based on IDF 5.5.5 -; PlatformIO Project Configuration File for EMS-ESP -; -; override any settings with your own local ones in pio_local.ini (see example pio_local.ini_example) -; The firmware is renamed following this convention, and placed in the build/firmware folder: -; EMS-ESP32---[+ if PSRAM onboard].bin -; For example: EMS-ESP-3_7_0-dev_31-esp32-16MB+.bin -; -; boards can be found in https://github.com/pioarduino/platform-espressif32/tree/main/boards - -[platformio] -; build all supported targets. Use pio_local.ini to override and build specific targets. -default_envs = s_4M, s_16M, s_16M_P, s3_16M_P - -description = EMS-ESP Firmware for the ESP32 -src_dir = src -lib_dir = lib -boards_dir = boards - -extra_configs = - factory_settings.ini - pio_local.ini - -[common] -core_build_flags = -std=gnu++20 -O3 -flto=auto -Wno-type-limits -Wall -Wextra -Wno-unused-parameter -Wno-unused-variable -Wno-format -Wno-missing-field-initializers -core_unbuild_flags = -std=gnu++11 -std=gnu++14 -std=gnu++17 -fno-lto - -my_build_flags = - -build_flags = - ${common.core_build_flags} - ${factory_settings.build_flags} - ${common.my_build_flags} - -I./src/core - -D ARDUINO_ARCH_ESP32 - -D ONEWIRE_CRC16=0 - -D CONFIG_UART_ISR_IN_IRAM - ; AsyncTCP - -D CONFIG_ASYNC_TCP_MAX_ACK_TIME=5000 ; default - -D CONFIG_ASYNC_TCP_PRIORITY=10 ; default - -D CONFIG_ASYNC_TCP_QUEUE_SIZE=64 ; default - -D CONFIG_ASYNC_TCP_RUNNING_CORE=1 ; force async_tcp task to be on same core as Arduino app (default is any core) - -D CONFIG_ASYNC_TCP_STACK_SIZE=8192 ; default is 16KB/8192*2 - ; ESPAsyncWebServer - ; -D WS_MAX_QUEUED_MESSAGES=0 ; not used, default 8 - ; -D SSE_MAX_QUEUED_MESSAGES=1 ; for log messages, default 32 - ; ESP_SSLClient - -D ENABLE_PSRAM ; take the ~17KB of TLS buffers from PSRAM instead of the internal heap. The library - ; needs BOARD_HAS_PSRAM too, so boards without PSRAM keep using the internal heap - -D EMSESP_MQTT_RUNNING_CORE=1 ; default 1 - ; -D EMSESP_MQTT_STACKSIZE=5120 ; default - -D EMSESP_UART_RUNNING_CORE=1 ; default any core - ; -D EMSESP_UART_STACKSIZE=2560 ; default - ; -D EMSESP_GATEWAY_TX_HOLD ; hold Tx while a K30RF/KM200 gateway is syncing, see issue #3094 - ; -D EMSESP_TX_PRE_DELAY=1000 ; wait this many us before each Tx so we don't overlap the master's break, see issue #3094 - -unbuild_flags = - ${common.core_unbuild_flags} - -; 4MB Flash variants -[espressif32_base_4M] -framework = arduino -board_build.partitions = partitions/esp32_partition_4M.csv -board_upload.flash_size = 4MB -board_build.app_partition_name = app0 -platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.05.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.8 based on IDF 5.5.4.260407 -; platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.09.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.12 based on IDF 5.5.5 - ; 16MB Flash variants [espressif32_base_16M] framework = arduino @@ -402,257 +327,3 @@ build_flags = -DEMSESP_STANDALONE custom_test_command = entity_dump custom_output_file = dump_entities.csv custom_post_script = scripts/build_standalone_post.py - -; 32MB Flash variants -[espressif32_base_32M] -framework = arduino -board_build.partitions = partitions/esp32_partition_32M.csv -board_upload.flash_size = 32MB -board_build.app_partition_name = app0 -platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.05.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.8 based on IDF 5.5.4.260407 -; platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.09.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.12 based on IDF 5.5.5 - -[env] -build_flags = - ${common.build_flags} -build_unflags = - ${common.unbuild_flags} -extra_scripts = - post:scripts/rename_fw.py ; renames the firmware .bin file -monitor_speed = 115200 -monitor_filters = direct, esp32_exception_decoder -build_type = release -board_build.filesystem = littlefs -board_build.littlefs_version = 2.0 -lib_deps = - bblanchon/ArduinoJson @ 7.4.3 - ESP32Async/AsyncTCP @ 3.5.0 - ESP32Async/ESPAsyncWebServer @ 3.12.1 - https://github.com/mobizt/ReadyMail.git @ 0.4.2 - https://github.com/mobizt/ESP_SSLClient.git @ 3.1.3 - ; https://github.com/emsesp/EMS-ESP-Modules.git @ 1.0.8 - -; builds the web interface only, not the firmware -[env:build-webUI] -platform = native -targets = build -extra_scripts = pre:scripts/build_interface.py -; Exclude all source files so the C code doesn't build -build_src_filter = -<*> - -; -; Builds for different board types -; If you're building for a single target environment, we recommend creating a pio_local.ini (see example file) -; - -[env:s_4M] -; 4MB ESP32 - no SSL, no PSRAM - like a BBQKees older S32 and E32 models -extends = espressif32_base_4M -board = s_4M - -[env:s_16M] -; 16MB ESP32 - no PSRAM - like a BBQKees later S32 V2 models -extends = espressif32_base_16M -board = s_16M - -[env:s_16M_P] -; 16MB ESP32 - with PSRAM - like a BBQKees E32V2 -extends = espressif32_base_16M -board = s_16M_P - -[env:s3_16M_P] -; 16MB ESP32-S3 - with PSRAM - like a BBQKees S3 -extends = espressif32_base_16M -board = s3_16M_P - -[env:s3_16M_P_custom] -; same as s3_16M_P, but the Arduino/IDF libs are rebuilt with HybridCompile using the -; custom_sdkconfig below, to compare flash size and free heap against stock s3_16M_P. -; first build takes 10-20 min (IDF compiled from source), later builds are cached. -; building s3_16M_P and this env alternately triggers a rebuild each time. -extends = env:s3_16M_P -custom_sdkconfig = - '# CONFIG_BT_ENABLED is not set' - '# CONFIG_LWIP_PPP_SUPPORT is not set' - '# CONFIG_LWIP_IP_FORWARD is not set' - '# CONFIG_LWIP_IPV4_NAPT is not set' - '# CONFIG_ULP_COPROC_ENABLED is not set' - CONFIG_LWIP_LOCAL_HOSTNAME="ems-esp" - -[env:s3_32M_P] -; 16MB ESP32-S3 - with PSRAM and 32MB flash -extends = espressif32_base_32M -board = s3_32M_P - -[env:s2_4M_P] -; based on lolin_s2_mini 4MB with 2MB PSRAM -extends = espressif32_base_4M -board = s2_4M_P - -[env:c3_mini_4M] -; based on lolin_c3_mini 4MB, no PSRAM -extends = espressif32_base_4M -board = c3_mini_4M - -; lolin C3 mini v1 needs special wifi initialization -; https://www.wemos.cc/en/latest/c3/c3_mini_1_0_0.html#about-wifi -[env:c3_miniv1_4M] -extends = espressif32_base_4M -board = c3_mini_4M -build_flags = - ${common.build_flags} - -DBOARD_C3_MINI_V1 - -; XIAO ESP32C - 512KB SRAM & 4MB Flash - https://wiki.seeedstudio.com/xiao_esp32c6_getting_started/ -[env:c6_xiao_4M] -extends = espressif32_base_4M -board_build.app_partition_name = app0 -board = seeed_xiao_esp32c6 -build_flags = - ${common.build_flags} - -DEMSESP_EN_ONLY - -; foundation for building and testing natively, standalone without an ESP32 -; use the `standalone` environment instead of `native` for testing -[env:native] -platform = native -build_type = debug -build_flags = -build_src_flags = - -DEMSESP_STANDALONE -DEMSESP_TEST - -DARDUINOJSON_ENABLE_ARDUINO_STRING=1 - -std=gnu++20 -Og -ggdb - -Wall -Wextra - -Wno-unused-parameter -Wno-sign-compare -Wno-missing-braces - -Wno-vla-cxx-extension -Wno-tautological-constant-out-of-range-compare - -I./src/core - -I./lib_standalone - -I./lib/uuid-common/src - -I./lib/uuid-console/src - -I./lib/uuid-log/src - -I./lib/PButton - -I./lib/espMqttClient/src - -I./lib/espMqttClient/src/Transport -build_src_filter = - +<../src> - +<../lib> - +<../lib_standalone> - -<../src/ESP32React> - -<../lib/uuid-telnet> - -<../lib/uuid-syslog> - -<../lib/eModbus> - -<../lib/OneWire> - -<../src/core/ModuleLibrary.cpp> -lib_ldf_mode = off -lib_deps = - -; unit tests -; The test code is in ./test/test_api.cpp and the test_api.h file is generated by the native-test-create environment. -; to run use `platformio run -e native-test -t exec`. All tests should PASS. -; tip: use https://jsondiff.com/ to compare the expected and actual responses. -; to update the test output run `python3 scripts/generate_test_api.py` -[env:native-test] -platform = native -test_build_src = true -build_flags = -build_type = debug -build_src_flags = - -DEMSESP_STANDALONE -DEMSESP_TEST - -DEMSESP_UNITY - -DARDUINOJSON_ENABLE_ARDUINO_STRING=1 - -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\" - -std=gnu++20 -Og -ggdb - -Wall -Wextra - -Wno-unused-parameter -Wno-sign-compare -Wno-missing-braces - -Wno-vla-cxx-extension -Wno-tautological-constant-out-of-range-compare - -I./src/core - -I./lib_standalone - -I./lib/uuid-common/src - -I./lib/uuid-console/src - -I./lib/uuid-log/src - -I./lib/PButton - -I./lib/espMqttClient/src - -I./lib/espMqttClient/src/Transport - -I./test/api - -I./test -build_src_filter = - +<../src> - +<../test> - +<../lib> - +<../lib_standalone> - -<../src/ESP32React> - -<../lib/uuid-telnet> - -<../lib/uuid-syslog> - -<../lib/eModbus> - -<../lib/OneWire> - -<../src/core/ModuleLibrary.cpp> -lib_ldf_mode = off -lib_deps = Unity -test_testing_command = - ${platformio.build_dir}/${this.__env__}/program - -; builds the test cases and creates the test_api.h file -; run with `pio run -e native-test-create -t exec` and capture the output between "START - CUT HERE" and "END - CUT HERE" and paste it into the test_api.h file -[env:native-test-create] -extends = env:native-test -build_flags = - -DEMSESP_UNITY_CREATE -; -; Building and testing locally on OS, which we call "standalone" without an ESP32. -; See https://docs.platformio.org/en/latest/platforms/native.html -; -; It will generate an executable which when run will show the EMS-ESP Console where you can run tests using the `test` command. -; -; See https://docs.platformio.org/en/latest/core/installation/shell-commands.html#piocore-install-shell-commands -; -; to build and run directly on linux: pio run -e standalone -t exec -; -; to build and run on Windows, it needs winsock for the console input so: -; - For the first time, install Msys2 (https://www.msys2.org/) and the GCC compiler with `run pacman -S mingw-w64-ucrt-x86_64-gcc` -; - Then, build with `pio run -e standalone` to create the program.exe file -; - run by calling the executable from the Mysys shell e.g. `C:/msys64/msys2_shell.cmd -defterm -here -no-start -ucrt64 -c /.pio/build/native/program.exe` -; - or use with Windows Terminal https://www.msys2.org/docs/terminals/ -; -[env:standalone] -extends = env:native -build_flags = - -DEMSESP_DEFAULT_LOCALE=\"en\" -DEMSESP_DEFAULT_BOARD_PROFILE=\"S32\" - -; Modbus -; Creates the file modbus_entity_parameters.hpp -; Creating the modbus registers is a multi-step process. Before it was in a shell script called generate_csv_and_headers.sh -; but now moved to pio so everything is in python and cross-platform. The logic is as follows: -; 1. create a dummy modbus_entity_parameters.hpp file so the first pass compiles -; 2. compile the EMS-ESPcode with the EMSESP_MODBUS flag set -; 3. run the entity_dump test command and generate the dump_entities.csv file -; 4. use the dump_entities.csv file with update_modbus_registers.py script to generate the modbus_entity_parameters.hpp file -; 5. clean up everything and start again with the EMSESP_STANDALONE flag set -; 6. run the entity_dump test command again to create the real dump_entities.csv file -; -; To use, run: -; pio run -e build_modbus -t clean -t build -[env:build_modbus] -extends = env:native -targets = build -extra_scripts = - pre:scripts/build_modbus_entity_parameters_pre.py - post:scripts/build_run_test.py -build_flags = -DEMSESP_MODBUS -custom_test_command = entity_dump -custom_output_file = dump_entities.csv -custom_post_script = scripts/build_modbus_entity_parameters_post.py - -; builds the real dump_entities.csv and dump_telegrams.csv files -; and also the Modbus-Entity-Registers.md file which is used in the emsesp.org documentation -; -; To use, run after build_modbus environment, with: -; pio run -e build_standalone -t clean -t build -[env:build_standalone] -extends = env:native -targets = build -extra_scripts = - post:scripts/build_run_test.py -build_flags = -DEMSESP_STANDALONE -custom_test_command = entity_dump -custom_output_file = dump_entities.csv -custom_post_script = scripts/build_standalone_post.py From cefd5a14cf0855fd26c68f4f90b8a0776f05a786 Mon Sep 17 00:00:00 2001 From: proddy Date: Fri, 9 Oct 2026 08:35:59 +0200 Subject: [PATCH 4/9] fix include conflicts --- src/core/{console.h => emsesp_console.h} | 0 src/core/{httpClient.h => emsesp_http_client.h} | 0 src/core/{network.h => emsesp_network.h} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename src/core/{console.h => emsesp_console.h} (100%) rename src/core/{httpClient.h => emsesp_http_client.h} (100%) rename src/core/{network.h => emsesp_network.h} (100%) diff --git a/src/core/console.h b/src/core/emsesp_console.h similarity index 100% rename from src/core/console.h rename to src/core/emsesp_console.h diff --git a/src/core/httpClient.h b/src/core/emsesp_http_client.h similarity index 100% rename from src/core/httpClient.h rename to src/core/emsesp_http_client.h diff --git a/src/core/network.h b/src/core/emsesp_network.h similarity index 100% rename from src/core/network.h rename to src/core/emsesp_network.h From eaaea504df43bb48763fc85b48a94f49313e15e3 Mon Sep 17 00:00:00 2001 From: proddy Date: Fri, 9 Oct 2026 08:36:15 +0200 Subject: [PATCH 5/9] rename tasmota, fix includes --- boards/s3_32M_P.json | 2 +- boards/s_16M.json | 2 +- boards/s_16M_P.json | 2 +- boards/s_4M.json | 2 +- mock-api/restServer.ts | 6 +++--- platformio.ini | 23 ++++++++++++++--------- src/core/analogsensor.h | 2 +- src/core/command.h | 2 +- src/core/console.cpp | 2 +- src/core/console_stream.h | 2 +- src/core/default_settings.h | 2 +- src/core/emsesp.h | 4 ++-- src/core/httpClient.cpp | 2 +- src/core/mqtt.h | 2 +- src/core/network.cpp | 2 +- src/core/shuntingYard.cpp | 2 +- src/core/system.cpp | 4 ++-- src/core/system.h | 2 +- src/core/temperaturesensor.h | 2 +- src/emsesp_version.h | 2 +- src/test/test.cpp | 2 +- src/web/WebCommandService.cpp | 2 +- src/web/WebStatusService.cpp | 2 +- 23 files changed, 40 insertions(+), 35 deletions(-) diff --git a/boards/s3_32M_P.json b/boards/s3_32M_P.json index 792b1a6b1..e172d9bb7 100644 --- a/boards/s3_32M_P.json +++ b/boards/s3_32M_P.json @@ -21,7 +21,7 @@ "arduino", "espidf" ], - "name": "Tasmota ESP32-S3 32M Flash OPI PSRAM, 4608KB Code/OTA, 2MB FS", + "name": "ESP32-S3 32M Flash OPI PSRAM, 4608KB Code/OTA, 2MB FS", "upload": { "flash_size": "32MB", "maximum_ram_size": 327680, diff --git a/boards/s_16M.json b/boards/s_16M.json index 664d2bd61..c9a0d32a2 100644 --- a/boards/s_16M.json +++ b/boards/s_16M.json @@ -18,7 +18,7 @@ "arduino", "espidf" ], - "name": "Tasmota ESP32 16M Flash, 4608KB Code/OTA, 2MB FS", + "name": "ESP32 16M Flash, 4608KB Code/OTA, 2MB FS", "upload": { "flash_size": "16MB", "maximum_ram_size": 327680, diff --git a/boards/s_16M_P.json b/boards/s_16M_P.json index a0d10643d..f9218cea9 100644 --- a/boards/s_16M_P.json +++ b/boards/s_16M_P.json @@ -19,7 +19,7 @@ "arduino", "espidf" ], - "name": "Tasmota ESP32 16M Flash DIO PSRAM, 4608KB Code/OTA, 2MB FS", + "name": "ESP32 16M Flash DIO PSRAM, 4608KB Code/OTA, 2MB FS", "upload": { "flash_size": "16MB", "maximum_ram_size": 327680, diff --git a/boards/s_4M.json b/boards/s_4M.json index 8978e2625..2c2b0ebeb 100644 --- a/boards/s_4M.json +++ b/boards/s_4M.json @@ -17,7 +17,7 @@ "arduino", "espidf" ], - "name": "Tasmota ESP32 4M Flash, 4608KB Code/OTA, 2MB FS", + "name": "ESP32 4M Flash, 4608KB Code/OTA, 2MB FS", "upload": { "flash_size": "4MB", "maximum_ram_size": 327680, diff --git a/mock-api/restServer.ts b/mock-api/restServer.ts index c606b951b..5e2c14f66 100644 --- a/mock-api/restServer.ts +++ b/mock-api/restServer.ts @@ -103,7 +103,7 @@ let system_status = { cpu_cores: 2, cpu_freq_mhz: 240, max_alloc_heap: 191, - arduino_version: 'Tasmota Arduino v3.3.8', + arduino_version: 'Arduino v3.3.8', sdk_version: '5.5.4.260407', partition: 'app0', flash_chip_size: 16384, @@ -209,7 +209,7 @@ switch (emulate_esp) { case 'ESP32': system_status.esp_platform = 'ESP32'; system_status.cpu_type = 'ESP32-D0WD-V3'; - system_status.arduino_version = 'Tasmota Arduino v3.3.8'; + system_status.arduino_version = 'Arduino v3.3.8'; system_status.sdk_version = '5.5.4.260407'; system_status.psram = true; system_status.psram_size = 4096; @@ -225,7 +225,7 @@ switch (emulate_esp) { default: system_status.esp_platform = 'ESP32S3'; system_status.cpu_type = 'ESP32-S3'; - system_status.arduino_version = 'Tasmota Arduino v3.3.8'; + system_status.arduino_version = 'Arduino v3.3.8'; system_status.sdk_version = '5.5.4.260407'; system_status.psram = true; system_status.psram_size = 8189; diff --git a/platformio.ini b/platformio.ini index db0340026..1f7fc3038 100644 --- a/platformio.ini +++ b/platformio.ini @@ -21,8 +21,8 @@ extra_configs = pio_local.ini [common] -core_build_flags = -std=gnu++20 -O3 -flto=auto -Wno-type-limits -Wall -Wextra -Wno-unused-parameter -Wno-unused-variable -Wno-format -Wno-missing-field-initializers -core_unbuild_flags = -std=gnu++11 -std=gnu++14 -std=gnu++17 -fno-lto +core_build_flags = -Wall -Wextra -Wno-type-limits -Wno-unused-parameter -Wno-unused-variable -Wno-format -Wno-missing-field-initializers +core_unbuild_flags = -O2 -O3 -Og -fno-lto my_build_flags = @@ -31,6 +31,9 @@ build_flags = ${factory_settings.build_flags} ${common.my_build_flags} -I./src/core + ; core 3.3.12 uses spi_host_device_t in ETH.h without including it. A framework + ; refresh restores that header, so force the declaration on every compile. + -include hal/spi_types.h -D ARDUINO_ARCH_ESP32 -D ONEWIRE_CRC16=0 -D CONFIG_UART_ISR_IN_IRAM @@ -62,8 +65,7 @@ framework = arduino board_build.partitions = partitions/esp32_partition_4M.csv board_upload.flash_size = 4MB board_build.app_partition_name = app0 -platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.05.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.8 based on IDF 5.5.4.260407 -; platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.09.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.12 based on IDF 5.5.5 +platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.09.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.12 based on IDF 5.5.5 ; 16MB Flash variants [espressif32_base_16M] @@ -71,8 +73,8 @@ framework = arduino board_build.partitions = partitions/esp32_partition_16M.csv board_upload.flash_size = 16MB board_build.app_partition_name = app0 -platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.05.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.8 based on IDF 5.5.4.260407 -; platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.09.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.12 based on IDF 5.5.5 +platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.09.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.12 based on IDF 5.5.5 +; platform_packages = framework-arduinoespressif32 @ https://github.com/emsesp/esp32-arduino-lib-builder/releases/download/0810-2237-5.5.5/framework-arduinoespressif32-v5.5.5-a63b839f.tar.xz ; 32MB Flash variants [espressif32_base_32M] @@ -80,8 +82,7 @@ framework = arduino board_build.partitions = partitions/esp32_partition_32M.csv board_upload.flash_size = 32MB board_build.app_partition_name = app0 -platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.05.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.8 based on IDF 5.5.4.260407 -; platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.09.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.12 based on IDF 5.5.5 +platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.09.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.12 based on IDF 5.5.5 [env] build_flags = @@ -143,7 +144,11 @@ custom_sdkconfig = '# CONFIG_LWIP_IP_FORWARD is not set' '# CONFIG_LWIP_IPV4_NAPT is not set' '# CONFIG_ULP_COPROC_ENABLED is not set' - CONFIG_LWIP_LOCAL_HOSTNAME="ems-esp" + '# CONFIG_ETH_USE_SPI_ETHERNET is not set' + '# CONFIG_ETH_SPI_ETHERNET_W5500 is not set' + '# CONFIG_ETH_SPI_ETHERNET_DM9051 is not set' + '# CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL is not set' + '# CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS is not set' [env:s3_16M_P] ; 16MB ESP32-S3 - with PSRAM - like a BBQKees S3 diff --git a/src/core/analogsensor.h b/src/core/analogsensor.h index ce3909953..a6f334a9a 100644 --- a/src/core/analogsensor.h +++ b/src/core/analogsensor.h @@ -21,7 +21,7 @@ #include "helpers.h" #include "mqtt.h" -#include "console.h" +#include "emsesp_console.h" #include diff --git a/src/core/command.h b/src/core/command.h index efc8ed6a4..1a2d2b820 100644 --- a/src/core/command.h +++ b/src/core/command.h @@ -21,7 +21,7 @@ #include -#include "console.h" +#include "emsesp_console.h" #include using uuid::console::Shell; diff --git a/src/core/console.cpp b/src/core/console.cpp index da3ef8815..be2f828b5 100644 --- a/src/core/console.cpp +++ b/src/core/console.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "console.h" +#include "emsesp_console.h" #include "console_stream.h" #include "emsesp.h" diff --git a/src/core/console_stream.h b/src/core/console_stream.h index ba1a4458e..edb95a749 100644 --- a/src/core/console_stream.h +++ b/src/core/console_stream.h @@ -20,7 +20,7 @@ #include -#include "console.h" +#include "emsesp_console.h" namespace emsesp { diff --git a/src/core/default_settings.h b/src/core/default_settings.h index fea89c2fc..ab15b0e3e 100644 --- a/src/core/default_settings.h +++ b/src/core/default_settings.h @@ -304,7 +304,7 @@ enum { #define STRINGIZE(s) #s #endif -#define ARDUINO_VERSION_STR(major, minor, patch) "Tasmota Arduino v" STRINGIZE(major) "." STRINGIZE(minor) "." STRINGIZE(patch) +#define ARDUINO_VERSION_STR(major, minor, patch) "Arduino v" STRINGIZE(major) "." STRINGIZE(minor) "." STRINGIZE(patch) #define ARDUINO_VERSION ARDUINO_VERSION_STR(ESP_ARDUINO_VERSION_MAJOR, ESP_ARDUINO_VERSION_MINOR, ESP_ARDUINO_VERSION_PATCH) #endif diff --git a/src/core/emsesp.h b/src/core/emsesp.h index ac15d7fb5..d5fe2212d 100644 --- a/src/core/emsesp.h +++ b/src/core/emsesp.h @@ -66,10 +66,10 @@ #include "mqtt.h" #include "modbus.h" #include "system.h" -#include "network.h" +#include "emsesp_network.h" #include "temperaturesensor.h" #include "analogsensor.h" -#include "console.h" +#include "emsesp_console.h" #include "console_stream.h" #include "shower.h" #include "roomcontrol.h" diff --git a/src/core/httpClient.cpp b/src/core/httpClient.cpp index 29f00eba8..cf2a104c0 100644 --- a/src/core/httpClient.cpp +++ b/src/core/httpClient.cpp @@ -18,7 +18,7 @@ #include "emsesp.h" -#include "httpClient.h" +#include "emsesp_http_client.h" #include #include diff --git a/src/core/mqtt.h b/src/core/mqtt.h index a8d6503df..5bbf9f821 100644 --- a/src/core/mqtt.h +++ b/src/core/mqtt.h @@ -23,7 +23,7 @@ #include "helpers.h" #include "system.h" -#include "console.h" +#include "emsesp_console.h" #include "command.h" #include "emsdevicevalue.h" #include diff --git a/src/core/network.cpp b/src/core/network.cpp index 235619916..8dbeccb8f 100644 --- a/src/core/network.cpp +++ b/src/core/network.cpp @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -#include "network.h" +#include "emsesp_network.h" #include "emsesp.h" diff --git a/src/core/shuntingYard.cpp b/src/core/shuntingYard.cpp index a60fc77a6..78a5fed2c 100644 --- a/src/core/shuntingYard.cpp +++ b/src/core/shuntingYard.cpp @@ -22,7 +22,7 @@ #include "shuntingYard.h" -#include "httpClient.h" +#include "emsesp_http_client.h" namespace emsesp { diff --git a/src/core/system.cpp b/src/core/system.cpp index 3c65f7c82..2d8eb1aba 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -17,7 +17,7 @@ */ #include "system.h" -#include "network.h" +#include "emsesp_network.h" #include "emsesp.h" // for send_raw_telegram() command #ifndef EMSESP_STANDALONE @@ -33,7 +33,7 @@ #include "firmwareVersion.h" #include "shuntingYard.h" // for compute() used by the message and sendmail commands -#include "httpClient.h" // for the shared TLS client settings +#include "emsesp_http_client.h" // for the shared TLS client settings #if defined(EMSESP_TEST) #include "../test/test.h" diff --git a/src/core/system.h b/src/core/system.h index 66b1c8fc0..36b061d45 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -23,7 +23,7 @@ #include #include "helpers.h" -#include "console.h" +#include "emsesp_console.h" #include "mqtt.h" #include "telegram.h" #include "led.h" diff --git a/src/core/temperaturesensor.h b/src/core/temperaturesensor.h index 1b9ebe23a..42fc29be4 100644 --- a/src/core/temperaturesensor.h +++ b/src/core/temperaturesensor.h @@ -23,7 +23,7 @@ #include "helpers.h" #include "mqtt.h" -#include "console.h" +#include "emsesp_console.h" #include diff --git a/src/emsesp_version.h b/src/emsesp_version.h index fc1c2c9d9..98f549e52 100644 --- a/src/emsesp_version.h +++ b/src/emsesp_version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.9.0-dev.16" +#define EMSESP_APP_VERSION "3.9.0-dev.17a" diff --git a/src/test/test.cpp b/src/test/test.cpp index 9a1045ea5..8e38974dd 100644 --- a/src/test/test.cpp +++ b/src/test/test.cpp @@ -458,7 +458,7 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const // S3 has 16MB flash // EMSESP::system_.uploadFirmwareURL("https://github.com/emsesp/EMS-ESP32/releases/download/latest/EMS-ESP-3_7_0-dev_32-ESP32S3-16MB+.bin"); // S3 - // Test for 4MB Tasmota builds + // Test for 4MB builds // EMSESP::system_.uploadFirmwareURL("https://github.com/emsesp/EMS-ESP32/releases/download/latest/EMS-ESP-3_7_2-dev_8-ESP32-4MB.bin"); // S32 EMSESP::system_.uploadFirmwareURL("https://github.com/emsesp/EMS-ESP32/releases/download/latest/EMS-ESP-3_7_2-dev_8-ESP32-16MB.bin"); // E32 diff --git a/src/web/WebCommandService.cpp b/src/web/WebCommandService.cpp index 62b6610d5..62db7a08e 100644 --- a/src/web/WebCommandService.cpp +++ b/src/web/WebCommandService.cpp @@ -20,7 +20,7 @@ #include "WebCommandService.h" #include "shuntingYard.h" -#include "httpClient.h" +#include "emsesp_http_client.h" namespace emsesp { diff --git a/src/web/WebStatusService.cpp b/src/web/WebStatusService.cpp index 642d84a52..6207e5db8 100644 --- a/src/web/WebStatusService.cpp +++ b/src/web/WebStatusService.cpp @@ -21,7 +21,7 @@ #ifndef EMSESP_STANDALONE #include #endif -#include "httpClient.h" +#include "emsesp_http_client.h" namespace emsesp { From e3b1eb6c51bb2e05a73e3c83be8397308176634b Mon Sep 17 00:00:00 2001 From: proddy Date: Fri, 9 Oct 2026 17:47:01 +0200 Subject: [PATCH 6/9] test new esp32-arduino-lib-builder packages --- .github/workflows/dev_release.yml | 233 ++++++++++++++++++++++++++---- interface/package.json | 2 +- interface/pnpm-lock.yaml | 42 +++--- platformio.ini | 23 ++- scripts/enable_ccache.py | 27 ++++ scripts/upload.py | 152 +++++++++++++------ scripts/upload_cli.py | 171 +++++++++++++--------- 7 files changed, 482 insertions(+), 168 deletions(-) create mode 100644 scripts/enable_ccache.py diff --git a/.github/workflows/dev_release.yml b/.github/workflows/dev_release.yml index f518634e4..a9811b2b3 100644 --- a/.github/workflows/dev_release.yml +++ b/.github/workflows/dev_release.yml @@ -11,30 +11,209 @@ on: permissions: contents: write +# Parallel graph (wall-clock is ~max of the firmware jobs, not the sum): +# webui ──────┐ +# ├─► firmware (s_4M / s_16M / s_16M_P / s3_16M_P) ─┐ +# modbus ─────┤ ├─► release +# └─► standalone ───────────────────────────────────┘ +# +# Extra -j on a single runner does not help: PlatformIO already compiles each +# environment with all CPUs. The win is separate GitHub runners. + jobs: - pre-release: - name: 'Build Dev Release' + webui: + name: Build webUI if: github.event_name == 'workflow_dispatch' || github.repository == 'emsesp/EMS-ESP32' runs-on: ubuntu-latest steps: - - - name: Install python 3.13 - uses: actions/setup-python@v7 + - name: Checkout repository + uses: actions/checkout@v7 + + - name: Install pnpm + uses: pnpm/action-setup@v4 with: - python-version: '3.13' + package_json_file: interface/package.json - name: Install Node.js 26 uses: actions/setup-node@v7 with: node-version: 26 + - name: Cache pnpm store + uses: actions/cache@v4 + with: + path: ~/.local/share/pnpm/store + key: ${{ runner.os }}-pnpm-${{ hashFiles('interface/package.json') }} + restore-keys: | + ${{ runner.os }}-pnpm- + + - name: Build webUI + env: + CI: true + working-directory: interface + run: | + pnpm install + pnpm build-webUI + + - name: Upload WWWData.h + uses: actions/upload-artifact@v4 + with: + name: wwwdata + path: src/ESP32React/WWWData.h + if-no-files-found: error + + modbus: + name: Build modbus + if: github.event_name == 'workflow_dispatch' || github.repository == 'emsesp/EMS-ESP32' + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v7 + + - name: Install python 3.13 + uses: actions/setup-python@v7 + with: + python-version: '3.13' + + - name: Install uv + uses: astral-sh/setup-uv@v7 + with: + version: "latest" + enable-cache: false + + - name: Install pioarduino + run: uv pip install --system pioarduino==6.2.0 + + - name: Build modbus + run: platformio run -e build_modbus + + - name: Upload modbus header + uses: actions/upload-artifact@v4 + with: + name: modbus-header + path: src/core/modbus_entity_parameters.hpp + if-no-files-found: error + + standalone: + name: Build standalone + needs: modbus + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v7 + + - name: Install python 3.13 + uses: actions/setup-python@v7 + with: + python-version: '3.13' + + - name: Install uv + uses: astral-sh/setup-uv@v7 + with: + version: "latest" + enable-cache: false + + - name: Install pioarduino + run: uv pip install --system pioarduino==6.2.0 + + - name: Restore generated modbus header + uses: actions/download-artifact@v4 + with: + name: modbus-header + path: src/core + + - name: Build standalone + run: platformio run -e build_standalone + + - name: Upload generated docs + uses: actions/upload-artifact@v4 + with: + name: generated-docs + path: | + docs/dump_entities.csv + docs/dump_telegrams.csv + docs/Modbus-Entity-Registers.md + if-no-files-found: error + + firmware: + name: Firmware ${{ matrix.env }} + needs: [webui, modbus] + runs-on: ubuntu-latest + strategy: + fail-fast: true + matrix: + # Keep in sync with platformio.ini default_envs + env: [s_4M, s_16M, s_16M_P, s3_16M_P] + steps: + - name: Checkout repository + uses: actions/checkout@v7 + + - name: Install python 3.13 + uses: actions/setup-python@v7 + with: + python-version: '3.13' + + - name: Install uv + uses: astral-sh/setup-uv@v7 + with: + version: "latest" + enable-cache: false + + - name: Cache PlatformIO packages + uses: actions/cache@v4 + with: + path: | + ~/.platformio/.cache + ~/.platformio/packages + ~/.platformio/platforms + key: ${{ runner.os }}-pio-${{ matrix.env }}-${{ hashFiles('platformio.ini') }} + restore-keys: | + ${{ runner.os }}-pio-${{ matrix.env }}- + ${{ runner.os }}-pio- + + - name: Install pioarduino + run: uv pip install --system pioarduino==6.2.0 intelhex + + - name: Restore WWWData.h + uses: actions/download-artifact@v4 + with: + name: wwwdata + path: src/ESP32React + + - name: Restore generated modbus header + uses: actions/download-artifact@v4 + with: + name: modbus-header + path: src/core + + - name: Setup ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: firmware-${{ matrix.env }} + restore-keys: firmware-${{ matrix.env }}- + max-size: 1G + + - name: Build ${{ matrix.env }} + env: + CCACHE_COMPILERCHECK: content + run: platformio run -e ${{ matrix.env }} + + - name: Upload firmware + uses: actions/upload-artifact@v4 + with: + name: firmware-${{ matrix.env }} + path: build/firmware + if-no-files-found: error + + release: + name: Create GitHub Release + needs: [firmware, standalone] + runs-on: ubuntu-latest + steps: - name: Checkout repository uses: actions/checkout@v7 with: fetch-depth: 0 - - - name: Install pnpm - run: npm install -g pnpm - name: Get the EMS-ESP version id: build_info @@ -49,27 +228,27 @@ jobs: prev=`git rev-parse -q --verify 'refs/tags/latest^{commit}' || git describe --tags --abbrev=0 --match 'v*'` echo "REF=$prev" >> $GITHUB_OUTPUT - - name: Install PlatformIO - run: | - python -m pip install --upgrade pip - pip install -U platformio - python -m pip install intelhex + - name: Restore generated modbus header + uses: actions/download-artifact@v4 + with: + name: modbus-header + path: src/core - - name: Build webUI - run: | - platformio run -e build-webUI + - name: Restore generated docs + uses: actions/download-artifact@v4 + with: + name: generated-docs + path: . - - name: Build modbus - run: | - platformio run -e build_modbus + - name: Restore firmware binaries + uses: actions/download-artifact@v4 + with: + pattern: firmware-* + merge-multiple: true + path: build/firmware - - name: Build standalone - run: | - platformio run -e build_standalone - - - name: Build all PIO target environments, from default_envs - run: | - platformio run + - name: List firmware files + run: ls -la build/firmware - name: Commit the generated files uses: stefanzweifel/git-auto-commit-action@v7 diff --git a/interface/package.json b/interface/package.json index 95c0e9e8b..05cda3a78 100644 --- a/interface/package.json +++ b/interface/package.json @@ -31,7 +31,7 @@ "@table-library/react-table-library": "4.1.15", "alova": "^3.5.5", "jwt-decode": "^4.0.0", - "preact": "^11.0.0", + "preact": "^11.0.1", "react": "^19.3.0", "react-dom": "^19.3.0", "react-icons": "^5.7.0", diff --git a/interface/pnpm-lock.yaml b/interface/pnpm-lock.yaml index 91d2b5913..fc19efc83 100644 --- a/interface/pnpm-lock.yaml +++ b/interface/pnpm-lock.yaml @@ -191,8 +191,8 @@ importers: specifier: ^4.0.0 version: 4.0.0 preact: - specifier: ^11.0.0 - version: 11.0.0 + specifier: ^11.0.1 + version: 11.0.1 react: specifier: ^19.3.0 version: 19.3.0 @@ -214,7 +214,7 @@ importers: version: 10.0.1(eslint@10.12.0(supports-color@10.2.2)) '@preact/preset-vite': specifier: ^2.10.6 - version: 2.10.6(@babel/core@7.29.7(supports-color@10.2.2))(preact@11.0.0)(supports-color@10.2.2)(vite@8.3.3(@types/node@26.6.4)(terser@5.51.2)) + version: 2.10.6(@babel/core@7.29.7(supports-color@10.2.2))(preact@11.0.1)(supports-color@10.2.2)(vite@8.3.3(@types/node@26.6.4)(terser@5.51.2)) '@trivago/prettier-plugin-sort-imports': specifier: ^6.0.2 version: 6.0.2(prettier@3.9.9)(supports-color@10.2.2) @@ -1338,8 +1338,8 @@ packages: duplexer3@0.1.5: resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==} - electron-to-chromium@1.5.449: - resolution: {integrity: sha512-dKeYk/3SFQz+nXi5whp3OuJzeWABEG4zCtkWJ9+Vby1E1iFM2y0gElRtQvNXRPJTsj8WAF4HMP7hvP790QtJ9g==} + electron-to-chromium@1.5.451: + resolution: {integrity: sha512-vUNKPrUzw8ByEMOxloq0a71iD/mPuPxVkbDm3G5YhnxTzOw/I6qAg2MUS0cYj/c267lWmw26yW4Vv7foxTxPLg==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -2531,8 +2531,8 @@ packages: resolution: {integrity: sha512-C+y9x90UElAddDZmV4qOx9W53B61PO7cIqWz2dQsWlwswuq4mr8NEwytdGKboYbQlGZ3awrkTeNvcZiZNHnQ8A==} engines: {node: '>=20'} - preact@11.0.0: - resolution: {integrity: sha512-LRTLKuSe6VYPhvBDjwFaX+nuZnpsw6IN2GSboisAvhGsncylBmrbDCERhgKDn6yP2mAwbGvsB5OVyTe33M8gBg==} + preact@11.0.1: + resolution: {integrity: sha512-TNNwEq++jYNHy747LziCjPRVgEwnLSOIOAw/oYcjI3Tdnubx2EXtt76I0an8PATvUWtx/DX1kFIg1eFN+O/fkw==} peerDependencies: preact-render-to-string: '>=6.7.0' peerDependenciesMeta: @@ -3015,8 +3015,8 @@ packages: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} - update-browserslist-db@1.3.3: - resolution: {integrity: sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==} + update-browserslist-db@1.3.4: + resolution: {integrity: sha512-t8wonIOdQyOjtPd2VktlBu6QjTVTks6v8bSi7kd0r2Vll8TK1JYDZtRRdbmCHOAMphkZgrzK+L+vXK4pyZ8t4A==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -3591,12 +3591,12 @@ snapshots: '@popperjs/core@2.11.8': {} - '@preact/preset-vite@2.10.6(@babel/core@7.29.7(supports-color@10.2.2))(preact@11.0.0)(supports-color@10.2.2)(vite@8.3.3(@types/node@26.6.4)(terser@5.51.2))': + '@preact/preset-vite@2.10.6(@babel/core@7.29.7(supports-color@10.2.2))(preact@11.0.1)(supports-color@10.2.2)(vite@8.3.3(@types/node@26.6.4)(terser@5.51.2))': dependencies: '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) '@babel/plugin-transform-react-jsx-development': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) - '@prefresh/vite': 2.4.12(preact@11.0.0)(supports-color@10.2.2)(vite@8.3.3(@types/node@26.6.4)(terser@5.51.2)) + '@prefresh/vite': 2.4.12(preact@11.0.1)(supports-color@10.2.2)(vite@8.3.3(@types/node@26.6.4)(terser@5.51.2)) '@rollup/pluginutils': 5.4.0 babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.29.7(supports-color@10.2.2)) debug: 4.4.3(supports-color@10.2.2) @@ -3614,20 +3614,20 @@ snapshots: dependencies: '@babel/core': 7.29.7(supports-color@10.2.2) - '@prefresh/core@1.5.11(preact@11.0.0)': + '@prefresh/core@1.5.11(preact@11.0.1)': dependencies: - preact: 11.0.0 + preact: 11.0.1 '@prefresh/utils@1.2.1': {} - '@prefresh/vite@2.4.12(preact@11.0.0)(supports-color@10.2.2)(vite@8.3.3(@types/node@26.6.4)(terser@5.51.2))': + '@prefresh/vite@2.4.12(preact@11.0.1)(supports-color@10.2.2)(vite@8.3.3(@types/node@26.6.4)(terser@5.51.2))': dependencies: '@babel/core': 7.29.7(supports-color@10.2.2) '@prefresh/babel-plugin': 0.5.4(@babel/core@7.29.7(supports-color@10.2.2)) - '@prefresh/core': 1.5.11(preact@11.0.0) + '@prefresh/core': 1.5.11(preact@11.0.1) '@prefresh/utils': 1.2.1 '@rollup/pluginutils': 4.2.1 - preact: 11.0.0 + preact: 11.0.1 vite: 8.3.3(@types/node@26.6.4)(terser@5.51.2) transitivePeerDependencies: - supports-color @@ -4005,9 +4005,9 @@ snapshots: dependencies: baseline-browser-mapping: 2.11.27 caniuse-lite: 1.0.30001815 - electron-to-chromium: 1.5.449 + electron-to-chromium: 1.5.451 node-releases: 2.0.57 - update-browserslist-db: 1.3.3(browserslist@4.29.3) + update-browserslist-db: 1.3.4(browserslist@4.29.3) buffer-alloc-unsafe@1.1.0: {} @@ -4370,7 +4370,7 @@ snapshots: duplexer3@0.1.5: {} - electron-to-chromium@1.5.449: {} + electron-to-chromium@1.5.451: {} emoji-regex@10.6.0: {} @@ -5501,7 +5501,7 @@ snapshots: powershell-utils@0.2.1: {} - preact@11.0.0: {} + preact@11.0.1: {} prelude-ls@1.2.1: {} @@ -5953,7 +5953,7 @@ snapshots: universalify@2.0.1: {} - update-browserslist-db@1.3.3(browserslist@4.29.3): + update-browserslist-db@1.3.4(browserslist@4.29.3): dependencies: browserslist: 4.29.3 escalade: 3.2.0 diff --git a/platformio.ini b/platformio.ini index 1f7fc3038..623058d2f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -67,14 +67,14 @@ board_upload.flash_size = 4MB board_build.app_partition_name = app0 platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.09.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.12 based on IDF 5.5.5 -; 16MB Flash variants +; 16MB Flash variants - using EMS-ESP Arduino/IDF libs (not Tasmota) [espressif32_base_16M] framework = arduino board_build.partitions = partitions/esp32_partition_16M.csv board_upload.flash_size = 16MB board_build.app_partition_name = app0 platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.09.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.12 based on IDF 5.5.5 -; platform_packages = framework-arduinoespressif32 @ https://github.com/emsesp/esp32-arduino-lib-builder/releases/download/0810-2237-5.5.5/framework-arduinoespressif32-v5.5.5-a63b839f.tar.xz +platform_packages = framework-arduinoespressif32 @ https://github.com/emsesp/esp32-arduino-lib-builder/releases/download/0810-2237-5.5.5/framework-arduinoespressif32-v5.5.5-a63b839f.tar.xz ; 32MB Flash variants [espressif32_base_32M] @@ -90,6 +90,7 @@ build_flags = build_unflags = ${common.unbuild_flags} extra_scripts = + pre:scripts/enable_ccache.py ; uses ccache when it is installed post:scripts/rename_fw.py ; renames the firmware .bin file monitor_speed = 115200 monitor_filters = direct, esp32_exception_decoder @@ -133,22 +134,32 @@ extends = espressif32_base_16M board = s_16M_P [env:s_16M_P_custom] -; same as s_16M_P, but the Arduino/IDF libs are rebuilt with HybridCompile using the -; custom_sdkconfig below, to compare flash size and free heap against stock s_16M_P. -; first build takes 10-20 min (IDF compiled from source), later builds are cached. -; building s_16M_P and this env alternately triggers a rebuild each time. +; same as s_16M_P, but the Arduino/IDF libs are rebuilt with HybridCompile using the custom_sdkconfig below extends = env:s_16M_P custom_sdkconfig = '# CONFIG_BT_ENABLED is not set' '# CONFIG_LWIP_PPP_SUPPORT is not set' '# CONFIG_LWIP_IP_FORWARD is not set' '# CONFIG_LWIP_IPV4_NAPT is not set' + '# CONFIG_LWIP_IPV4_NAPT_PORTMAP is not set' '# CONFIG_ULP_COPROC_ENABLED is not set' '# CONFIG_ETH_USE_SPI_ETHERNET is not set' '# CONFIG_ETH_SPI_ETHERNET_W5500 is not set' '# CONFIG_ETH_SPI_ETHERNET_DM9051 is not set' '# CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL is not set' '# CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS is not set' + '# CONFIG_FREERTOS_USE_TRACE_FACILITY is not set' + '# CONFIG_FREERTOS_ENABLE_TASK_SNAPSHOT is not set' + '# CONFIG_ESP_MODEM_USE_PPP_MODE is not set' + '# CONFIG_ESP_GDBSTUB_ENABLED is not set' + '# CONFIG_MDNS_ENABLE_CONSOLE_CLI is not set' + '# CONFIG_MDNS_ENABLE_BROWSE is not set' + '# CONFIG_I2S_ISR_IRAM_SAFE is not set' + ; UART ISR must live in IRAM. Tasmota puts ringbuf ISRs in flash, which + ; forces CONFIG_UART_ISR_IN_IRAM=n and panics (Cache error) when the EMS + ; or console UART fires during a flash read (WiFi stop / mDNS / NVS). + '# CONFIG_RINGBUF_PLACE_ISR_FUNCTIONS_INTO_FLASH is not set' + 'CONFIG_UART_ISR_IN_IRAM=y' [env:s3_16M_P] ; 16MB ESP32-S3 - with PSRAM - like a BBQKees S3 diff --git a/scripts/enable_ccache.py b/scripts/enable_ccache.py new file mode 100644 index 000000000..2bf5d1684 --- /dev/null +++ b/scripts/enable_ccache.py @@ -0,0 +1,27 @@ +# Wrap the C/C++ compilers with ccache when it is on PATH. +# No-op locally unless ccache is installed; CI installs it via ccache-action. +Import("env") + +import shutil + +ccache = shutil.which("ccache") +if ccache: + + def wrap(tool): + cmd = env.get(tool) + if not cmd: + return + resolved = env.subst(cmd) if isinstance(cmd, str) else cmd + if isinstance(resolved, str): + if resolved.startswith("ccache"): + return + env[tool] = f"{ccache} {resolved}" + return + if isinstance(resolved, (list, tuple)): + if resolved and "ccache" in str(resolved[0]): + return + env[tool] = [ccache, *resolved] + + wrap("CC") + wrap("CXX") + print("ccache enabled") diff --git a/scripts/upload.py b/scripts/upload.py index 19be74710..2d228b1fd 100644 --- a/scripts/upload.py +++ b/scripts/upload.py @@ -14,12 +14,17 @@ # and # extra_scripts = scripts/upload.py # +# Prefer a numeric IP over .local — mDNS (especially under WSL2) adds latency +# and can stall on IPv6 before falling back to IPv4. + +import hashlib +import os +import socket +import time +from urllib.parse import urlparse import requests -import hashlib -from urllib.parse import urlparse -import time -import os +from requests.adapters import HTTPAdapter Import("env") @@ -35,6 +40,10 @@ except ImportError: from tqdm import tqdm from termcolor import cprint +# http.client/urllib3 default is 8–16 KiB, which starves a LAN TCP window. +UPLOAD_BLOCK_SIZE = 64 * 1024 +UPLOAD_SNDBUF = 256 * 1024 + def print_success(x): cprint(x, 'green') @@ -44,6 +53,43 @@ def print_fail(x): cprint(f'Error: {x}', 'red') +def resolve_ipv4(host): + """Resolve hostname to IPv4 so we skip mDNS/IPv6 retries on every request.""" + if host.startswith('['): + return host + if ':' in host and host.count(':') == 1: + host = host.rsplit(':', 1)[0] + try: + socket.inet_pton(socket.AF_INET, host) + return host + except OSError: + pass + infos = socket.getaddrinfo(host, 80, socket.AF_INET, socket.SOCK_STREAM) + if not infos: + raise socket.gaierror(f'No IPv4 address for {host}') + return infos[0][4][0] + + +class FastHTTPAdapter(HTTPAdapter): + """Larger write chunks and socket send buffer for streaming POSTs.""" + + def init_poolmanager(self, connections, maxsize, block=False, **pool_kwargs): + socket_options = [ + (socket.IPPROTO_TCP, socket.TCP_NODELAY, 1), + (socket.SOL_SOCKET, socket.SO_SNDBUF, UPLOAD_SNDBUF), + ] + pool_kwargs.setdefault('socket_options', socket_options) + pool_kwargs.setdefault('blocksize', UPLOAD_BLOCK_SIZE) + super().init_poolmanager(connections, maxsize, block=block, **pool_kwargs) + + +def make_session(): + session = requests.Session() + adapter = FastHTTPAdapter(pool_connections=2, pool_maxsize=2) + session.mount('http://', adapter) + return session + + def build_headers(host_ip, emsesp_url, content_type='application/json', access_token=None, extra_headers=None): """Build common HTTP headers with optional overrides.""" headers = { @@ -51,18 +97,17 @@ def build_headers(host_ip, emsesp_url, content_type='application/json', access_t 'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/118.0', 'Accept': '*/*', 'Accept-Language': 'en-US', - 'Accept-Encoding': 'gzip, deflate', 'Referer': emsesp_url, 'Content-Type': content_type, 'Connection': 'keep-alive' } - + if access_token: headers['Authorization'] = f'Bearer {access_token}' - + if extra_headers: headers.update(extra_headers) - + return headers @@ -79,44 +124,52 @@ def on_upload(source, target, env): username = env.GetProjectOption('custom_username') password = env.GetProjectOption('custom_password') emsesp_ip = env.GetProjectOption('custom_emsesp_ip') - except Exception as e: + except Exception: print_fail(f'Missing settings. Add these to your pio_local.ini file:\n\ncustom_username=username\ncustom_password=password\ncustom_emsesp_ip=ems-esp.local\n') return - emsesp_url = f"http://{emsesp_ip}" - parsed_url = urlparse(emsesp_url) - host_ip = parsed_url.netloc - - signon_url = f"{emsesp_url}/rest/signIn" - signon_headers = build_headers(host_ip, emsesp_url) - - username_password = { - "username": username, - "password": password - } - - response = requests.post( - signon_url, json=username_password, headers=signon_headers) - - if response.status_code != 200: - print_fail("Authentication with EMS-ESP failed (code " + - str(response.status_code) + ")") + parsed_url = urlparse(f"http://{emsesp_ip}") + host_name = parsed_url.hostname or emsesp_ip + try: + host_ip = resolve_ipv4(host_name) + except OSError as e: + print_fail(f"Could not resolve {host_name} to IPv4: {e}") return - print_success("Authentication with EMS-ESP successful") - access_token = response.json().get('access_token') + if host_ip != host_name: + print_success(f"Resolved {host_name} -> {host_ip}") - # start the upload - firmware_path = str(source[0]) + emsesp_url = f"http://{host_ip}" + session = make_session() - with open(firmware_path, 'rb') as firmware: - md5 = hashlib.md5(firmware.read()).hexdigest() + try: + signon_url = f"{emsesp_url}/rest/signIn" + signon_headers = build_headers(host_ip, emsesp_url) - firmware.seek(0) + response = session.post( + signon_url, + json={"username": username, "password": password}, + headers=signon_headers, + timeout=15) + + if response.status_code != 200: + print_fail("Authentication with EMS-ESP failed (code " + + str(response.status_code) + ")") + return + + print_success("Authentication with EMS-ESP successful") + access_token = response.json().get('access_token') + + firmware_path = str(source[0]) + with open(firmware_path, 'rb') as firmware_file: + firmware = firmware_file.read() + + md5 = hashlib.md5(firmware).hexdigest() + filename = os.path.basename(firmware_path) encoder = MultipartEncoder(fields={ 'MD5': md5, - 'file': (firmware_path, firmware, 'application/octet-stream')} + 'file': (filename, firmware, 'application/octet-stream')} ) bar = tqdm(desc='Upload Progress', @@ -124,7 +177,8 @@ def on_upload(source, target, env): dynamic_ncols=True, unit='B', unit_scale=True, - unit_divisor=1024 + unit_divisor=1024, + mininterval=0.2 ) monitor = MultipartEncoderMonitor( @@ -142,27 +196,35 @@ def on_upload(source, target, env): ) upload_url = f"{emsesp_url}/rest/uploadFile" - - response = requests.post( - upload_url, data=monitor, headers=post_headers) + started = time.perf_counter() + response = session.post( + upload_url, data=monitor, headers=post_headers, timeout=None) + elapsed = time.perf_counter() - started bar.close() - time.sleep(0.1) - print() if response.status_code != 200: print_fail(f"Upload failed (code {response.status_code}).") else: - print_success("Upload successful. Rebooting device.") + kibs = (encoder.len / 1024) / elapsed if elapsed > 0 else 0 + print_success( + f"Upload successful ({kibs:.0f} KiB/s in {elapsed:.1f}s). Rebooting device.") restart_headers = build_headers( host_ip, emsesp_url, access_token=access_token) restart_url = f"{emsesp_url}/api/system/restart" - response = requests.get(restart_url, headers=restart_headers) - if response.status_code != 200: - print_fail(f"Restart failed (code {response.status_code})") + try: + response = session.get( + restart_url, headers=restart_headers, timeout=10) + if response.status_code != 200: + print_fail(f"Restart failed (code {response.status_code})") + except requests.RequestException: + # Device often drops the connection as it reboots — that's OK. + pass print() + finally: + session.close() if env.get('UPLOAD_PROTOCOL') == 'custom': diff --git a/scripts/upload_cli.py b/scripts/upload_cli.py index a6ed77681..c002009a2 100644 --- a/scripts/upload_cli.py +++ b/scripts/upload_cli.py @@ -9,21 +9,27 @@ # # Run using for example: # python3 upload_cli.py -i 10.10.10.175 -f ../build/firmware/EMS-ESP-3_7_0-dev_34-ESP32S3-16MB+.bin +# +# Prefer a numeric IP over .local — mDNS (especially under WSL2) adds latency +# and can stall on IPv6 before falling back to IPv4. import argparse import hashlib +import socket import sys import time from pathlib import Path +from urllib.parse import urlparse import requests +from requests.adapters import HTTPAdapter from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor from termcolor import cprint from tqdm import tqdm -# Constants USER_AGENT = 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/118.0' -CHUNK_SIZE = 8192 # 8KB chunks for MD5 calculation +UPLOAD_BLOCK_SIZE = 64 * 1024 +UPLOAD_SNDBUF = 256 * 1024 def print_success(x): @@ -34,13 +40,34 @@ def print_fail(x): return cprint(x, 'red') -def calculate_md5(file_path): - """Calculate MD5 hash of a file in chunks for memory efficiency.""" - md5_hash = hashlib.md5() - with open(file_path, 'rb') as f: - for chunk in iter(lambda: f.read(CHUNK_SIZE), b''): - md5_hash.update(chunk) - return md5_hash.hexdigest() +def resolve_ipv4(host): + """Resolve hostname to IPv4 so we skip mDNS/IPv6 retries on every request.""" + if host.startswith('['): + return host + if ':' in host and host.count(':') == 1: + host = host.rsplit(':', 1)[0] + try: + socket.inet_pton(socket.AF_INET, host) + return host + except OSError: + pass + infos = socket.getaddrinfo(host, 80, socket.AF_INET, socket.SOCK_STREAM) + if not infos: + raise socket.gaierror(f'No IPv4 address for {host}') + return infos[0][4][0] + + +class FastHTTPAdapter(HTTPAdapter): + """Larger write chunks and socket send buffer for streaming POSTs.""" + + def init_poolmanager(self, connections, maxsize, block=False, **pool_kwargs): + socket_options = [ + (socket.IPPROTO_TCP, socket.TCP_NODELAY, 1), + (socket.SOL_SOCKET, socket.SO_SNDBUF, UPLOAD_SNDBUF), + ] + pool_kwargs.setdefault('socket_options', socket_options) + pool_kwargs.setdefault('blocksize', UPLOAD_BLOCK_SIZE) + super().init_poolmanager(connections, maxsize, block=block, **pool_kwargs) def create_base_headers(host_ip, emsesp_url): @@ -50,7 +77,6 @@ def create_base_headers(host_ip, emsesp_url): 'User-Agent': USER_AGENT, 'Accept': '*/*', 'Accept-Language': 'en-US', - 'Accept-Encoding': 'gzip, deflate', 'Referer': emsesp_url, 'Connection': 'keep-alive' } @@ -58,35 +84,39 @@ def create_base_headers(host_ip, emsesp_url): def upload(file, ip, username, password): """Upload firmware to EMS-ESP device.""" - # Print welcome message print() print("EMS-ESP Firmware Upload") - # Validate file exists file_path = Path(file) if not file_path.exists(): print_fail(f"File not found: {file}") return - # Setup URLs and headers - emsesp_url = f"http://{ip}" - host_ip = ip - - # Use a session for connection pooling and persistence + parsed_url = urlparse(f"http://{ip}") + host_name = parsed_url.hostname or ip + try: + host_ip = resolve_ipv4(host_name) + except OSError as e: + print_fail(f"Could not resolve {host_name} to IPv4: {e}") + return + + if host_ip != host_name: + print_success(f"Resolved {host_name} -> {host_ip}") + + emsesp_url = f"http://{host_ip}" session = requests.Session() - + session.mount('http://', FastHTTPAdapter(pool_connections=2, pool_maxsize=2)) + try: - # Authentication signon_url = f"{emsesp_url}/rest/signIn" signon_headers = create_base_headers(host_ip, emsesp_url) signon_headers['Content-Type'] = 'application/json' - username_password = { - "username": username, - "password": password - } - - response = session.post(signon_url, json=username_password, headers=signon_headers) + response = session.post( + signon_url, + json={"username": username, "password": password}, + headers=signon_headers, + timeout=15) if response.status_code != 200: print_fail(f"Authentication failed (code {response.status_code})") @@ -95,56 +125,62 @@ def upload(file, ip, username, password): print_success("Authentication successful") access_token = response.json().get('access_token') - # Calculate MD5 hash - print("Calculating MD5 hash...") - md5 = calculate_md5(file_path) + firmware = file_path.read_bytes() + md5 = hashlib.md5(firmware).hexdigest() - # Start the upload - with open(file_path, 'rb') as firmware: - encoder = MultipartEncoder(fields={ - 'MD5': md5, - 'file': (file, firmware, 'application/octet-stream') - }) + encoder = MultipartEncoder(fields={ + 'MD5': md5, + 'file': (file_path.name, firmware, 'application/octet-stream') + }) - bar = tqdm( - desc='Upload Progress', - total=encoder.len, - dynamic_ncols=True, - unit='B', - unit_scale=True, - unit_divisor=1024 - ) + bar = tqdm( + desc='Upload Progress', + total=encoder.len, + dynamic_ncols=True, + unit='B', + unit_scale=True, + unit_divisor=1024, + mininterval=0.2 + ) - monitor = MultipartEncoderMonitor( - encoder, lambda monitor: bar.update(monitor.bytes_read - bar.n)) + monitor = MultipartEncoderMonitor( + encoder, lambda monitor: bar.update(monitor.bytes_read - bar.n)) - post_headers = create_base_headers(host_ip, emsesp_url) - post_headers.update({ - 'Content-Type': monitor.content_type, - 'Content-Length': str(monitor.len), - 'Origin': emsesp_url, + post_headers = create_base_headers(host_ip, emsesp_url) + post_headers.update({ + 'Content-Type': monitor.content_type, + 'Content-Length': str(monitor.len), + 'Origin': emsesp_url, + 'Authorization': f'Bearer {access_token}' + }) + + upload_url = f"{emsesp_url}/rest/uploadFile" + started = time.perf_counter() + response = session.post( + upload_url, data=monitor, headers=post_headers, timeout=None) + elapsed = time.perf_counter() - started + + bar.close() + + if response.status_code != 200: + print_fail(f"Upload failed (code {response.status_code})") + else: + kibs = (encoder.len / 1024) / elapsed if elapsed > 0 else 0 + print_success( + f"Upload successful ({kibs:.0f} KiB/s in {elapsed:.1f}s). Rebooting device.") + restart_headers = create_base_headers(host_ip, emsesp_url) + restart_headers.update({ + 'Content-Type': 'application/json', 'Authorization': f'Bearer {access_token}' }) - - upload_url = f"{emsesp_url}/rest/uploadFile" - response = session.post(upload_url, data=monitor, headers=post_headers) - - bar.close() - time.sleep(0.1) - - if response.status_code != 200: - print_fail(f"Upload failed (code {response.status_code})") - else: - print_success("Upload successful. Rebooting device.") - restart_headers = create_base_headers(host_ip, emsesp_url) - restart_headers.update({ - 'Content-Type': 'application/json', - 'Authorization': f'Bearer {access_token}' - }) - restart_url = f"{emsesp_url}/api/system/restart" - response = session.get(restart_url, headers=restart_headers) + restart_url = f"{emsesp_url}/api/system/restart" + try: + response = session.get( + restart_url, headers=restart_headers, timeout=10) if response.status_code != 200: print_fail(f"Restart failed (code {response.status_code})") + except requests.RequestException: + pass except requests.RequestException as e: print_fail(f"Network error: {e}") @@ -161,7 +197,6 @@ def upload(file, ip, username, password): print() -# main parser = argparse.ArgumentParser(description="EMS-ESP Firmware Upload") parser.add_argument("-f", "--file", metavar="FILE", required=True, type=str, help="firmware file") From 09aa40e604d6dd5944d98f3ce9b7b1196dda34e7 Mon Sep 17 00:00:00 2001 From: proddy Date: Fri, 9 Oct 2026 18:10:33 +0200 Subject: [PATCH 7/9] use new arduino core lib --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 623058d2f..60b0b51a8 100644 --- a/platformio.ini +++ b/platformio.ini @@ -74,7 +74,7 @@ board_build.partitions = partitions/esp32_partition_16M.csv board_upload.flash_size = 16MB board_build.app_partition_name = app0 platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.09.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.12 based on IDF 5.5.5 -platform_packages = framework-arduinoespressif32 @ https://github.com/emsesp/esp32-arduino-lib-builder/releases/download/0810-2237-5.5.5/framework-arduinoespressif32-v5.5.5-a63b839f.tar.xz +platform_packages = framework-arduinoespressif32 @ https://github.com/emsesp/esp32-arduino-lib-builder/releases/download/0910-1604-5.5.5/framework-arduinoespressif32-v5.5.5-a63b839f.tar.xz ; 32MB Flash variants [espressif32_base_32M] From c959cb1e8a18ea499e9886264e441229841827f8 Mon Sep 17 00:00:00 2001 From: proddy Date: Fri, 9 Oct 2026 23:55:15 +0200 Subject: [PATCH 8/9] #3275 --- CHANGELOG_LATEST.md | 8 +++++ interface/package.json | 4 +-- interface/pnpm-lock.yaml | 68 ++++++++++++++++++++-------------------- platformio.ini | 11 +++---- 4 files changed, 48 insertions(+), 43 deletions(-) diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index d25e66f90..87c7f12b5 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -20,6 +20,9 @@ This release is based on the latest Espressif/Arduino core version 3. It brings - new Application Setting "Automatically check for firmware updates", off by default. When enabled EMS-ESP asks emsesp.org for the latest version once a day on its own, otherwise it only asks when the WebUI wants to show it, so an unattended system never contacts emsesp.org - Automatically check and convert older 3.8.x filesystem to the new 3.9.0 format - heatpump pool: `poolon` (switch, 0x48A), `hppoolon` (pool heating active, 0x488), MP100 `poolshuntset` (0x5C4) and `poolrequest` (0x5BA), shorter pool names +- `s_16M_P_custom` PlatformIO env that HybridCompiles Arduino/IDF with a trimmed sdkconfig (no Bluetooth, PPP/modem, IP forward/NAPT, ULP, SPI Ethernet, GDBSTUB, FreeRTOS trace, mDNS browse/CLI) and places the UART ISR in IRAM +- 16MB builds use EMS-ESP's own Arduino/IDF libs from [esp32-arduino-lib-builder](https://github.com/emsesp/esp32-arduino-lib-builder) (`0910-1604-5.5.5`) instead of Tasmota's prebuilt cores +- optional `ccache` wrapping when it is installed (`scripts/enable_ccache.py`) ## Fixed @@ -45,6 +48,9 @@ This release is based on the latest Espressif/Arduino core version 3. It brings - add 0kW to heat limits [#3262](https://github.com/emsesp/EMS-ESP32/issues/3262), [#3265](https://github.com/emsesp/EMS-ESP32/issues/3265) - show temperature value for sensor 0x40 [#3272](https://github.com/emsesp/EMS-ESP32/issues/3272) - no EMS bus on custom boards whose interface relies on a pull-up on the Rx line. The newer ESP-IDF no longer enables the internal pull-up on the UART Rx pin, so it is now set explicitly as it was in 3.8 [#3258](https://github.com/emsesp/EMS-ESP32/discussions/3258) +- Guru Meditation (cache error) on HybridCompile builds when the EMS or console UART ISR ran from flash during a SPI flash read (WiFi power-down, mDNS, NVS). The UART ISR and ringbuf ISR helpers are now in IRAM +- compile errors against Arduino 3.3.12 from Tasmota/Arduino headers named `console.h`, `network.h` and `httpClient.h`. Those EMS-ESP headers are now `emsesp_console.h`, `emsesp_network.h` and `emsesp_http_client.h` +- HTTP OTA from WSL/mDNS stalled on IPv6 and used tiny write chunks. Uploads now resolve IPv4 and stream in 64 KB blocks (`scripts/upload.py`, `scripts/upload_cli.py`) ## Changed @@ -63,3 +69,5 @@ This release is based on the latest Espressif/Arduino core version 3. It brings - failed versions.json fetches back off from 5 up to 160 minutes instead of retrying every 5 minutes, as each attempt blocks the main loop for as long as the DNS, connect and read timeouts allow - optimized the LED library to support WS2812B V5 and the newer V6 RGB LEDs - hold Tx while a K30RF/KM200 gateway is syncing [#3094](https://github.com/emsesp/EMS-ESP32/issues/3094) +- Tasmota platform 2026.05.50 (Arduino 3.3.8 / IDF 5.5.4) to 2026.09.50 (Arduino 3.3.12 / IDF 5.5.5) +- CI dev-release firmware jobs run in parallel (webUI, firmware targets, standalone) instead of one serial job diff --git a/interface/package.json b/interface/package.json index 05cda3a78..d208136f7 100644 --- a/interface/package.json +++ b/interface/package.json @@ -45,7 +45,7 @@ "@types/node": "^26.6.4", "@types/react": "^19.3.0", "@types/react-dom": "^19.3.0", - "concurrently": "^10.0.5", + "concurrently": "^10.0.6", "eslint": "^10.12.0", "eslint-config-prettier": "^10.1.8", "etag": "^1.8.1", @@ -55,7 +55,7 @@ "terser": "^5.51.2", "typescript": "^6.0.3", "typescript-eslint": "^8.71.1", - "vite": "^8.3.3", + "vite": "^8.3.4", "vite-plugin-imagemin": "^0.6.1" }, "packageManager": "pnpm@12.10.1" diff --git a/interface/pnpm-lock.yaml b/interface/pnpm-lock.yaml index fc19efc83..6046f5856 100644 --- a/interface/pnpm-lock.yaml +++ b/interface/pnpm-lock.yaml @@ -214,7 +214,7 @@ importers: version: 10.0.1(eslint@10.12.0(supports-color@10.2.2)) '@preact/preset-vite': specifier: ^2.10.6 - version: 2.10.6(@babel/core@7.29.7(supports-color@10.2.2))(preact@11.0.1)(supports-color@10.2.2)(vite@8.3.3(@types/node@26.6.4)(terser@5.51.2)) + version: 2.10.6(@babel/core@7.29.7(supports-color@10.2.2))(preact@11.0.1)(supports-color@10.2.2)(vite@8.3.4(@types/node@26.6.4)(terser@5.51.2)) '@trivago/prettier-plugin-sort-imports': specifier: ^6.0.2 version: 6.0.2(prettier@3.9.9)(supports-color@10.2.2) @@ -228,8 +228,8 @@ importers: specifier: ^19.3.0 version: 19.3.0(@types/react@19.3.0) concurrently: - specifier: ^10.0.5 - version: 10.0.5 + specifier: ^10.0.6 + version: 10.0.6 eslint: specifier: ^10.12.0 version: 10.12.0(supports-color@10.2.2) @@ -258,11 +258,11 @@ importers: specifier: ^8.71.1 version: 8.71.1(eslint@10.12.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) vite: - specifier: ^8.3.3 - version: 8.3.3(@types/node@26.6.4)(terser@5.51.2) + specifier: ^8.3.4 + version: 8.3.4(@types/node@26.6.4)(terser@5.51.2) vite-plugin-imagemin: specifier: ^0.6.1 - version: 0.6.1(supports-color@10.2.2)(vite@8.3.3(@types/node@26.6.4)(terser@5.51.2)) + version: 0.6.1(supports-color@10.2.2)(vite@8.3.4(@types/node@26.6.4)(terser@5.51.2)) packages: @@ -1004,8 +1004,8 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.11.27: - resolution: {integrity: sha512-ElY12DaROGuan+lMmZ8Cvo/ZUbXPe7Enc/9VU/b1T3Kp4dwytRcNdR8DoSJN5SNJT/CuvcCA0DHDVmMOCePdRQ==} + baseline-browser-mapping@2.11.28: + resolution: {integrity: sha512-br0LOmjJqr6Rt4AEkadbcNf67Zpo7EPwKsWbjnRrJIa1QYFHQBqM4C7F2WTbV8ahTEcg1Eo+Y0aWjjORInYFyA==} engines: {node: '>=6.0.0'} hasBin: true @@ -1157,8 +1157,8 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - concurrently@10.0.5: - resolution: {integrity: sha512-JaP/CoftUrCcAFW/g//RbgEGwlelnEae6cfBLgH6ZdO6s8jPkn6p9SB9u6pdVxYXoiSnFqseOlHfrEfF82TVOg==} + concurrently@10.0.6: + resolution: {integrity: sha512-qgICXXFp2/3nAebRLmxcINKEXM1mLK3ij/y31P02VTs/KIkddIeC05n73DxOXsXMB9LFSqNU1/na3lDlOhcbIw==} engines: {node: '>=22'} hasBin: true @@ -2318,8 +2318,8 @@ packages: node-html-parser@6.1.13: resolution: {integrity: sha512-qIsTMOY4C/dAa5Q5vsobRpOOvPfC4pB61UVW2uSwZNUp0QU/jCekTal1vMmbO0DgdHeLUJpv/ARmDqErVxA3Sg==} - node-releases@2.0.57: - resolution: {integrity: sha512-kQK9LGGFiHtrWiNhZtA7Qbw17AQz+dmsEKODRIVTXA9+e5MS/2gZEBhYJt13GrAz5/IOZKddH/0Z3TP/Zgo+yw==} + node-releases@2.0.58: + resolution: {integrity: sha512-BdiKAgbfRlvfMq/XzO/AF9NPmaA8a+YpLAyAFTVFBj3gARrAIfWvNq7Q7NE4se3KgFu0KkrvQMxTS0XVnsGzHg==} engines: {node: '>=18'} normalize-package-data@2.5.0: @@ -2768,8 +2768,8 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shell-quote@1.9.0: - resolution: {integrity: sha512-Iov+JwFv/2HcTpcwNMKd8+IWNb8tboQJNQTkAY/LLVK7gGH9jy+LGkVqPxfekHl+yMmiqXszdGWXgkfml7hjqA==} + shell-quote@1.12.0: + resolution: {integrity: sha512-PcByqNyT/38F2kDNi006HAMRJaULuBzq/FOsw3qdZvX/GA9W/jamDaRskgHjubHiftXK5sIFxLNkvrXUwcof6Q==} engines: {node: '>= 0.4'} signal-exit@3.0.7: @@ -3057,8 +3057,8 @@ packages: peerDependencies: vite: 5.x || 6.x || 7.x || 8.x - vite@8.3.3: - resolution: {integrity: sha512-cTAldKPImjg6c+gk48U19POPn3GCBzZwpdsN8ZMEEcbpes+6/wvfqUd0C2y3qYY4wsj8PwgFwrZ/1jBPVttMSg==} + vite@8.3.4: + resolution: {integrity: sha512-IftLE8phJhdNRIGNEV4JCCvfNScoZQVfcXaN8TQropWGJtOHcu+iAB9pHcuGvmEb/kddw0LBe/jS8ztdC6xdiQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -3591,19 +3591,19 @@ snapshots: '@popperjs/core@2.11.8': {} - '@preact/preset-vite@2.10.6(@babel/core@7.29.7(supports-color@10.2.2))(preact@11.0.1)(supports-color@10.2.2)(vite@8.3.3(@types/node@26.6.4)(terser@5.51.2))': + '@preact/preset-vite@2.10.6(@babel/core@7.29.7(supports-color@10.2.2))(preact@11.0.1)(supports-color@10.2.2)(vite@8.3.4(@types/node@26.6.4)(terser@5.51.2))': dependencies: '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) '@babel/plugin-transform-react-jsx-development': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) - '@prefresh/vite': 2.4.12(preact@11.0.1)(supports-color@10.2.2)(vite@8.3.3(@types/node@26.6.4)(terser@5.51.2)) + '@prefresh/vite': 2.4.12(preact@11.0.1)(supports-color@10.2.2)(vite@8.3.4(@types/node@26.6.4)(terser@5.51.2)) '@rollup/pluginutils': 5.4.0 babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.29.7(supports-color@10.2.2)) debug: 4.4.3(supports-color@10.2.2) magic-string: 0.30.21 picocolors: 1.1.1 - vite: 8.3.3(@types/node@26.6.4)(terser@5.51.2) - vite-prerender-plugin: 0.5.14(vite@8.3.3(@types/node@26.6.4)(terser@5.51.2)) + vite: 8.3.4(@types/node@26.6.4)(terser@5.51.2) + vite-prerender-plugin: 0.5.14(vite@8.3.4(@types/node@26.6.4)(terser@5.51.2)) zimmerframe: 1.1.5 transitivePeerDependencies: - preact @@ -3620,7 +3620,7 @@ snapshots: '@prefresh/utils@1.2.1': {} - '@prefresh/vite@2.4.12(preact@11.0.1)(supports-color@10.2.2)(vite@8.3.3(@types/node@26.6.4)(terser@5.51.2))': + '@prefresh/vite@2.4.12(preact@11.0.1)(supports-color@10.2.2)(vite@8.3.4(@types/node@26.6.4)(terser@5.51.2))': dependencies: '@babel/core': 7.29.7(supports-color@10.2.2) '@prefresh/babel-plugin': 0.5.4(@babel/core@7.29.7(supports-color@10.2.2)) @@ -3628,7 +3628,7 @@ snapshots: '@prefresh/utils': 1.2.1 '@rollup/pluginutils': 4.2.1 preact: 11.0.1 - vite: 8.3.3(@types/node@26.6.4)(terser@5.51.2) + vite: 8.3.4(@types/node@26.6.4)(terser@5.51.2) transitivePeerDependencies: - supports-color @@ -3942,7 +3942,7 @@ snapshots: base64-js@1.5.1: {} - baseline-browser-mapping@2.11.27: {} + baseline-browser-mapping@2.11.28: {} bin-build@3.0.0: dependencies: @@ -4003,10 +4003,10 @@ snapshots: browserslist@4.29.3: dependencies: - baseline-browser-mapping: 2.11.27 + baseline-browser-mapping: 2.11.28 caniuse-lite: 1.0.30001815 electron-to-chromium: 1.5.451 - node-releases: 2.0.57 + node-releases: 2.0.58 update-browserslist-db: 1.3.4(browserslist@4.29.3) buffer-alloc-unsafe@1.1.0: {} @@ -4125,11 +4125,11 @@ snapshots: concat-map@0.0.1: {} - concurrently@10.0.5: + concurrently@10.0.6: dependencies: chalk: 5.6.2 rxjs: 7.8.2 - shell-quote: 1.9.0 + shell-quote: 1.12.0 supports-color: 10.2.2 tree-kill: 1.2.2 yargs: 18.0.0 @@ -5307,7 +5307,7 @@ snapshots: css-select: 5.2.2 he: 1.2.0 - node-releases@2.0.57: {} + node-releases@2.0.58: {} normalize-package-data@2.5.0: dependencies: @@ -5724,7 +5724,7 @@ snapshots: shebang-regex@3.0.0: {} - shell-quote@1.9.0: {} + shell-quote@1.12.0: {} signal-exit@3.0.7: {} @@ -5982,7 +5982,7 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - vite-plugin-imagemin@0.6.1(supports-color@10.2.2)(vite@8.3.3(@types/node@26.6.4)(terser@5.51.2)): + vite-plugin-imagemin@0.6.1(supports-color@10.2.2)(vite@8.3.4(@types/node@26.6.4)(terser@5.51.2)): dependencies: '@types/imagemin': 7.0.1 '@types/imagemin-gifsicle': 7.0.4 @@ -6007,11 +6007,11 @@ snapshots: imagemin-webp: 6.1.0 jpegtran-bin: 6.0.1 pathe: 0.2.0 - vite: 8.3.3(@types/node@26.6.4)(terser@5.51.2) + vite: 8.3.4(@types/node@26.6.4)(terser@5.51.2) transitivePeerDependencies: - supports-color - vite-prerender-plugin@0.5.14(vite@8.3.3(@types/node@26.6.4)(terser@5.51.2)): + vite-prerender-plugin@0.5.14(vite@8.3.4(@types/node@26.6.4)(terser@5.51.2)): dependencies: kolorist: 1.8.0 magic-string: 0.30.21 @@ -6019,9 +6019,9 @@ snapshots: simple-code-frame: 1.3.0 source-map: 0.7.6 stack-trace: 1.0.0 - vite: 8.3.3(@types/node@26.6.4)(terser@5.51.2) + vite: 8.3.4(@types/node@26.6.4)(terser@5.51.2) - vite@8.3.3(@types/node@26.6.4)(terser@5.51.2): + vite@8.3.4(@types/node@26.6.4)(terser@5.51.2): dependencies: lightningcss: 1.33.0 picomatch: 4.0.7 diff --git a/platformio.ini b/platformio.ini index 60b0b51a8..5c99e8ce1 100644 --- a/platformio.ini +++ b/platformio.ini @@ -65,15 +65,15 @@ framework = arduino board_build.partitions = partitions/esp32_partition_4M.csv board_upload.flash_size = 4MB board_build.app_partition_name = app0 -platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.09.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.12 based on IDF 5.5.5 +platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.09.50/platform-espressif32.zip ; Arduino Core 3.3.12 based on IDF 5.5.5 -; 16MB Flash variants - using EMS-ESP Arduino/IDF libs (not Tasmota) +; 16MB Flash variants [espressif32_base_16M] framework = arduino board_build.partitions = partitions/esp32_partition_16M.csv board_upload.flash_size = 16MB board_build.app_partition_name = app0 -platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.09.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.12 based on IDF 5.5.5 +platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.09.50/platform-espressif32.zip ; Arduino Core 3.3.12 based on IDF 5.5.5 platform_packages = framework-arduinoespressif32 @ https://github.com/emsesp/esp32-arduino-lib-builder/releases/download/0910-1604-5.5.5/framework-arduinoespressif32-v5.5.5-a63b839f.tar.xz ; 32MB Flash variants @@ -82,7 +82,7 @@ framework = arduino board_build.partitions = partitions/esp32_partition_32M.csv board_upload.flash_size = 32MB board_build.app_partition_name = app0 -platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.09.50/platform-espressif32.zip ; Tasmota Arduino Core 3.3.12 based on IDF 5.5.5 +platform = https://github.com/tasmota/platform-espressif32/releases/download/2026.09.50/platform-espressif32.zip ; Arduino Core 3.3.12 based on IDF 5.5.5 [env] build_flags = @@ -155,9 +155,6 @@ custom_sdkconfig = '# CONFIG_MDNS_ENABLE_CONSOLE_CLI is not set' '# CONFIG_MDNS_ENABLE_BROWSE is not set' '# CONFIG_I2S_ISR_IRAM_SAFE is not set' - ; UART ISR must live in IRAM. Tasmota puts ringbuf ISRs in flash, which - ; forces CONFIG_UART_ISR_IN_IRAM=n and panics (Cache error) when the EMS - ; or console UART fires during a flash read (WiFi stop / mDNS / NVS). '# CONFIG_RINGBUF_PLACE_ISR_FUNCTIONS_INTO_FLASH is not set' 'CONFIG_UART_ISR_IN_IRAM=y' From 04d373b93c4fe93d1a460a6fc7bd434d939058ae Mon Sep 17 00:00:00 2001 From: proddy Date: Sat, 10 Oct 2026 00:00:00 +0200 Subject: [PATCH 9/9] 3.9.0-dev.18 --- src/emsesp_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emsesp_version.h b/src/emsesp_version.h index 98f549e52..a9ad69f24 100644 --- a/src/emsesp_version.h +++ b/src/emsesp_version.h @@ -1 +1 @@ -#define EMSESP_APP_VERSION "3.9.0-dev.17a" +#define EMSESP_APP_VERSION "3.9.0-dev.18"