diff --git a/.github/workflows/build-ide.yml b/.github/workflows/build-ide.yml index 565b08d392..edc226b4d9 100644 --- a/.github/workflows/build-ide.yml +++ b/.github/workflows/build-ide.yml @@ -52,7 +52,6 @@ jobs: - name: Build Sketches env: ESP8266_ARDUINO_BUILDER: "arduino" - ESP8266_ARDUINO_IDE: "${{ runner.temp }}/arduino_ide" ESP8266_ARDUINO_LWIP: ${{ matrix.lwip }} run: | bash ./tests/build.sh 8 ${{ matrix.chunk }} @@ -75,8 +74,6 @@ jobs: key: ${{ runner.os }}-${{ hashFiles('package/package_esp8266com_index.template.json', 'tests/common.sh', 'tests/build.sh') }} - name: Build Sketch env: - ESP8266_ARDUINO_HARDWARE: "${{ runner.temp }}/hardware" - ESP8266_ARDUINO_IDE: "${{ runner.temp }}/arduino_ide" ESP8266_ARDUINO_SKETCHES: "libraries/esp8266/examples/Blink/Blink.ino" run: | bash ./tests/build.sh @@ -100,8 +97,6 @@ jobs: key: ${{ runner.os }}-${{ hashFiles('package/package_esp8266com_index.template.json', 'tests/common.sh', 'tests/build.sh') }} - name: Build Sketch env: - ESP8266_ARDUINO_HARDWARE: "${{ runner.temp }}/hardware" - ESP8266_ARDUINO_IDE: "${{ runner.temp }}/arduino_ide" ESP8266_ARDUINO_SKETCHES: "libraries/esp8266/examples/Blink/Blink.ino" run: | bash ./tests/build.sh diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index a7c72a0c41..977570aad8 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -24,5 +24,8 @@ jobs: python-version: '3.x' - name: Build documentation run: | - pip install --user -r doc/requirements.txt - bash ./tests/ci/build_docs.sh + pushd doc/ + python3 -mvenv _venv + ./_venv/bin/pip install -r requirements.txt + env SPHINXBUILD=$(pwd)/_venv/bin/sphinx-build ../tests/ci/build_docs.sh + popd diff --git a/.github/workflows/style-check.yml b/.github/workflows/style-check.yml index 50a99f2e53..c371c2f252 100644 --- a/.github/workflows/style-check.yml +++ b/.github/workflows/style-check.yml @@ -26,7 +26,8 @@ jobs: - name: Style check run: | sudo apt update - bash ./tests/ci/style_check.sh + python ./tests/test_restyle.py --quiet + env CLANG_FORMAT="clang-format-18" bash ./tests/ci/style_check.sh # Validate orthography diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000000..867f3d40de --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,21 @@ +# Read the Docs configuration file for Sphinx projects +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the OS, Python version and other tools you might need +build: + os: ubuntu-24.04 + tools: + python: "3.12" + +# Build documentation in the "doc/" directory with Sphinx +sphinx: + configuration: doc/conf.py + +# Install same versions as our local tools +# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html +python: + install: + - requirements: doc/requirements.txt diff --git a/boards.txt b/boards.txt index a1fdd6125d..de7f875eef 100644 --- a/boards.txt +++ b/boards.txt @@ -5645,6 +5645,229 @@ agruminolemon.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM agruminolemon.menu.iramfloat.yes=allowed in ISR agruminolemon.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM +############################################################## +mercury.name=Mercury +mercury.build.board=mercury +mercury.build.variant=mercury +mercury.upload.tool=esptool +mercury.upload.maximum_data_size=81920 +mercury.upload.wait_for_upload_port=true +mercury.upload.erase_cmd= +mercury.serial.disableDTR=true +mercury.serial.disableRTS=true +mercury.build.mcu=esp8266 +mercury.build.core=esp8266 +mercury.build.spiffs_pagesize=256 +mercury.build.debug_optim= +mercury.build.debug_port= +mercury.build.debug_level= +mercury.menu.xtal.80=80 MHz +mercury.menu.xtal.80.build.f_cpu=80000000L +mercury.menu.xtal.160=160 MHz +mercury.menu.xtal.160.build.f_cpu=160000000L +mercury.menu.vt.flash=Flash +mercury.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +mercury.menu.vt.heap=Heap +mercury.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +mercury.menu.vt.iram=IRAM +mercury.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +mercury.menu.exception.disabled=Disabled (new aborts on oom) +mercury.menu.exception.disabled.build.exception_flags=-fno-exceptions +mercury.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +mercury.menu.exception.enabled=Enabled +mercury.menu.exception.enabled.build.exception_flags=-fexceptions +mercury.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +mercury.menu.stacksmash.disabled=Disabled +mercury.menu.stacksmash.disabled.build.stacksmash_flags= +mercury.menu.stacksmash.enabled=Enabled +mercury.menu.stacksmash.enabled.build.stacksmash_flags=-fstack-protector +mercury.menu.ssl.all=All SSL ciphers (most compatible) +mercury.menu.ssl.all.build.sslflags= +mercury.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +mercury.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +mercury.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +mercury.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +mercury.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +mercury.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +mercury.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +mercury.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +mercury.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +mercury.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +mercury.menu.mmu.ext128k=128K Heap External 23LC1024 +mercury.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +mercury.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +mercury.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +mercury.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +mercury.menu.non32xfer.fast.build.non32xferflags= +mercury.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +mercury.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER +mercury.upload.resetmethod=--before default_reset --after hard_reset +mercury.build.flash_mode=dio +mercury.build.flash_flags=-DFLASHMODE_DIO +mercury.build.flash_freq=40 +mercury.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) +mercury.menu.eesz.4M2M.build.flash_size=4M +mercury.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +mercury.menu.eesz.4M2M.build.spiffs_pagesize=256 +mercury.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +mercury.menu.eesz.4M2M.build.spiffs_start=0x200000 +mercury.menu.eesz.4M2M.build.spiffs_end=0x3FA000 +mercury.menu.eesz.4M2M.build.spiffs_blocksize=8192 +mercury.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) +mercury.menu.eesz.4M3M.build.flash_size=4M +mercury.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +mercury.menu.eesz.4M3M.build.spiffs_pagesize=256 +mercury.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +mercury.menu.eesz.4M3M.build.spiffs_start=0x100000 +mercury.menu.eesz.4M3M.build.spiffs_end=0x3FA000 +mercury.menu.eesz.4M3M.build.spiffs_blocksize=8192 +mercury.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) +mercury.menu.eesz.4M1M.build.flash_size=4M +mercury.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +mercury.menu.eesz.4M1M.build.spiffs_pagesize=256 +mercury.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +mercury.menu.eesz.4M1M.build.spiffs_start=0x300000 +mercury.menu.eesz.4M1M.build.spiffs_end=0x3FA000 +mercury.menu.eesz.4M1M.build.spiffs_blocksize=8192 +mercury.menu.eesz.4M=4MB (FS:none OTA:~1019KB) +mercury.menu.eesz.4M.build.flash_size=4M +mercury.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +mercury.menu.eesz.4M.build.spiffs_pagesize=256 +mercury.menu.eesz.4M.build.rfcal_addr=0x3FC000 +mercury.menu.ip.lm2f=v2 Lower Memory +mercury.menu.ip.lm2f.build.lwip_include=lwip2/include +mercury.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +mercury.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +mercury.menu.ip.hb2f=v2 Higher Bandwidth +mercury.menu.ip.hb2f.build.lwip_include=lwip2/include +mercury.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +mercury.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +mercury.menu.ip.lm2n=v2 Lower Memory (no features) +mercury.menu.ip.lm2n.build.lwip_include=lwip2/include +mercury.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +mercury.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +mercury.menu.ip.hb2n=v2 Higher Bandwidth (no features) +mercury.menu.ip.hb2n.build.lwip_include=lwip2/include +mercury.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +mercury.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +mercury.menu.ip.lm6f=v2 IPv6 Lower Memory +mercury.menu.ip.lm6f.build.lwip_include=lwip2/include +mercury.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +mercury.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +mercury.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +mercury.menu.ip.hb6f.build.lwip_include=lwip2/include +mercury.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +mercury.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +mercury.menu.dbg.Disabled=Disabled +mercury.menu.dbg.Disabled.build.debug_port= +mercury.menu.dbg.Serial=Serial +mercury.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +mercury.menu.dbg.Serial1=Serial1 +mercury.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +mercury.menu.lvl.None____=None +mercury.menu.lvl.None____.build.debug_level= +mercury.menu.optim.Smallest=None +mercury.menu.optim.Smallest.build.debug_optim=-Os +mercury.menu.optim.Lite=Lite +mercury.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +mercury.menu.optim.Full=Optimum +mercury.menu.optim.Full.build.debug_optim=-Og +mercury.menu.lvl.SSL=SSL +mercury.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +mercury.menu.lvl.TLS_MEM=TLS_MEM +mercury.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +mercury.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +mercury.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +mercury.menu.lvl.HTTP_SERVER=HTTP_SERVER +mercury.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +mercury.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +mercury.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +mercury.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +mercury.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +mercury.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +mercury.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +mercury.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +mercury.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +mercury.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +mercury.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +mercury.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +mercury.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +mercury.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +mercury.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +mercury.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +mercury.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +mercury.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +mercury.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +mercury.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +mercury.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +mercury.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +mercury.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +mercury.menu.lvl.CORE=CORE +mercury.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +mercury.menu.lvl.WIFI=WIFI +mercury.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +mercury.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +mercury.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +mercury.menu.lvl.UPDATER=UPDATER +mercury.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +mercury.menu.lvl.OTA=OTA +mercury.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +mercury.menu.lvl.OOM=OOM +mercury.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +mercury.menu.lvl.MDNS=MDNS +mercury.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +mercury.menu.lvl.HWDT=HWDT +mercury.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +mercury.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +mercury.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K +mercury.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +mercury.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +mercury.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +mercury.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS -DDEBUG_ESP_HWDT +mercury.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +mercury.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS -DDEBUG_ESP_HWDT_NOEXTRA4K +mercury.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +mercury.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS +mercury.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +mercury.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS -DDEBUG_ESP_HWDT +mercury.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +mercury.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM -DDEBUG_ESP_MDNS -DDEBUG_ESP_HWDT_NOEXTRA4K +mercury.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +mercury.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +mercury.menu.wipe.none=Only Sketch +mercury.menu.wipe.none.upload.erase_cmd= +mercury.menu.wipe.sdk=Sketch + WiFi Settings +mercury.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +mercury.menu.wipe.all=All Flash Contents +mercury.menu.wipe.all.upload.erase_cmd=erase_flash +mercury.menu.baud.115200=115200 +mercury.menu.baud.115200.upload.speed=115200 +mercury.menu.baud.57600=57600 +mercury.menu.baud.57600.upload.speed=57600 +mercury.menu.baud.230400.linux=230400 +mercury.menu.baud.230400.macosx=230400 +mercury.menu.baud.230400.upload.speed=230400 +mercury.menu.baud.256000.windows=256000 +mercury.menu.baud.256000.upload.speed=256000 +mercury.menu.baud.460800.linux=460800 +mercury.menu.baud.460800.macosx=460800 +mercury.menu.baud.460800.upload.speed=460800 +mercury.menu.baud.512000.windows=512000 +mercury.menu.baud.512000.upload.speed=512000 +mercury.menu.baud.921600=921600 +mercury.menu.baud.921600.upload.speed=921600 +mercury.menu.baud.3000000=3000000 +mercury.menu.baud.3000000.upload.speed=3000000 +mercury.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +mercury.menu.eesz.autoflash.build.flash_size=16M +mercury.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld +mercury.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 +mercury.menu.eesz.autoflash.upload.maximum_size=1044464 +mercury.menu.iramfloat.no=in IROM +mercury.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +mercury.menu.iramfloat.yes=allowed in ISR +mercury.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM + ############################################################## nodemcu.name=NodeMCU 0.9 (ESP-12 Module) nodemcu.build.board=ESP8266_NODEMCU_ESP12 diff --git a/cores/esp8266/Esp-version.cpp b/cores/esp8266/Esp-version.cpp index c1ff61eb06..85485b2f73 100644 --- a/cores/esp8266/Esp-version.cpp +++ b/cores/esp8266/Esp-version.cpp @@ -20,10 +20,11 @@ #include #include -#include #include // LWIP_HASH_STR (lwip2) #include // BEARSSL_GIT short hash +#include + #define STRHELPER(x) #x #define STR(x) STRHELPER(x) // stringifier diff --git a/cores/esp8266/Esp.cpp b/cores/esp8266/Esp.cpp index 67719dcfe7..8a3ea32515 100644 --- a/cores/esp8266/Esp.cpp +++ b/cores/esp8266/Esp.cpp @@ -254,7 +254,7 @@ String EspClass::getCoreVersion() return String(core_release); } char buf[12]; - snprintf(buf, sizeof(buf), "%08x", core_version); + snprintf(buf, sizeof(buf), PSTR("%08x"), core_version); return String(buf); } diff --git a/cores/esp8266/LwipIntf.cpp b/cores/esp8266/LwipIntf.cpp index e142730df0..675063cd62 100644 --- a/cores/esp8266/LwipIntf.cpp +++ b/cores/esp8266/LwipIntf.cpp @@ -43,7 +43,7 @@ extern "C" // can return nullptr when STA is down // - Because WiFi is started in off mode at boot time, // wifi_station_set/get_hostname() is now no more used -// because setting hostname firt does not work anymore +// because setting hostname first does not work anymore // - wifi_station_hostname is overwritten by SDK when wifi is // woken up in WiFi::mode() // diff --git a/cores/esp8266/LwipIntfDev.h b/cores/esp8266/LwipIntfDev.h index daf63e935d..d69e2d73d8 100644 --- a/cores/esp8266/LwipIntfDev.h +++ b/cores/esp8266/LwipIntfDev.h @@ -83,15 +83,11 @@ class LwipIntfDev: public LwipIntf, public RawDev return &_netif; } - uint8_t* macAddress(uint8_t* mac) // WiFi lib way + uint8_t* macAddress(uint8_t* mac) { memcpy(mac, &_netif.hwaddr, 6); return mac; } - void MACAddress(uint8_t* mac) // Ethernet lib way - { - macAddress(mac); - } IPAddress localIP() const { return IPAddress(ip4_addr_get_u32(ip_2_ip4(&_netif.ip_addr))); @@ -104,15 +100,11 @@ class LwipIntfDev: public LwipIntf, public RawDev { return IPAddress(ip4_addr_get_u32(ip_2_ip4(&_netif.gw))); } - IPAddress dnsIP(int n = 0) const // WiFi lib way + IPAddress dnsIP(int n = 0) const { return IPAddress(dns_getserver(n)); } - IPAddress dnsServerIP() const // Ethernet lib way - { - return dnsIP(0); - } - void setDNS(IPAddress dns1, IPAddress dns2 = INADDR_ANY) // WiFi lib way + void setDNS(IPAddress dns1, IPAddress dns2 = INADDR_ANY) { if (dns1.isSet()) { @@ -123,10 +115,6 @@ class LwipIntfDev: public LwipIntf, public RawDev dns_setserver(1, dns2); } } - void setDnsServerIP(const IPAddress dnsIP) // Ethernet lib way - { - setDNS(dnsIP); - } // 1. Currently when no default is set, esp8266-Arduino uses the first // DHCP client interface receiving a valid address and gateway to @@ -361,9 +349,12 @@ boolean LwipIntfDev::begin(const uint8_t* macAddress, const uint16_t mtu template void LwipIntfDev::end() { - netif_remove(&_netif); - _started = false; - RawDev::end(); + if (_started) + { + netif_remove(&_netif); + _started = false; + RawDev::end(); + } } template diff --git a/cores/esp8266/StackThunk.cpp b/cores/esp8266/StackThunk.cpp index 0d2a8a3541..a0be352c7b 100644 --- a/cores/esp8266/StackThunk.cpp +++ b/cores/esp8266/StackThunk.cpp @@ -27,18 +27,26 @@ #include #include #include -#include "pgmspace.h" + #include "debug.h" #include "StackThunk.h" + +#include #include + #include #include extern "C" { +extern void optimistic_yield(uint32_t); + uint32_t *stack_thunk_ptr = NULL; uint32_t *stack_thunk_top = NULL; + uint32_t *stack_thunk_save = NULL; /* Saved A1 while in BearSSL */ +uint32_t *stack_thunk_yield_save = NULL; /* Saved A1 when yielding from within BearSSL */ + uint32_t stack_thunk_refcnt = 0; /* Largest stack usage seen in the wild at 6120 */ @@ -65,6 +73,7 @@ void stack_thunk_add_ref() } stack_thunk_top = stack_thunk_ptr + _stackSize - 1; stack_thunk_save = NULL; + stack_thunk_yield_save = NULL; stack_thunk_repaint(); } } @@ -82,6 +91,7 @@ void stack_thunk_del_ref() stack_thunk_ptr = NULL; stack_thunk_top = NULL; stack_thunk_save = NULL; + stack_thunk_yield_save = NULL; } } @@ -150,4 +160,40 @@ void stack_thunk_fatal_smashing() __stack_chk_fail(); } +/* Called within bearssl code instead of optimistic_yield(...) */ +void stack_thunk_yield(); +asm( + ".section .text.stack_thunk_yield,\"ax\",@progbits\n\t" + ".literal_position\n\t" + ".align 4\n\t" + ".global stack_thunk_yield\n\t" + ".type stack_thunk_yield, @function\n\t" + "\n" +"stack_thunk_yield:\n\t" +/* Keep the original caller */ + "addi a1, a1, -16\n\t" + "s32i.n a0, a1, 12\n\t" +/* Swap bearssl <-> cont stacks */ + "movi a2, stack_thunk_yield_save\n\t" + "s32i.n a1, a2, 0\n\t" + "movi a2, stack_thunk_save\n\t" +/* But, only when inside of bssl stack (saved a1 != 0) */ + "l32i.n a3, a2, 0\n\t" + "beqz a3, stack_thunk_yield_do_yield\n\t" + "l32i.n a1, a2, 0\n\t" +/* optimistic_yield(10000) without extra l32r */ +"stack_thunk_yield_do_yield:\n\t" + "movi a2, 0x10\n\t" + "addmi a2, a2, 0x2700\n\t" + "call0 optimistic_yield\n\t" +/* Swap bearssl <-> cont stacks, again */ + "movi a2, stack_thunk_yield_save\n\t" + "l32i.n a1, a2, 0\n\t" +/* Restore caller */ + "l32i.n a0, a1, 12\n\t" + "addi a1, a1, 16\n\t" + "ret.n\n\t" + ".size stack_thunk_yield, .-stack_thunk_yield\n\t" +); + } diff --git a/cores/esp8266/StackThunk.h b/cores/esp8266/StackThunk.h index e72e0efb9f..b7dd521e94 100644 --- a/cores/esp8266/StackThunk.h +++ b/cores/esp8266/StackThunk.h @@ -27,10 +27,14 @@ #ifndef _STACKTHUNK_H #define _STACKTHUNK_H +#include + #ifdef __cplusplus extern "C" { #endif +extern void stack_thunk_yield(void); + extern void stack_thunk_add_ref(); extern void stack_thunk_del_ref(); extern void stack_thunk_repaint(); @@ -78,9 +82,11 @@ thunk_"#fcnToThunk":\n\ call0 stack_thunk_fatal_smashing\n\ .L1"#fcnToThunk":\n\ movi a15, stack_thunk_save /* Restore A1(SP) */\n\ - l32i.n a1, a15, 0\n\ - l32i.n a15, a1, 8 /* Restore the saved registers */\n\ - l32i.n a0, a1, 12\n\ + l32i.n a1, a15, 0/* Restore A1(SP) */\n\ + movi a0, 0 /* Purge temporary storage */\n\ + s32i.n a0, a15, 0\n\ + l32i.n a15, a1, 8/* Restore A15 */\n\ + l32i.n a0, a1, 12/* Restore A0 */\n\ addi a1, a1, 16 /* Free up stack and return to caller */\n\ ret\n\ .size thunk_"#fcnToThunk", . - thunk_"#fcnToThunk"\n"); diff --git a/cores/esp8266/Stream.h b/cores/esp8266/Stream.h index 21f319ee6b..0706bec001 100644 --- a/cores/esp8266/Stream.h +++ b/cores/esp8266/Stream.h @@ -196,21 +196,25 @@ class Stream: public Print { // returns number of transferred bytes size_t sendAvailable (Stream* to) { return sendGeneric(to, -1, -1, oneShotMs::alwaysExpired); } size_t sendAvailable (Stream& to) { return sendAvailable(&to); } + size_t sendAvailable (Stream&& to) { return sendAvailable(&to); } // transfers data until timeout // returns number of transferred bytes size_t sendAll (Stream* to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, -1, -1, timeoutMs); } size_t sendAll (Stream& to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendAll(&to, timeoutMs); } + size_t sendAll (Stream&& to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendAll(&to, timeoutMs); } // transfers data until a char is encountered (the char is swallowed but not transferred) with timeout // returns number of transferred bytes size_t sendUntil (Stream* to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, -1, readUntilChar, timeoutMs); } size_t sendUntil (Stream& to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendUntil(&to, readUntilChar, timeoutMs); } + size_t sendUntil (Stream&& to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendUntil(&to, readUntilChar, timeoutMs); } // transfers data until requested size or timeout // returns number of transferred bytes size_t sendSize (Stream* to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, maxLen, -1, timeoutMs); } size_t sendSize (Stream& to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendSize(&to, maxLen, timeoutMs); } + size_t sendSize (Stream&& to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendSize(&to, maxLen, timeoutMs); } // remaining size (-1 by default = unknown) virtual ssize_t streamRemaining () { return -1; } diff --git a/cores/esp8266/StreamString.h b/cores/esp8266/StreamString.h index 2331a3c51a..dced5aee80 100644 --- a/cores/esp8266/StreamString.h +++ b/cores/esp8266/StreamString.h @@ -29,7 +29,7 @@ #include "WString.h" /////////////////////////////////////////////////////////////// -// S2Stream points to a String and makes it a Stream +// S2Stream ("String to Stream") points to a String and makes it a Stream // (it is also the helper for StreamString) class S2Stream: public Stream @@ -184,19 +184,18 @@ class S2Stream: public Stream return peekPointer < 0 ? string->length() : string->length() - peekPointer; } - // calling setConsume() will consume bytes as the stream is read - // (enabled by default) + // calling setConsume() will make the string consumed as the stream is read. + // (default behaviour) void setConsume() { peekPointer = -1; } - // Reading this stream will mark the string as read without consuming - // (not enabled by default) - // Calling resetPointer() resets the read state and allows rereading. - void resetPointer(int pointer = 0) + // Calling resetPointer() resets the read cursor and allows rereading. + // (this is the opposite of default mode set by setConsume()) + void resetPointer(size_t pointer = 0) { - peekPointer = pointer; + peekPointer = std::min(pointer, (size_t)string->length()); } protected: @@ -204,6 +203,7 @@ class S2Stream: public Stream int peekPointer; // -1:String is consumed / >=0:resettable pointer }; +/////////////////////////////////////////////////////////////// // StreamString is a S2Stream holding the String class StreamString: public String, public S2Stream diff --git a/cores/esp8266/Updater.cpp b/cores/esp8266/Updater.cpp index 91f2a9e6ae..ef79a5cbf3 100644 --- a/cores/esp8266/Updater.cpp +++ b/cores/esp8266/Updater.cpp @@ -70,6 +70,7 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) { #ifdef DEBUG_UPDATER DEBUG_UPDATER.println(F("[begin] already running")); #endif + _setError(UPDATE_ERROR_RUNNING_ALREADY); return false; } @@ -86,7 +87,7 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) { _setError(UPDATE_ERROR_BOOTSTRAP); return false; } - + #ifdef DEBUG_UPDATER if (command == U_FS) { DEBUG_UPDATER.println(F("[begin] Update Filesystem.")); @@ -133,7 +134,7 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) { //make sure that the size of both sketches is less than the total space (updateEndAddress) if(updateStartAddress < currentSketchSize) { - _setError(UPDATE_ERROR_SPACE); + _setError(UPDATE_ERROR_SPACE); return false; } } @@ -162,6 +163,7 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) { #ifdef DEBUG_UPDATER DEBUG_UPDATER.println(F("[begin] Unknown update command.")); #endif + _setError(UPDATE_ERROR_UNKNOWN_COMMAND); return false; } @@ -282,7 +284,7 @@ bool UpdaterClass::end(bool evenIfRemaining){ _hash->begin(); for (uint32_t offset = 0; offset < binSize; offset += sizeof(buff)) { auto len = std::min(sizeof(buff), binSize - offset); - ESP.flashRead(_startAddress + offset, reinterpret_cast(&buff[0]), len); + ESP.flashRead(_startAddress + offset, buff, len); _hash->add(buff, len); } _hash->end(); @@ -404,7 +406,7 @@ bool UpdaterClass::_writeBuffer(){ modifyFlashMode = true; } } - + if (eraseResult) { if(!_async) yield(); writeResult = ESP.flashWrite(_currentAddress, _buffer, _bufferLen); @@ -488,7 +490,7 @@ bool UpdaterClass::_verifyEnd() { uint8_t buf[4] __attribute__((aligned(4))); if(!ESP.flashRead(_startAddress, (uint32_t *) &buf[0], 4)) { _currentAddress = (_startAddress); - _setError(UPDATE_ERROR_READ); + _setError(UPDATE_ERROR_READ); return false; } @@ -500,7 +502,7 @@ bool UpdaterClass::_verifyEnd() { return true; } else if (buf[0] != 0xE9) { _currentAddress = (_startAddress); - _setError(UPDATE_ERROR_MAGIC_BYTE); + _setError(UPDATE_ERROR_MAGIC_BYTE); return false; } @@ -512,7 +514,7 @@ bool UpdaterClass::_verifyEnd() { // check if new bin fits to SPI flash if(bin_flash_size > ESP.getFlashChipRealSize()) { _currentAddress = (_startAddress); - _setError(UPDATE_ERROR_NEW_FLASH_CONFIG); + _setError(UPDATE_ERROR_NEW_FLASH_CONFIG); return false; } #endif @@ -649,6 +651,12 @@ String UpdaterClass::getErrorString() const { case UPDATE_ERROR_OOM: out = F("Out of memory"); break; + case UPDATE_ERROR_RUNNING_ALREADY: + out = F("Update already running"); + break; + case UPDATE_ERROR_UNKNOWN_COMMAND: + out = F("Unknown update command"); + break; default: out = F("UNKNOWN"); break; diff --git a/cores/esp8266/Updater.h b/cores/esp8266/Updater.h index ad652d3806..7ee1d28311 100644 --- a/cores/esp8266/Updater.h +++ b/cores/esp8266/Updater.h @@ -21,6 +21,8 @@ #define UPDATE_ERROR_SIGN (12) #define UPDATE_ERROR_NO_DATA (13) #define UPDATE_ERROR_OOM (14) +#define UPDATE_ERROR_RUNNING_ALREADY (15) +#define UPDATE_ERROR_UNKNOWN_COMMAND (16) #define U_FLASH 0 #define U_FS 100 @@ -55,7 +57,7 @@ class UpdaterClass { using THandlerFunction_Progress = std::function; using THandlerFunction_Error = std::function; using THandlerFunction = std::function; - + UpdaterClass(); ~UpdaterClass(); @@ -69,7 +71,7 @@ class UpdaterClass { bool begin(size_t size, int command = U_FLASH, int ledPin = -1, uint8_t ledOn = LOW); /* - Run Updater from asynchronous callbacs + Run Updater from asynchronous callbacks */ void runAsync(bool async){ _async = async; } @@ -216,7 +218,7 @@ class UpdaterClass { bool _verifyHeader(uint8_t data); bool _verifyEnd(); - void _setError(int error); + void _setError(int error); bool _async = false; uint8_t _error = 0; diff --git a/cores/esp8266/WString.h b/cores/esp8266/WString.h index 47a9177534..1a2aebb298 100644 --- a/cores/esp8266/WString.h +++ b/cores/esp8266/WString.h @@ -33,7 +33,7 @@ #include #include -// an abstract class used as a means to proide a unique pointer type +// an abstract class used as a means to provide a unique pointer type // but really has no body class __FlashStringHelper; #define FPSTR(pstr_pointer) (reinterpret_cast(pstr_pointer)) @@ -204,7 +204,7 @@ class String { bool concat(double num); // if there's not enough memory for the concatenated value, the string - // will be left unchanged (but this isn't signalled in any way) + // will be left unchanged (but this isn't signaled in any way) template String &operator +=(const T &rhs) { concat(rhs); @@ -343,7 +343,7 @@ class String { char *wbuffer() { return const_cast(buffer()); } // Writable version of buffer // concatenation is done via non-member functions - // make sure we still have access to internal methods, since we optimize based on capacity of both sides and want to manipulate internal buffers directly + // make sure we still have access to internal methods, since we optimize based on the capacity of both sides and want to manipulate internal buffers directly friend String operator +(const String &lhs, String &&rhs); friend String operator +(String &&lhs, String &&rhs); friend String operator +(char lhs, String &&rhs); diff --git a/cores/esp8266/cont.S b/cores/esp8266/cont.S index 4832b39170..ee049d46f5 100644 --- a/cores/esp8266/cont.S +++ b/cores/esp8266/cont.S @@ -26,8 +26,14 @@ cont_suspend: /* a1: sp */ /* a2: void* cont_ctx */ - /* adjust stack and save registers */ + /* adjust stack */ addi a1, a1, -24 + + /* make sure that a1 points after cont_ctx.stack[] */ + addi a4, a2, 32 + bltu a1, a4, cont_overflow + + /* save registers */ s32i a12, a1, 0 s32i a13, a1, 4 s32i a14, a1, 8 @@ -47,6 +53,11 @@ cont_suspend: l32i a1, a2, 4 jx a0 +cont_overflow: + mov.n a3, a1 + movi a4, __stack_overflow + jx a4 + cont_continue: l32i a12, a1, 0 l32i a13, a1, 4 @@ -113,7 +124,7 @@ cont_run: bnez a4, cont_resume /* else */ /* set new stack*/ - l32i a1, a2, 16; + l32i a1, a2, 16 /* goto pfn */ movi a2, cont_wrapper jx a2 @@ -121,12 +132,15 @@ cont_run: cont_resume: /* a1 <- cont_ctx.sp_suspend */ l32i a1, a2, 12 + /* make sure that a1 points after cont_ctx.stack[] */ + addi a5, a2, 32 + bltu a1, a5, cont_overflow /* reset yield flag, 0 -> cont_ctx.pc_suspend */ movi a3, 0 s32i a3, a2, 8 /* jump to saved cont_ctx.pc_suspend */ movi a0, cont_ret - jx a4 + jx a4 cont_norm: /* calculate pointer to cont_ctx.struct_start from sp */ diff --git a/cores/esp8266/cont.h b/cores/esp8266/cont.h index ba6d432a88..4c9578851b 100644 --- a/cores/esp8266/cont.h +++ b/cores/esp8266/cont.h @@ -22,11 +22,16 @@ #define CONT_H_ #include +#include #ifndef CONT_STACKSIZE #define CONT_STACKSIZE 4096 #endif +#ifndef CONT_STACKGUARD +#define CONT_STACKGUARD 0xfeefeffe +#endif + #ifdef __cplusplus extern "C" { #endif @@ -62,8 +67,11 @@ void cont_run(cont_t*, void (*pfn)(void)); // execution state (registers and stack) void cont_suspend(cont_t*); +// Check that cont resume state is valid. Immediately panics on failure. +void cont_check_overflow(cont_t*); + // Check guard bytes around the stack. Immediately panics on failure. -void cont_check(cont_t*); +void cont_check_guard(cont_t*); // Go through stack and check how many bytes are most probably still unchanged // and thus weren't used by the user code. i.e. that stack space is free. (high water mark) @@ -78,7 +86,6 @@ bool cont_can_suspend(cont_t* cont); // free, running the routine, then checking the max free void cont_repaint_stack(cont_t *cont); - #ifdef __cplusplus } #endif diff --git a/cores/esp8266/cont_util.cpp b/cores/esp8266/cont_util.cpp index 746dcccbfd..6c49a38fcf 100644 --- a/cores/esp8266/cont_util.cpp +++ b/cores/esp8266/cont_util.cpp @@ -23,38 +23,45 @@ #include #include -#include "cont.h" +#include "core_esp8266_features.h" #include "debug.h" +#include "cont.h" + extern "C" { -static constexpr unsigned int CONT_STACKGUARD { 0xfeefeffe }; +static constexpr uint32_t CONT_STACKSIZE_U32 { sizeof(cont_t::stack) / sizeof(*cont_t::stack) }; void cont_init(cont_t* cont) { memset(cont, 0, sizeof(cont_t)); cont->stack_guard1 = CONT_STACKGUARD; cont->stack_guard2 = CONT_STACKGUARD; - cont->stack_end = cont->stack + (sizeof(cont->stack) / 4); + cont->stack_end = &cont->stack[0] + CONT_STACKSIZE_U32; cont->struct_start = (unsigned*) cont; // fill stack with magic values to check high water mark - for(int pos = 0; pos < (int)(sizeof(cont->stack) / 4); pos++) + for(int pos = 0; pos < (int)(CONT_STACKSIZE_U32); pos++) { cont->stack[pos] = CONT_STACKGUARD; } } -void IRAM_ATTR cont_check(cont_t* cont) { - if ((cont->stack_guard1 == CONT_STACKGUARD) - && (cont->stack_guard2 == CONT_STACKGUARD)) +void IRAM_ATTR cont_check_guard(cont_t* cont) { + if ((cont->stack_guard1 != CONT_STACKGUARD) + || (cont->stack_guard2 != CONT_STACKGUARD)) { - return; + __stack_chk_fail(); + __builtin_unreachable(); } +} - __stack_chk_fail(); - __builtin_unreachable(); +void IRAM_ATTR cont_check_overflow(cont_t* cont) { + if (cont->sp_suspend && (cont->sp_suspend < &cont->stack[0])) { + __stack_overflow(cont, cont->sp_suspend); + __builtin_unreachable(); + } } // No need for this to be in IRAM, not expected to be IRQ called diff --git a/cores/esp8266/core_esp8266_flash_quirks.cpp b/cores/esp8266/core_esp8266_flash_quirks.cpp index 7128fcfe2d..2ae3a39050 100644 --- a/cores/esp8266/core_esp8266_flash_quirks.cpp +++ b/cores/esp8266/core_esp8266_flash_quirks.cpp @@ -66,12 +66,13 @@ void initFlashQuirks() { newSR3=SR3; if (get_flash_mhz()>26) { // >26Mhz? // Set the output drive to 100% + // These definitions are for the XM25QH32B part. On a XM25QH32C + // part, the XM25QH32B's 100% is C's 25% driver strength. newSR3 &= ~(SPI_FLASH_SR3_XMC_DRV_MASK << SPI_FLASH_SR3_XMC_DRV_S); newSR3 |= (SPI_FLASH_SR3_XMC_DRV_100 << SPI_FLASH_SR3_XMC_DRV_S); } if (newSR3 != SR3) { // only write if changed - if (SPI0Command(SPI_FLASH_CMD_WEVSR,NULL,0,0)==SPI_RESULT_OK) // write enable volatile SR - SPI0Command(SPI_FLASH_CMD_WSR3,&newSR3,8,0); // write to SR3 + SPI0Command(SPI_FLASH_CMD_WSR3,&newSR3,8,0,SPI_FLASH_CMD_WEVSR); // write to SR3, use write enable volatile prefix SPI0Command(SPI_FLASH_CMD_WRDI,NULL,0,0); // write disable - probably not needed } } diff --git a/cores/esp8266/core_esp8266_main.cpp b/cores/esp8266/core_esp8266_main.cpp index d0309cc71f..d7f14b02c8 100644 --- a/cores/esp8266/core_esp8266_main.cpp +++ b/cores/esp8266/core_esp8266_main.cpp @@ -24,6 +24,7 @@ //#define CONT_STACKSIZE 4096 #include +#include #include #include "Schedule.h" @@ -261,7 +262,7 @@ static void loop_wrapper() { } loop(); loop_end(); - cont_check(g_pcont); + cont_check_guard(g_pcont); if (serialEventRun) { serialEventRun(); } diff --git a/cores/esp8266/core_esp8266_noniso.cpp b/cores/esp8266/core_esp8266_noniso.cpp index f15fdc0860..fb5d8f573a 100644 --- a/cores/esp8266/core_esp8266_noniso.cpp +++ b/cores/esp8266/core_esp8266_noniso.cpp @@ -28,19 +28,12 @@ #include #include #include + #include "stdlib_noniso.h" extern "C" { -char* ltoa(long value, char* result, int base) { - return itoa((int)value, result, base); -} - -char* ultoa(unsigned long value, char* result, int base) { - return utoa((unsigned int)value, result, base); -} - -char * dtostrf(double number, signed char width, unsigned char prec, char *s) { +char* dtostrf(double number, signed char width, unsigned char prec, char *s) noexcept { bool negative = false; if (isnan(number)) { @@ -125,7 +118,7 @@ char * dtostrf(double number, signed char width, unsigned char prec, char *s) { */ const char* strrstr(const char*__restrict p_pcString, - const char*__restrict p_pcPattern) + const char*__restrict p_pcPattern) noexcept { const char* pcResult = 0; @@ -149,4 +142,4 @@ const char* strrstr(const char*__restrict p_pcString, return pcResult; } -}; +} // extern "C" diff --git a/cores/esp8266/core_esp8266_postmortem.cpp b/cores/esp8266/core_esp8266_postmortem.cpp index 7fff9d2ff7..95844534e8 100644 --- a/cores/esp8266/core_esp8266_postmortem.cpp +++ b/cores/esp8266/core_esp8266_postmortem.cpp @@ -48,7 +48,7 @@ static const char* s_unhandled_exception = NULL; // Common way to notify about where the stack smashing happened // (but, **only** if caller uses our handler function) -static uint32_t s_stacksmash_addr = 0; +static uint32_t s_stack_chk_addr = 0; void abort() __attribute__((noreturn)); static void uart_write_char_d(char c); @@ -59,6 +59,7 @@ static void print_stack(uint32_t start, uint32_t end); // using numbers different from "REASON_" in user_interface.h (=0..6) enum rst_reason_sw { + REASON_USER_STACK_OVERFLOW = 252, REASON_USER_STACK_SMASH = 253, REASON_USER_SWEXCEPTION_RST = 254 }; @@ -188,7 +189,7 @@ static void postmortem_report(uint32_t sp_dump) { } else if (rst_info.reason == REASON_SOFT_WDT_RST) { ets_printf_P(PSTR("\nSoft WDT reset")); - const char infinite_loop[] = { 0x06, 0xff, 0xff }; // loop: j loop + const uint8_t infinite_loop[] = { 0x06, 0xff, 0xff }; // loop: j loop if (is_pc_valid(rst_info.epc1) && 0 == memcmp_P(infinite_loop, (PGM_VOID_P)rst_info.epc1, 3u)) { // The SDK is riddled with these. They are usually preceded by an ets_printf. ets_printf_P(PSTR(" - deliberate infinite loop detected")); @@ -198,17 +199,23 @@ static void postmortem_report(uint32_t sp_dump) { rst_info.exccause, /* Address executing at time of Soft WDT level-1 interrupt */ rst_info.epc1, 0, 0, 0, 0); } else if (rst_info.reason == REASON_USER_STACK_SMASH) { - ets_printf_P(PSTR("\nStack smashing detected.\n")); - ets_printf_P(PSTR("\nException (%d):\nepc1=0x%08x epc2=0x%08x epc3=0x%08x excvaddr=0x%08x depc=0x%08x\n"), - 5 /* Alloca exception, closest thing to stack fault*/, s_stacksmash_addr, 0, 0, 0, 0); - } + ets_printf_P(PSTR("\nStack smashing detected at 0x%08x\n"), s_stack_chk_addr); + } + else if (rst_info.reason == REASON_USER_STACK_OVERFLOW) { + ets_printf_P(PSTR("\nStack overflow detected\n")); + } else { ets_printf_P(PSTR("\nGeneric Reset\n")); } - uint32_t cont_stack_start = (uint32_t) &(g_pcont->stack); - uint32_t cont_stack_end = (uint32_t) g_pcont->stack_end; - uint32_t stack_end; + uint32_t cont_stack_start; + if (rst_info.reason == REASON_USER_STACK_SMASH) { + cont_stack_start = s_stack_chk_addr; + } else { + cont_stack_start = (uint32_t) (&g_pcont->stack[0]); + } + + uint32_t cont_stack_end = cont_stack_start + CONT_STACKSIZE; // amount of stack taken by interrupt or exception handler // and everything up to __wrap_system_restart_local @@ -249,15 +256,21 @@ static void postmortem_report(uint32_t sp_dump) { sp_dump = stack_thunk_get_cont_sp(); } - if (sp_dump > cont_stack_start && sp_dump < cont_stack_end) { + uint32_t stack_end; + + // above and inside of cont, dump from the sp to the bottom of the stack + if ((rst_info.reason == REASON_USER_STACK_OVERFLOW) + || ((sp_dump > cont_stack_start) && (sp_dump < cont_stack_end))) + { ets_printf_P(PSTR("\nctx: cont\n")); stack_end = cont_stack_end; } + // in system, reposition to a known address + // it's actually 0x3ffffff0, but the stuff below ets_run + // is likely not really relevant to the crash else { ets_printf_P(PSTR("\nctx: sys\n")); stack_end = 0x3fffffb0; - // it's actually 0x3ffffff0, but the stuff below ets_run - // is likely not really relevant to the crash } ets_printf_P(PSTR("sp: %08x end: %08x offset: %04x\n"), sp_dump, stack_end, offset); @@ -296,11 +309,20 @@ static void print_stack(uint32_t start, uint32_t end) { for (uint32_t pos = start; pos < end; pos += 0x10) { uint32_t* values = (uint32_t*)(pos); + // avoid printing irrelevant data + if ((values[0] == CONT_STACKGUARD) + && (values[0] == values[1]) + && (values[1] == values[2]) + && (values[2] == values[3])) + { + continue; + } + // rough indicator: stack frames usually have SP saved as the second word - bool looksLikeStackFrame = (values[2] == pos + 0x10); + const bool looksLikeStackFrame = (values[2] == pos + 0x10); ets_printf_P(PSTR("%08x: %08x %08x %08x %08x %c\n"), - pos, values[0], values[1], values[2], values[3], (looksLikeStackFrame)?'<':' '); + pos, values[0], values[1], values[2], values[3], (looksLikeStackFrame) ? '<' : ' '); } } @@ -370,7 +392,7 @@ void __panic_func(const char* file, int line, const char* func) { uintptr_t __stack_chk_guard = 0x08675309 ^ RANDOM_REG32; void __stack_chk_fail(void) { s_user_reset_reason = REASON_USER_STACK_SMASH; - s_stacksmash_addr = (uint32_t)__builtin_return_address(0); + s_stack_chk_addr = (uint32_t)__builtin_return_address(0); if (gdb_present()) __asm__ __volatile__ ("syscall"); // triggers GDB when enabled @@ -382,4 +404,16 @@ void __stack_chk_fail(void) { __builtin_unreachable(); // never reached, needed to satisfy "noreturn" attribute } +void __stack_overflow(cont_t* cont, uint32_t* sp) { + s_user_reset_reason = REASON_USER_STACK_OVERFLOW; + s_stack_chk_addr = (uint32_t)&cont->stack[0]; + + if (gdb_present()) + __asm__ __volatile__ ("syscall"); // triggers GDB when enabled + + postmortem_report((uint32_t)sp); + + __builtin_unreachable(); // never reached, needed to satisfy "noreturn" attribute +} + } // extern "C" diff --git a/cores/esp8266/core_esp8266_spi_utils.cpp b/cores/esp8266/core_esp8266_spi_utils.cpp index 5f2ea25f92..cd5e153c27 100644 --- a/cores/esp8266/core_esp8266_spi_utils.cpp +++ b/cores/esp8266/core_esp8266_spi_utils.cpp @@ -32,6 +32,7 @@ #include "core_esp8266_features.h" #include "spi_utils.h" +#include "spi_flash_defs.h" extern "C" uint32_t Wait_SPI_Idle(SpiFlashChip *fc); @@ -51,12 +52,12 @@ namespace experimental { static SpiOpResult PRECACHE_ATTR _SPICommand(volatile uint32_t spiIfNum, uint32_t spic,uint32_t spiu,uint32_t spiu1,uint32_t spiu2, - uint32_t *data,uint32_t writeWords,uint32_t readWords) + uint32_t *data,uint32_t writeWords,uint32_t readWords, uint32_t pre_cmd) { if (spiIfNum>1) return SPI_RESULT_ERR; - // force SPI register access via base+offset. + // force SPI register access via base+offset. // Prevents loading individual address constants from flash. uint32_t *spibase = (uint32_t*)(spiIfNum ? &(SPI1CMD) : &(SPI0CMD)); #define SPIREG(reg) (*((volatile uint32_t *)(spibase+(&(reg) - &(SPI0CMD))))) @@ -65,6 +66,7 @@ _SPICommand(volatile uint32_t spiIfNum, // Everything defined here must be volatile or the optimizer can // treat them as constants, resulting in the flash reads we're // trying to avoid + SpiFlashOpResult (* volatile SPI_write_enablep)(SpiFlashChip *) = SPI_write_enable; uint32_t (* volatile Wait_SPI_Idlep)(SpiFlashChip *) = Wait_SPI_Idle; volatile SpiFlashChip *fchip=flashchip; volatile uint32_t spicmdusr=SPICMDUSR; @@ -77,15 +79,30 @@ _SPICommand(volatile uint32_t spiIfNum, PRECACHE_START(); Wait_SPI_Idlep((SpiFlashChip *)fchip); } - + // preserve essential controller state such as incoming/outgoing // data lengths and IO mode. uint32_t oldSPI0U = SPIREG(SPI0U); uint32_t oldSPI0U2= SPIREG(SPI0U2); uint32_t oldSPI0C = SPIREG(SPI0C); - //SPI0S &= ~(SPISE|SPISBE|SPISSE|SPISCD); SPIREG(SPI0C) = spic; + + if (SPI_FLASH_CMD_WREN == pre_cmd) { + // See SPI_write_enable comments in esp8266_undocumented.h + SPI_write_enablep((SpiFlashChip *)fchip); + } else + if (pre_cmd) { + // Send prefix cmd w/o data - sends 8 bits. eg. Volatile SR Write Enable, 0x50 + SPIREG(SPI0U) = (spiu & ~(SPIUMOSI|SPIUMISO)); + SPIREG(SPI0U1) = 0; + SPIREG(SPI0U2) = (spiu2 & ~0xFFFFu) | pre_cmd; + + SPIREG(SPI0CMD) = spicmdusr; //Send cmd + while ((SPIREG(SPI0CMD) & spicmdusr)); + } + + //SPI0S &= ~(SPISE|SPISBE|SPISSE|SPISCD); SPIREG(SPI0U) = spiu; SPIREG(SPI0U1)= spiu1; SPIREG(SPI0U2)= spiu2; @@ -117,11 +134,22 @@ _SPICommand(volatile uint32_t spiIfNum, SPIREG(SPI0U) = oldSPI0U; SPIREG(SPI0U2)= oldSPI0U2; SPIREG(SPI0C) = oldSPI0C; - - PRECACHE_END(); + if (!spiIfNum) { + // w/o a call to Wait_SPI_Idlep, 'Exception 0' or other exceptions (saw + // 28) may occur later after returning to iCache code. This issue was + // observed with non-volatile status register writes. + // + // My guess is: Returning too soon to uncached iCache executable space. An + // iCache read may not complete properly because the Flash or SPI + // interface is still busy with the last write operation. In such a case, + // I expect new reads from iROM to result in zeros. This would explain + // the Exception 0 for code, and Exception 20, 28, and 29 where a literal + // was misread as 0 and then used as a pointer. + Wait_SPI_Idlep((SpiFlashChip *)fchip); xt_wsr_ps(saved_ps); } + PRECACHE_END(); return (timeout>0 ? SPI_RESULT_OK : SPI_RESULT_TIMEOUT); } @@ -139,12 +167,37 @@ _SPICommand(volatile uint32_t spiIfNum, * miso_bits * Number of bits to read from the SPI bus after the outgoing * data has been sent. + * pre_cmd + * A few SPI Flash commands require enable commands to immediately preceed + * them. Since two calls to SPI0Command from ICACHE memory most likely would + * be separated by SPI Flash read request for iCache, use this option to + * supply a prefix command, 8-bits w/o read or write data. + * + * Case in point from the GD25Q32E datasheet: "The Write Enable for Volatile + * Status Register command must be issued prior to a Write Status Register + * command and any other commands can’t be inserted between them." * * Note: This code has only been tested with SPI bus 0, but should work * equally well with other buses. The ESP8266 has bus 0 and 1, * newer chips may have more one day. + * + * Supplemental Notes: + * + * SPI Bus wire view: Think of *data as an array of bytes, byte[0] goes out + * first with the most significant bit shifted out first and so on. When + * thinking of the data as an array of 32bit-words, the least significant byte + * of the first 32bit-word goes out first on the SPI bus with the most + * significant bit of that byte shifted out first onto the wire. + * + * When presenting a 3 or 4-byte address, the byte order will need to be + * reversed. Don't overthink it. For a 3-byte address, view *data as a byte + * array and set the first 3-bytes to the address. eg. byteData[0] MSB, + * byteData[1] middle, and byteData[2] LSB. + * + * When sending a fractional byte, fill in the most significant bit positions + * of the byte first. */ -SpiOpResult SPI0Command(uint8_t cmd, uint32_t *data, uint32_t mosi_bits, uint32_t miso_bits) { +SpiOpResult SPI0Command(uint8_t cmd, uint32_t *data, uint32_t mosi_bits, uint32_t miso_bits, uint32_t pre_cmd) { if (mosi_bits>(64*8)) return SPI_RESULT_ERR; if (miso_bits>(64*8)) @@ -159,8 +212,16 @@ SpiOpResult SPI0Command(uint8_t cmd, uint32_t *data, uint32_t mosi_bits, uint32_ if (miso_bits % 32 != 0) miso_words++; + // Use SPI_CS_SETUP to add time for #CS to settle (ringing) before SPI CLK + // begins. The BootROM does not do this; however, RTOS SDK and NONOS SDK do + // as part of flash init/configuration. + // + // One SPI bus clock cycle time inserted between #CS active and the 1st SPI + // bus clock cycle. The number of clock cycles is in SPI_CNTRL2 + // SPI_SETUP_TIME, which defaults to 1. + // // Select user defined command mode in the controller - uint32_t spiu=SPIUCOMMAND; //SPI_USR_COMMAND + uint32_t spiu=SPIUCOMMAND | SPIUCSSETUP; //SPI_USR_COMMAND | SPI_CS_SETUP // Set the command byte to send uint32_t spiu2 = ((7 & SPIMCOMMAND)<> (miso_bits % 8u)) & 0xFFu) << whole_byte_bits; + } + data[miso_bits/32u] &= mask; } } return rc; diff --git a/cores/esp8266/debug.h b/cores/esp8266/debug.h index 0e30000c53..437479748c 100644 --- a/cores/esp8266/debug.h +++ b/cores/esp8266/debug.h @@ -4,6 +4,8 @@ #include #include +#include "cont.h" + #define _DEBUG_LEAF_FUNCTION(...) __asm__ __volatile__("" ::: "a0", "memory") #ifdef DEBUG_ESP_CORE @@ -32,6 +34,7 @@ extern "C" { #endif void __stack_chk_fail(void) __attribute__((noreturn)); + void __stack_overflow(cont_t*, uint32_t*) __attribute__((noreturn)); void __unhandled_exception(const char* str) __attribute__((noreturn)); void __panic_func(const char* file, int line, const char* func) __attribute__((noreturn)); #define panic() __panic_func(PSTR(__FILE__), __LINE__, __func__) diff --git a/cores/esp8266/esp8266_undocumented.h b/cores/esp8266/esp8266_undocumented.h index 5e68e53fa1..d7924333a8 100644 --- a/cores/esp8266/esp8266_undocumented.h +++ b/cores/esp8266/esp8266_undocumented.h @@ -241,6 +241,17 @@ extern fn_c_exception_handler_t _xtos_c_handler_table[XCHAL_EXCCAUSE_NUM]; extern fn_c_exception_handler_t _xtos_set_exception_handler(int cause, fn_c_exception_handler_t fn); #endif +/* + BootROM function that sends the SPI Flash "Write Enable" command, 0x06. + The function internally calls Wait_SPI_Idle before enabling. + Polls status register forever waiting for WEL bit to set. + This function always returns 0; however, most examples test for 0. + + Every function I find that needs WEL set, call this function. I suspect the + waiting for the WEL bit to set is a Flash chip anomaly workaround. +*/ +extern SpiFlashOpResult SPI_write_enable(SpiFlashChip *fc); + extern uint32_t Wait_SPI_Idle(SpiFlashChip *fc); extern void Cache_Read_Disable(); extern int32_t system_func1(uint32_t); diff --git a/cores/esp8266/spi_utils.h b/cores/esp8266/spi_utils.h index bf0928f288..181554a55a 100644 --- a/cores/esp8266/spi_utils.h +++ b/cores/esp8266/spi_utils.h @@ -35,7 +35,7 @@ typedef enum { SPI_RESULT_TIMEOUT } SpiOpResult; -SpiOpResult SPI0Command(uint8_t cmd, uint32_t *data, uint32_t mosi_bits, uint32_t miso_bits); +SpiOpResult SPI0Command(uint8_t cmd, uint32_t *data, uint32_t mosi_bits, uint32_t miso_bits, uint32_t pre_cmd=0); } #ifdef __cplusplus diff --git a/cores/esp8266/stdlib_noniso.cpp b/cores/esp8266/stdlib_noniso.cpp index 693dfda850..4fed9b615d 100644 --- a/cores/esp8266/stdlib_noniso.cpp +++ b/cores/esp8266/stdlib_noniso.cpp @@ -21,11 +21,13 @@ #include "stdlib_noniso.h" +extern "C" { + // ulltoa() is slower than std::to_char() (1.6 times) // but is smaller by ~800B/flash and ~250B/rodata // ulltoa fills str backwards and can return a pointer different from str -char* ulltoa(unsigned long long val, char* str, int slen, unsigned int radix) +char* ulltoa(unsigned long long val, char* str, int slen, unsigned int radix) noexcept { str += --slen; *str = 0; @@ -39,7 +41,7 @@ char* ulltoa(unsigned long long val, char* str, int slen, unsigned int radix) } // lltoa fills str backwards and can return a pointer different from str -char* lltoa (long long val, char* str, int slen, unsigned int radix) +char* lltoa(long long val, char* str, int slen, unsigned int radix) noexcept { bool neg; if (val < 0) @@ -60,3 +62,13 @@ char* lltoa (long long val, char* str, int slen, unsigned int radix) } return ret; } + +char* ltoa(long value, char* result, int base) noexcept { + return itoa((int)value, result, base); +} + +char* ultoa(unsigned long value, char* result, int base) noexcept { + return utoa((unsigned int)value, result, base); +} + +} // extern "C" diff --git a/cores/esp8266/stdlib_noniso.h b/cores/esp8266/stdlib_noniso.h index f86f78befc..0c8c488ed1 100644 --- a/cores/esp8266/stdlib_noniso.h +++ b/cores/esp8266/stdlib_noniso.h @@ -22,38 +22,35 @@ #ifndef STDLIB_NONISO_H #define STDLIB_NONISO_H +#include + #ifdef __cplusplus -extern "C"{ +extern "C" { #endif -int atoi(const char *s); - -long atol(const char* s); - -double atof(const char* s); - -char* itoa (int val, char *s, int radix); - -char* ltoa (long val, char *s, int radix); +#ifdef __cplusplus +#define __STDLIB_NONISO_NOEXCEPT noexcept +#else +#define __STDLIB_NONISO_NOEXCEPT +#endif -char* lltoa (long long val, char* str, int slen, unsigned int radix); +char* ltoa (long val, char *s, int radix) __STDLIB_NONISO_NOEXCEPT; -char* utoa (unsigned int val, char *s, int radix); +char* lltoa (long long val, char* str, int slen, unsigned int radix) __STDLIB_NONISO_NOEXCEPT; -char* ultoa (unsigned long val, char *s, int radix); +char* ultoa (unsigned long val, char *s, int radix) __STDLIB_NONISO_NOEXCEPT; -char* ulltoa (unsigned long long val, char* str, int slen, unsigned int radix); +char* ulltoa (unsigned long long val, char* str, int slen, unsigned int radix) __STDLIB_NONISO_NOEXCEPT; -char* dtostrf (double val, signed char width, unsigned char prec, char *s); +char* dtostrf (double val, signed char width, unsigned char prec, char *s) __STDLIB_NONISO_NOEXCEPT; -void reverse(char* begin, char* end); +const char* strrstr (const char*__restrict p_pcString, + const char*__restrict p_pcPattern) __STDLIB_NONISO_NOEXCEPT; -const char* strrstr(const char*__restrict p_pcString, - const char*__restrict p_pcPattern); +#undef __STDLIB_NONISO_NOEXCEPT #ifdef __cplusplus } // extern "C" #endif - #endif diff --git a/doc/Makefile b/doc/Makefile index 36b4923488..61d3e40b3c 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -2,7 +2,7 @@ # # You can set these variables from the command line. -SPHINXOPTS = +SPHINXOPTS = --fail-on-warning --nitpicky SPHINXBUILD = sphinx-build SPHINXPROJ = ESP8266ArduinoCore SOURCEDIR = . @@ -17,4 +17,4 @@ help: # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/doc/boards.rst b/doc/boards.rst index c8cd3d6bdb..8977c5b927 100644 --- a/doc/boards.rst +++ b/doc/boards.rst @@ -39,25 +39,30 @@ Minimal Hardware Setup for Bootloading and Usage +-----------------+------------+------------------+ | GND | | GND | +-----------------+------------+------------------+ -| TX or GPIO2\* | | RX | +| TX or GPIO2 | | | +| [#tx_or_gpio2]_ | RX | | +-----------------+------------+------------------+ | RX | | TX | +-----------------+------------+------------------+ | GPIO0 | PullUp | DTR | +-----------------+------------+------------------+ -| Reset\* | PullUp | RTS | +| Reset | | | +| [#reset]_ | PullUp | RTS | +-----------------+------------+------------------+ -| GPIO15\* | PullDown | | +| GPIO15 | | | +| [#gpio15]_ | PullDown | | +-----------------+------------+------------------+ -| CH\_PD | PullUp | | +| CH\_PD | | | +| [#ch_pd]_ | PullUp | | +-----------------+------------+------------------+ -- Note -- GPIO15 is also named MTDO -- Reset is also named RSBT or REST (adding PullUp improves the +.. rubric:: Notes + +.. [#tx_or_gpio2] GPIO15 is also named MTDO +.. [#reset] Reset is also named RSBT or REST (adding PullUp improves the stability of the module) -- GPIO2 is alternative TX for the boot loader mode -- **Directly connecting a pin to VCC or GND is not a substitute for a +.. [#gpio15] GPIO2 is alternative TX for the boot loader mode +.. [#ch_pd] **Directly connecting a pin to VCC or GND is not a substitute for a PullUp or PullDown resistor, doing this can break upload management and the serial console, instability has also been noted in some cases.** @@ -88,15 +93,16 @@ ESPxx Hardware +---------------+------------+------------------+ | GPIO0 | | GND | +---------------+------------+------------------+ -| Reset | | RTS\* | +| Reset | | RTS [#rts]_ | +---------------+------------+------------------+ | GPIO15 | PullDown | | +---------------+------------+------------------+ | CH\_PD | PullUp | | +---------------+------------+------------------+ -- Note -- if no RTS is used a manual power toggle is needed +.. rubric:: Notes + +.. [#rts] if no RTS is used a manual power toggle is needed Minimal Hardware Setup for Running only ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -176,7 +182,11 @@ rst cause boot mode ~~~~~~~~~ -the first value respects the pin setup of the Pins 0, 2 and 15. +the first value respects the pin setup of the Pins 0, 2 and 15 + +.. code-block:: + + Number = (GPIO15 << 2) | (GPIO0 << 1) | GPIO2 +----------+----------+---------+---------+-------------+ | Number | GPIO15 | GPIO0 | GPIO2 | Mode | @@ -198,7 +208,6 @@ the first value respects the pin setup of the Pins 0, 2 and 15. | 7 | 3.3V | 3.3V | 3.3V | SDIO | +----------+----------+---------+---------+-------------+ -note: - number = ((GPIO15 << 2) \| (GPIO0 << 1) \| GPIO2); Generic ESP8285 Module ---------------------- @@ -262,6 +271,13 @@ ESPresso Lite 2.0 ESPresso Lite 2.0 is an Arduino-compatible Wi-Fi development board based on an earlier V1 (beta version). Re-designed together with Cytron Technologies, the newly-revised ESPresso Lite V2.0 features the auto-load/auto-program function, eliminating the previous need to reset the board manually before flashing a new program. It also feature two user programmable side buttons and a reset button. The special distinctive features of on-board pads for I2C sensor and actuator is retained. +Mercury +------- + +ESP8266 based development board supercharged with onboard motor driver, RGB LED, support for servo motors and etc. +Git: https://github.com/raliotech/products/tree/master +Product page: https://www.raliotech.com + Phoenix 1.0 ----------- @@ -413,14 +429,10 @@ ThaiEasyElec's ESPino ESPino by ThaiEasyElec using WROOM-02 module from Espressif Systems with 4 MB Flash. -We will update an English description soon. - Product page: -http://thaieasyelec.com/products/wireless-modules/wifi-modules/espino-wifi-development-board-detail.html -- Schematics: -www.thaieasyelec.com/downloads/ETEE052/ETEE052\_ESPino\_Schematic.pdf - -Dimensions: -http://thaieasyelec.com/downloads/ETEE052/ETEE052\_ESPino\_Dimension.pdf -- Pinouts: -http://thaieasyelec.com/downloads/ETEE052/ETEE052\_ESPino\_User\_Manual\_TH\_v1\_0\_20160204.pdf (Please see pg. 8) +* Product page (retired product): https://www.thaieasyelec.com/product/%E0%B8%A2%E0%B8%81%E0%B9%80%E0%B8%A5%E0%B8%B4%E0%B8%81%E0%B8%88%E0%B8%B3%E0%B8%AB%E0%B8%99%E0%B9%88%E0%B8%B2%E0%B8%A2-retired-espino-wifi-development-board/11000833173001086 +* Schematics: https://downloads.thaieasyelec.com/ETEE052/ETEE052\_ESPino\_Schematic.pdf +* Dimensions: https://downloads.thaieasyelec.com/ETEE052/ETEE052\_ESPino\_Dimension.pdf +* Pinouts (Please see pg.8): https://downloads.thaieasyelec.com/ETEE052/ETEE052\_ESPino\_User\_Manual\_TH\_v1\_0\_20160204.pdf WifInfo ------- diff --git a/doc/conf.py b/doc/conf.py index 3b05ae5617..58df5eb922 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -34,7 +34,9 @@ extensions = [ 'nbsphinx', 'sphinx.ext.mathjax', + 'sphinx_rtd_theme', ] + # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -49,8 +51,9 @@ # General information about the project. project = u'ESP8266 Arduino Core' -copyright = u'2017, Ivan Grokhotkov' +slug = re.sub(r'\W+', '-', project.lower()) author = u'Ivan Grokhotkov' +copyright = author # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -66,12 +69,18 @@ # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = 'en' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] +exclude_patterns = [ + '_readthedocs', + '_venv', + '_build', + 'Thumbs.db', + '.DS_Store', +] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' @@ -85,7 +94,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'default' +html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -98,12 +107,12 @@ # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] - # -- Options for HTMLHelp output ------------------------------------------ -# Output file base name for HTML help builder. -htmlhelp_basename = 'ESP8266ArduinoCoredoc' +html_show_sourcelink = True +# Output file base name for HTML help builder. +htmlhelp_basename = slug # -- Options for LaTeX output --------------------------------------------- @@ -129,8 +138,7 @@ # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'ESP8266ArduinoCore.tex', u'ESP8266 Arduino Core Documentation', - u'Ivan Grokhotkov', 'manual'), + (master_doc, f'{slug}.tex', f'{project} Documentation', author, 'manual'), ] @@ -139,8 +147,7 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - (master_doc, 'esp8266arduinocore', u'ESP8266 Arduino Core Documentation', - [author], 1) + (master_doc, slug, f'{project} Documentation', [author], 1) ] @@ -150,23 +157,7 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'ESP8266ArduinoCore', u'ESP8266 Arduino Core Documentation', - author, 'ESP8266ArduinoCore', 'One line description of project.', - 'Miscellaneous'), + (master_doc, slug, f'{project} Documentation', author, slug, project, 'Miscellaneous'), ] linkcheck_anchors_ignore = ["/#!"] - -# -- Use sphinx_rtd_theme for local builds -------------------------------- -# ref. https://github.com/snide/sphinx_rtd_theme#using-this-theme-locally-then-building-on-read-the-docs -# -# on_rtd is whether we are on readthedocs.org -env_readthedocs = os.environ.get('READTHEDOCS', None) -print(env_readthedocs) - -if not env_readthedocs: # only import and set the theme if we're building docs locally - import sphinx_rtd_theme - html_theme = 'sphinx_rtd_theme' - html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] - - diff --git a/doc/esp8266wifi/generic-examples.rst b/doc/esp8266wifi/generic-examples.rst index bbac0cd839..98ccf6c47b 100644 --- a/doc/esp8266wifi/generic-examples.rst +++ b/doc/esp8266wifi/generic-examples.rst @@ -38,23 +38,39 @@ Register the Events To get events to work we need to complete just two steps: -1. Declare the event handler: +1. Declare the event handler in global scope. -``cpp WiFiEventHandler disconnectedEventHandler;`` +.. code-block:: cpp -2. Select particular event (in this case ``onStationModeDisconnected``) - and add the code to be executed when event is fired. + WiFiEventHandler disconnectedEventHandler; -``cpp disconnectedEventHandler = WiFi.onStationModeDisconnected([](const WiFiEventStationModeDisconnected& event) { Serial.println("Station disconnected"); });`` If this event is fired the code will print out information that station has been disconnected. +Alternatively, it can be declared as ``static`` in both function and global scopes. -That's it. It is all we need to do. + +2. Select particular event (in this case ``onStationModeDisconnected``). + When this event is fired the code will print out information that station has been disconnected: + +.. code-block:: cpp + + disconnectedEventHandler = WiFi.onStationModeDisconnected( + [](auto&& event) { + Serial.println("Station disconnected"); + }); + +3. Disable ``disconnectedEventHandler``, so the event is no longer handled by our callback: + +.. code-block:: cpp + + disconnectedEventHandler = nullptr; + +Take note that lifetime of the callback handler is up to the app. e.g. if ``onStationModeDisconnected`` is declared in the function scope, it would be discarded immediately after the function exits. The Code ~~~~~~~~ The complete code, including both methods discussed at the beginning, is provided below. -.. code:: cpp +.. code-block:: cpp #include diff --git a/doc/esp8266wifi/server-examples.rst b/doc/esp8266wifi/server-examples.rst index cbe5c1abf7..c10b5f7eba 100644 --- a/doc/esp8266wifi/server-examples.rst +++ b/doc/esp8266wifi/server-examples.rst @@ -16,7 +16,6 @@ Table of Contents - `The Page is Served <#the-page-is-served>`__ - `Get it Together <#put-it-together>`__ - `Get it Run <#get-it-run>`__ -- `What Else? <#what-else>`__ - `Conclusion <#conclusion>`__ The Object diff --git a/doc/esp8266wifi/soft-access-point-examples.rst b/doc/esp8266wifi/soft-access-point-examples.rst index c4cf39c6c2..2c9fee762c 100644 --- a/doc/esp8266wifi/soft-access-point-examples.rst +++ b/doc/esp8266wifi/soft-access-point-examples.rst @@ -79,7 +79,9 @@ Sketch is small so analysis shouldn't be difficult. In first line we are includi Setting up of the access point ``ESPsoftAP_01`` is done by executing: -``cpp boolean result = WiFi.softAP("ESPsoftAP_01", "pass-to-soft-AP");`` +.. code:: cpp + + boolean result = WiFi.softAP("ESPsoftAP_01", "pass-to-soft-AP"); If this operation is successful then ``result`` will be ``true`` or ``false`` if otherwise. Basing on that either ``Ready`` or ``Failed!`` will be printed out by the following ``if - else`` conditional statement. diff --git a/doc/esp8266wifi/station-class.rst b/doc/esp8266wifi/station-class.rst index 30d9cf13f5..06e072adc6 100644 --- a/doc/esp8266wifi/station-class.rst +++ b/doc/esp8266wifi/station-class.rst @@ -473,7 +473,7 @@ Function returns one of the following connection statuses: - ``WL_IDLE_STATUS`` when Wi-Fi is in process of changing between statuses - ``WL_DISCONNECTED`` if module is not configured in station mode -Returned value is type of ``wl_status_t`` defined in `wl\_definitions.h `__ +Returned value is type of ``wl_status_t`` defined in `wl\_definitions.h `__ *Example code:* @@ -511,7 +511,7 @@ Returned value is type of ``wl_status_t`` defined in `wl\_definitions.h `__ as follows: +Particular connection statuses 6 and 3 may be looked up in `wl\_definitions.h `__ as follows: :: diff --git a/doc/faq/readme.rst b/doc/faq.rst similarity index 94% rename from doc/faq/readme.rst rename to doc/faq.rst index cfd65eca90..869db97bc2 100644 --- a/doc/faq/readme.rst +++ b/doc/faq.rst @@ -13,14 +13,14 @@ Please feel free to contribute if you believe that some frequent issues are not covered below. -I am getting "espcomm\_sync failed" error when trying to upload my ESP. How to resolve this issue? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +I am getting "espcomm\_sync failed", "esptool.FatalError", etc. How to resolve this issue? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This message indicates issue with uploading ESP module over a serial connection. There are couple of possible causes, that depend on the type of your module, if you use separate USB to serial converter. -`Read more `__. +`Read more `__. Why esptool is not listed in "Programmer" menu? How do I upload ESP without it? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -38,7 +38,7 @@ My ESP crashes running some code. How to troubleshoot it? The code may crash because of s/w bug or issue with your h/w. Before entering an issue report, please perform initial troubleshooting. -`Read more `__. +`Read more `__. How can I get some extra KBs in flash ? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -50,7 +50,7 @@ How can I get some extra KBs in flash ? * Use the debug level option ``NoAssert-NDEBUG`` (in the Tools menu) -`Read more `__. +`Read more `__. About WPS ~~~~~~~~~ @@ -63,7 +63,7 @@ required to enable it: ``./tools/boards.txt.py --allowWPS --boardsgen`` -`Read more `__. +`Read more `__. For platformIO (and maybe other build environments), you will also need to add the build flag: -D NO_EXTRA_4K_HEAP @@ -77,7 +77,7 @@ This Arduino library doesn't work on ESP. How do I make it work? You would like to use this Arduino library with ESP8266 and it does not perform. It is not listed among libraries verified to work with ESP8266. -`Read more `__. +`Read more `__. In the IDE, for ESP-12E that has 4M flash, I can choose 4M (1M FS) or 4M (3M FS). No matter what I select, the IDE tells me the maximum code space is about 1M. Where does my flash go? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -118,7 +118,7 @@ This error may pop up after switching between `staging `__ and `stable `__ esp8266 / Arduino package installations, or after upgrading the package version -`Read more `__. +`Read more `__. How to clear TCP PCBs in time-wait state ? @@ -199,4 +199,4 @@ By using a uniquely named `.h` file, macro definitions can be created and globally used. Additionally, compiler command-line options can be embedded in this file as a unique block comment. -`Read more `__. +`Read more `__. diff --git a/doc/faq/a01-espcomm_sync-failed.rst b/doc/faq/a01-upload-failed.rst similarity index 78% rename from doc/faq/a01-espcomm_sync-failed.rst rename to doc/faq/a01-upload-failed.rst index df4d0aa177..cefda15829 100644 --- a/doc/faq/a01-espcomm_sync-failed.rst +++ b/doc/faq/a01-upload-failed.rst @@ -1,7 +1,5 @@ -:orphan: - -I am getting "espcomm\_sync failed" error when trying to upload my ESP. How to resolve this issue? --------------------------------------------------------------------------------------------------- +Why I am getting errors when trying to upload to my ESP? +-------------------------------------------------------- - `Introduction <#introduction>`__ - `Initial Checks <#initial-checks>`__ @@ -9,20 +7,30 @@ I am getting "espcomm\_sync failed" error when trying to upload my ESP. How to r - `Reset Methods <#reset-methods>`__ - `Ck <#ck>`__ - `Nodemcu <#nodemcu>`__ -- `I'm Stuck <#im-stuck>`__ +- `I'm Stuck <#i-m-stuck>`__ - `Conclusion <#conclusion>`__ Introduction ~~~~~~~~~~~~ -This message indicates issue with uploading ESP module over a serial +Messages like + +* ``espcomm_sync failed`` + +* ``esptool.FatalError: Failed to connect to ESP8266: Invalid head of packet (0xF0)`` + +* ``esptool.FatalError: Failed to connect to ESP8266: Timed out waiting for packet header`` + +indicate issue with with uploading ESP module over a serial connection. There are couple of possible causes, that depend on the type of module, if you use separate USB to serial converter, what parameters are selected for upload, etc. As result there is no single answer on the root cause. To find it out you may need to complete couple of troubleshooting steps. - Note: If you are just starting with ESP, to reduce potential issues +.. note:: + + If you are just starting with ESP, to reduce potential issues with uploading, select ESP board with integrated USB to serial converter. This will considerably reduce number of user depended factors or configuration settings that influence upload process. @@ -167,7 +175,93 @@ loading <../boards.rst#minimal-hardware-setup-for-bootloading-only>`__ during: upload* and try uploading again. For successful upload this log should look similar to example shown below: -``C:\Users\Krzysztof\AppData\Local\Arduino15\packages\esp8266\tools\esptool\0.4.8/esptool.exe -vv -cd ck -cb 115200 -cp COM3 -ca 0x00000 -cf C:\Users\KRZYSZ~1\AppData\Local\Temp\build7e44b372385012e74d64fb272d24b802.tmp/Blink.ino.bin esptool v0.4.8 - (c) 2014 Ch. Klippel setting board to ck setting baudrate from 115200 to 115200 setting port from COM1 to COM3 setting address from 0x00000000 to 0x00000000 espcomm_upload_file espcomm_upload_mem setting serial port timeouts to 1000 ms opening bootloader resetting board trying to connect flush start setting serial port timeouts to 1 ms setting serial port timeouts to 1000 ms flush complete espcomm_send_command: sending command header espcomm_send_command: sending command payload read 0, requested 1 trying to connect flush start setting serial port timeouts to 1 ms setting serial port timeouts to 1000 ms flush complete espcomm_send_command: sending command header espcomm_send_command: sending command payload espcomm_send_command: receiving 2 bytes of data espcomm_send_command: receiving 2 bytes of data espcomm_send_command: receiving 2 bytes of data espcomm_send_command: receiving 2 bytes of data espcomm_send_command: receiving 2 bytes of data espcomm_send_command: receiving 2 bytes of data espcomm_send_command: receiving 2 bytes of data espcomm_send_command: receiving 2 bytes of data Uploading 226368 bytes from to flash at 0x00000000 erasing flash size: 037440 address: 000000 first_sector_index: 0 total_sector_count: 56 head_sector_count: 16 adjusted_sector_count: 40 erase_size: 028000 espcomm_send_command: sending command header espcomm_send_command: sending command payload setting serial port timeouts to 15000 ms setting serial port timeouts to 1000 ms espcomm_send_command: receiving 2 bytes of data writing flash .............................................................................................................................................................................................................................. starting app without reboot espcomm_send_command: sending command header espcomm_send_command: sending command payload espcomm_send_command: receiving 2 bytes of data closing bootloader flush start setting serial port timeouts to 1 ms setting serial port timeouts to 1000 ms flush complete`` + For example, uploading using esptool.py and esptool + +.. code-block:: console + + $ esptool.py --after no_reset --chip esp8266 --baud 460800 write_flash 0x0 d1-mini-firmware.bin + esptool.py v4.5.1 + Found 1 serial ports + Serial port /dev/ttyUSB0 + Connecting.... + Chip is ESP8266EX + Features: WiFi + Crystal is 26MHz + MAC: 11:22:33:44:55:66 + Uploading stub... + Running stub... + Stub running... + Changing baud rate to 460800 + Changed. + Configuring flash size... + Flash will be erased from 0x00000000 to 0x0004efff... + Compressed 321440 bytes to 221714... + Wrote 321440 bytes (221714 compressed) at 0x00000000 in 5.0 seconds (effective 511.4 kbit/s)... + Hash of data verified. + + Leaving... + Staying in bootloader. + + +.. code-block:: console + + > C:\Users\Krzysztof\AppData\Local\Arduino15\packages\esp8266\tools\esptool\0.4.8/esptool.exe -vv -cd ck -cb 115200 -cp COM3 -ca 0x00000 -cf C:\Users\KRZYSZ~1\AppData\Local\Temp\build7e44b372385012e74d64fb272d24b802.tmp/Blink.ino.bin + esptool v0.4.8 - (c) 2014 Ch. Klippel + setting board to ck + setting baudrate from 115200 to 115200 + setting port from COM1 to COM3 + setting address from 0x00000000 to 0x00000000 + espcomm_upload_file + espcomm_upload_mem + setting serial port timeouts to 1000 ms + opening bootloader + resetting board + trying to connect + flush start + setting serial port timeouts to 1 ms + setting serial port timeouts to 1000 ms + flush complete + espcomm_send_command: sending command header + espcomm_send_command: sending command payload + read 0, requested 1 + trying to connect + flush start + setting serial port timeouts to 1 ms + setting serial port timeouts to 1000 ms + flush complete + espcomm_send_command: sending command header + espcomm_send_command: sending command payload + espcomm_send_command: receiving 2 bytes of data + espcomm_send_command: receiving 2 bytes of data + espcomm_send_command: receiving 2 bytes of data + espcomm_send_command: receiving 2 bytes of data + espcomm_send_command: receiving 2 bytes of data + espcomm_send_command: receiving 2 bytes of data + espcomm_send_command: receiving 2 bytes of data + espcomm_send_command: receiving 2 bytes of data + Uploading 226368 bytes from to flash at 0x00000000 + erasing flash + size: 037440 address: 000000 + first_sector_index: 0 + total_sector_count: 56 + head_sector_count: 16 + adjusted_sector_count: 40 + erase_size: 028000 + espcomm_send_command: sending command header + espcomm_send_command: sending command payload + setting serial port timeouts to 15000 ms + setting serial port timeouts to 1000 ms + espcomm_send_command: receiving 2 bytes of data + writing flash .............................................................................................................................................................................................................................. + starting app without reboot + espcomm_send_command: sending command header + espcomm_send_command: sending command payload + espcomm_send_command: receiving 2 bytes of data + closing bootloader + flush start + setting serial port timeouts to 1 ms + setting serial port timeouts to 1000 ms + flush complete Upload log may be longer depending on number of connection attempts made by esptool. Analyze it for any anomalies to configuration you have @@ -177,7 +271,7 @@ rate, etc. Resolve all noted differences. Reset Methods ~~~~~~~~~~~~~ -If you got to this point and still see ``espcomm_sync failed``, then now +If you got to this point and still see uploading error, then now you need to bring in the heavy guns. Connect scope or logic analyzer to GPIO0, RST and RXD pins of the ESP to @@ -238,13 +332,13 @@ Each retry is reported in upload log as follows: resetting board trying to connect - flush start - setting serial port timeouts to 1 ms - setting serial port timeouts to 1000 ms - flush complete - espcomm_send_command: sending command header - espcomm_send_command: sending command payload - read 0, requested 1 + flush start + setting serial port timeouts to 1 ms + setting serial port timeouts to 1000 ms + flush complete + espcomm_send_command: sending command header + espcomm_send_command: sending command payload + read 0, requested 1 Presented circuit has one important limitation when it comes to work with Arduino IDE. After opening Serial Monitor (Ctrl-Shift-M), both RTS diff --git a/doc/faq/a02-my-esp-crashes.rst b/doc/faq/a02-my-esp-crashes.rst index 0134dda666..c480a825e7 100644 --- a/doc/faq/a02-my-esp-crashes.rst +++ b/doc/faq/a02-my-esp-crashes.rst @@ -1,11 +1,9 @@ -:orphan: - My ESP crashes running some code. How to troubleshoot it? --------------------------------------------------------- - `Introduction <#introduction>`__ - `What ESP has to Say <#what-esp-has-to-say>`__ -- `Get Your H/W Right <#get-your-hw-right>`__ +- `Get Your H/W Right <#get-your-h-w-right>`__ - `Enable compilation warnings <#enable-compilation-warnings>`__ - `What is the Cause of Restart? <#what-is-the-cause-of-restart>`__ - `Exception <#exception>`__ @@ -148,8 +146,8 @@ table to understand what kind of issue it is. If you have no clues what it's about and where it happens, then use `Arduino ESP8266/ESP32 Exception Stack Trace Decoder `__ to find -out in which line of application it is triggered. Please refer to `Check -Where the Code Crashes <#check-where-the-code-crashes>`__ point below +out in which line of application it is triggered. Please refer to +`Exception decoder <#exception-decoder>`__ point below for a quick example how to do it. **NOTE:** When decoding exceptions be sure to include all lines between diff --git a/doc/faq/a03-library-does-not-work.rst b/doc/faq/a03-library-does-not-work.rst index 8b9b2d6910..e34ad050b1 100644 --- a/doc/faq/a03-library-does-not-work.rst +++ b/doc/faq/a03-library-does-not-work.rst @@ -1,5 +1,3 @@ -:orphan: - This Arduino library doesn't work on ESP. How do I make it working? ------------------------------------------------------------------- @@ -7,7 +5,7 @@ This Arduino library doesn't work on ESP. How do I make it working? - `Identify the Issues <#identify-the-issues>`__ - `Fix it Yourself <#fix-it-yourself>`__ - `Compilation Errors <#compilation-errors>`__ -- `Exceptions / Watchdog Resets <#exceptions--watchdog-resets>`__ +- `Exceptions / Watchdog Resets <#exceptions-watchdog-resets>`__ - `Functionality Issues <#functionality-issues>`__ - `Conclusion <#conclusion>`__ diff --git a/doc/faq/a04-board-generic-is-unknown.rst b/doc/faq/a04-board-generic-is-unknown.rst index 88e2f3e7be..ada9a7eec2 100644 --- a/doc/faq/a04-board-generic-is-unknown.rst +++ b/doc/faq/a04-board-generic-is-unknown.rst @@ -1,5 +1,3 @@ -:orphan: - How to resolve "Board generic (platform esp8266, package esp8266) is unknown" error? ------------------------------------------------------------------------------------ diff --git a/doc/faq/a05-board-generator.rst b/doc/faq/a05-board-generator.rst index 279491355e..4f31fce996 100644 --- a/doc/faq/a05-board-generator.rst +++ b/doc/faq/a05-board-generator.rst @@ -1,5 +1,3 @@ -:orphan: - Board generator --------------- diff --git a/doc/faq/a06-global-build-options.rst b/doc/faq/a06-global-build-options.rst index 3e86b88a58..231dbc6baf 100644 --- a/doc/faq/a06-global-build-options.rst +++ b/doc/faq/a06-global-build-options.rst @@ -1,5 +1,3 @@ -:orphan: - How to specify global build defines and options =============================================== diff --git a/doc/index.rst b/doc/index.rst index 5f3ec247c6..afe976348e 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -1,9 +1,11 @@ Welcome to ESP8266 Arduino Core's documentation! ================================================ +Using ESP8266 Core +================== + .. toctree:: :maxdepth: 2 - :caption: Contents: Installing Arduino IDE options @@ -15,11 +17,26 @@ Welcome to ESP8266 Arduino Core's documentation! PROGMEM Using GDB to debug MMU - Boards - FAQ +Troubleshooting +=============== +.. toctree:: + :caption: Troubleshooting + :maxdepth: 2 + + FAQ Exception causes Debugging Stack Dump Using with Eclipse + +FAQ +=== +.. toctree:: + :caption: FAQ + :maxdepth: 2 + :titlesonly: + :glob: + + faq/* diff --git a/doc/mmu.rst b/doc/mmu.rst index 3846bc3f81..27ea4d4d7d 100644 --- a/doc/mmu.rst +++ b/doc/mmu.rst @@ -250,5 +250,3 @@ address range of IRAM or DRAM. uint8_t mmu_set_uint8(void *p8, const uint8_t val); uint16_t mmu_set_uint16(uint16_t *p16, const uint16_t val); int16_t mmu_set_int16(int16_t *p16, const int16_t val); - -:: diff --git a/doc/ota_updates/readme.rst b/doc/ota_updates/readme.rst index ba68506cb0..de351fe3f2 100755 --- a/doc/ota_updates/readme.rst +++ b/doc/ota_updates/readme.rst @@ -216,7 +216,7 @@ Requirements Application Example ~~~~~~~~~~~~~~~~~~~ -Instructions below show configuration of OTA on a NodeMCU 1.0 (ESP-12E Module) board. You can use any other board that meets the `requirements <#basic-requirements>`__ described above. This instruction is valid for all operating systems supported by the Arduino IDE. Screen captures have been made on Windows 7 and you may see small differences (like name of the serial port), if you are using Linux or MacOS. +Instructions below show configuration of OTA on a NodeMCU 1.0 (ESP-12E Module) board. You can use any other board that meets the `requirements <#ota-basic-requirements>`__ described above. This instruction is valid for all operating systems supported by the Arduino IDE. Screen captures have been made on Windows 7 and you may see small differences (like name of the serial port), if you are using Linux or MacOS. 1. Before you begin, please make sure that you have the following software installed: @@ -336,7 +336,7 @@ Select COM port and baud rate on external terminal program as if you were using :alt: Termite settings -Then run OTA from IDE and look what is displayed on terminal. Successful `ArduinoOTA <#arduinoota>`__ process using BasicOTA.ino sketch looks like below (IP address depends on your network configuration): +Then run OTA from IDE and look what is displayed on terminal. Successful `ArduinoOTA <#arduino-ide>`__ process using BasicOTA.ino sketch looks like below (IP address depends on your network configuration): .. figure:: a-ota-external-serial-terminal-output.png :alt: OTA upload successful - output on an external serial terminal @@ -407,7 +407,7 @@ The sample implementation provided below has been done using: ``ESP8266HTTPUpdateServer`` library, - NodeMCU 1.0 (ESP-12E Module). -You can use another module if it meets previously described `requirements <#basic-requirements>`__. +You can use another module if it meets previously described `requirements <#ota-basic-requirements>`__. 1. Before you begin, please make sure that you have the following software installed: diff --git a/doc/requirements.txt b/doc/requirements.txt index 8d90d8f59a..6b2684762d 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1,10 +1,8 @@ # Requirements file for pip # list of Python packages used in documentation build -sphinx -sphinx-rtd-theme -breathe -nbsphinx -docutils<0.17 -testresources -#at the time of writing, requirement is pygments<3,>=2.4.1 -pygments>=2.4.1 +sphinx>=8.1.2,<9.0.0 +sphinx-rtd-theme>=3.0.2,<4.0.0 +breathe>=4.36.0,<5.0.0 +nbsphinx>=0.9.7,<1.0.0 +testresources>=2.0.1,<3.0.0 +pygments>=2.19.1,<3.0.0 diff --git a/libraries/DNSServer/examples/CaptivePortalAdvanced/handleHttp.ino b/libraries/DNSServer/examples/CaptivePortalAdvanced/handleHttp.ino index aec2556a48..c7380017d1 100644 --- a/libraries/DNSServer/examples/CaptivePortalAdvanced/handleHttp.ino +++ b/libraries/DNSServer/examples/CaptivePortalAdvanced/handleHttp.ino @@ -27,9 +27,7 @@ void handleRoot() { boolean captivePortal() { if (!isIp(server.hostHeader()) && server.hostHeader() != (String(myHostname) + ".local")) { Serial.println("Request redirected to captive portal"); - server.sendHeader("Location", String("http://") + toStringIp(server.client().localIP()), true); - server.send(302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves. - server.client().stop(); // Stop is needed because we sent no content length + server.redirect(String("http://") + toStringIp(server.client().localIP())); return true; } return false; @@ -91,12 +89,7 @@ void handleWifiSave() { Serial.println("wifi save"); server.arg("n").toCharArray(ssid, sizeof(ssid) - 1); server.arg("p").toCharArray(password, sizeof(password) - 1); - server.sendHeader("Location", "wifi", true); - server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); - server.sendHeader("Pragma", "no-cache"); - server.sendHeader("Expires", "-1"); - server.send(302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves. - server.client().stop(); // Stop is needed because we sent no content length + server.redirect("wifi"); saveCredentials(); connect = strlen(ssid) > 0; // Request WLAN connect with new credentials if there is a SSID } diff --git a/libraries/DNSServer/examples/NAPTCaptivePortal/PortalRedirectHttp.ino b/libraries/DNSServer/examples/NAPTCaptivePortal/PortalRedirectHttp.ino index 286490772d..5de12229a5 100644 --- a/libraries/DNSServer/examples/NAPTCaptivePortal/PortalRedirectHttp.ino +++ b/libraries/DNSServer/examples/NAPTCaptivePortal/PortalRedirectHttp.ino @@ -36,13 +36,11 @@ void sendPortalRedirect(String path, String targetName) { If the "Location" header element works the HTML stuff is never seen. */ // https://tools.ietf.org/html/rfc7231#section-6.4.3 - server.sendHeader("Location", path, true); - addNoCacheHeader(); String reply = FPSTR(portalRedirectHTML); reply.reserve(reply.length() + 2 * path.length() + 80); reply.replace("{t}", targetName); reply.replace("{1}", path); - server.send(302, "text/html", reply); + server.redirect(path, reply); } #endif // LWIP_FEATURES && !LWIP_IPV6 diff --git a/libraries/ESP8266HTTPClient/examples/BasicHttpsClient/BasicHttpsClient.ino b/libraries/ESP8266HTTPClient/examples/BasicHttpsClient/BasicHttpsClient.ino index 62bfc93a9a..0e174c2790 100644 --- a/libraries/ESP8266HTTPClient/examples/BasicHttpsClient/BasicHttpsClient.ino +++ b/libraries/ESP8266HTTPClient/examples/BasicHttpsClient/BasicHttpsClient.ino @@ -39,10 +39,15 @@ void loop() { // wait for WiFi connection if ((WiFiMulti.run() == WL_CONNECTED)) { - std::unique_ptr client(new BearSSL::WiFiClientSecure); + auto certs = std::make_unique(cert_Cloudflare_Inc_ECC_CA_3); + auto client = std::make_unique(); - client->setFingerprint(fingerprint_sni_cloudflaressl_com); - // Or, if you happy to ignore the SSL certificate, then use the following line instead: + client->setTrustAnchors(certs.get()); + // Or, if you prefer to use fingerprinting: + // client->setFingerprint(fingerprint_w3_org); + // This is *not* a recommended option, as fingerprint changes with the host certificate + + // Or, if you are *absolutely* sure it is ok to ignore the SSL certificate: // client->setInsecure(); HTTPClient https; diff --git a/libraries/ESP8266HTTPClient/examples/BasicHttpsClient/certs.h b/libraries/ESP8266HTTPClient/examples/BasicHttpsClient/certs.h index 0263070421..76451ff76a 100644 --- a/libraries/ESP8266HTTPClient/examples/BasicHttpsClient/certs.h +++ b/libraries/ESP8266HTTPClient/examples/BasicHttpsClient/certs.h @@ -1,7 +1,7 @@ // this file is autogenerated - any modification will be overwritten // unused symbols will not be linked in the final binary -// generated on 2023-03-20 23:02:42 +// generated on 2024-07-30 22:46:21 // by ['../../../../tools/cert.py', '-s', 'jigsaw.w3.org', '-n', 'jigsaw'] #pragma once @@ -12,21 +12,21 @@ const char* jigsaw_host = "jigsaw.w3.org"; const uint16_t jigsaw_port = 443; -// CN: sni.cloudflaressl.com => name: sni_cloudflaressl_com -// not valid before: 2023-02-14 00:00:00 -// not valid after: 2024-02-14 23:59:59 -const char fingerprint_sni_cloudflaressl_com [] PROGMEM = "70:7c:82:07:f3:58:18:87:25:42:31:83:45:86:bd:17:86:71:4e:1f"; -const char pubkey_sni_cloudflaressl_com [] PROGMEM = R"PUBKEY( +// CN: w3.org => name: w3_org +// not valid before: 2024-01-26 00:00:00+00:00 +// not valid after: 2024-12-31 23:59:59+00:00 +const char fingerprint_w3_org [] PROGMEM = "07:f2:bd:4c:d0:ce:58:da:13:03:9d:a9:0d:df:e9:5b:60:5f:7f:a5"; +const char pubkey_w3_org [] PROGMEM = R"PUBKEY( -----BEGIN PUBLIC KEY----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/NU/7vfdymScyhfx81ieO8XiwGqq -TU4tjeWzSosWSpmQwnGmRqiU2h2wyT9uYxRme6uQ0yLedf4nz9ks+4OxtA== +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPwx1EbG8lugJ74owfhQChFkoxc9R +EZ9D7g5JfO7TUZH+nxWxCT7njoKgD9yvJZYTy/oijTdhB7o7knUsBLRj8A== -----END PUBLIC KEY----- )PUBKEY"; // http://cacerts.digicert.com/CloudflareIncECCCA-3.crt // CN: Cloudflare Inc ECC CA-3 => name: Cloudflare_Inc_ECC_CA_3 -// not valid before: 2020-01-27 12:48:08 -// not valid after: 2024-12-31 23:59:59 +// not valid before: 2020-01-27 12:48:08+00:00 +// not valid after: 2024-12-31 23:59:59+00:00 const char cert_Cloudflare_Inc_ECC_CA_3 [] PROGMEM = R"CERT( -----BEGIN CERTIFICATE----- MIIDzTCCArWgAwIBAgIQCjeHZF5ftIwiTv0b7RQMPDANBgkqhkiG9w0BAQsFADBa diff --git a/libraries/ESP8266HTTPClient/examples/StreamHttpsClient/StreamHttpsClient.ino b/libraries/ESP8266HTTPClient/examples/StreamHttpsClient/StreamHttpsClient.ino index 5c80a1eeb8..df3b2f233f 100644 --- a/libraries/ESP8266HTTPClient/examples/StreamHttpsClient/StreamHttpsClient.ino +++ b/libraries/ESP8266HTTPClient/examples/StreamHttpsClient/StreamHttpsClient.ino @@ -1,7 +1,5 @@ /** - StreamHTTPClient.ino - - Created on: 24.05.2015 + Based on StreamHTTPClient.ino */ @@ -9,9 +7,10 @@ #include #include - #include +#include "certs.h" + ESP8266WiFiMulti WiFiMulti; void setup() { @@ -37,23 +36,27 @@ void loop() { // wait for WiFi connection if ((WiFiMulti.run() == WL_CONNECTED)) { - std::unique_ptr client(new BearSSL::WiFiClientSecure); + auto certs = std::make_unique(cert_Amazon_RSA_2048_M02); + auto client = std::make_unique(); + + client->setTrustAnchors(certs.get()); + // Or, if you prefer to use fingerprinting: + // client->setFingerprint(fingerprint___mbed_com); + // This is *not* a recommended option, as fingerprint changes with the host certificate - bool mfln = client->probeMaxFragmentLength("tls.mbed.org", 443, 1024); - Serial.printf("\nConnecting to https://tls.mbed.org\n"); + // Or, if you are *absolutely* sure it is ok to ignore the SSL certificate: + // client->setInsecure(); + + bool mfln = client->probeMaxFragmentLength(mbed_host, mbed_port, 1024); + Serial.printf("\nConnecting to %s:%hu...\n", mbed_host, mbed_port); Serial.printf("Maximum fragment Length negotiation supported: %s\n", mfln ? "yes" : "no"); if (mfln) { client->setBufferSizes(1024, 1024); } Serial.print("[HTTPS] begin...\n"); - // configure server and url - const uint8_t fingerprint[20] = { 0x15, 0x77, 0xdc, 0x04, 0x7c, 0x00, 0xf8, 0x70, 0x09, 0x34, 0x24, 0xf4, 0xd3, 0xa1, 0x7a, 0x6c, 0x1e, 0xa3, 0xe0, 0x2a }; - - client->setFingerprint(fingerprint); - HTTPClient https; - if (https.begin(*client, "https://tls.mbed.org/")) { + if (https.begin(*client, mbed_host, mbed_port)) { Serial.print("[HTTPS] GET...\n"); // start connection and send HTTP header diff --git a/libraries/ESP8266HTTPClient/examples/StreamHttpsClient/certUpdate b/libraries/ESP8266HTTPClient/examples/StreamHttpsClient/certUpdate new file mode 100755 index 0000000000..cb11cef93c --- /dev/null +++ b/libraries/ESP8266HTTPClient/examples/StreamHttpsClient/certUpdate @@ -0,0 +1,2 @@ +cd ${0%/*} 2>/dev/null +python3 ../../../../tools/cert.py -s tls.mbed.org -n mbed > certs.h diff --git a/libraries/ESP8266HTTPClient/examples/StreamHttpsClient/certs.h b/libraries/ESP8266HTTPClient/examples/StreamHttpsClient/certs.h new file mode 100644 index 0000000000..c06f2d2154 --- /dev/null +++ b/libraries/ESP8266HTTPClient/examples/StreamHttpsClient/certs.h @@ -0,0 +1,173 @@ + +// this file is autogenerated - any modification will be overwritten +// unused symbols will not be linked in the final binary +// generated on 2024-07-30 22:46:02 +// by ['../../../../tools/cert.py', '-s', 'tls.mbed.org', '-n', 'mbed'] + +#pragma once + +//////////////////////////////////////////////////////////// +// certificate chain for tls.mbed.org:443 + +const char* mbed_host = "tls.mbed.org"; +const uint16_t mbed_port = 443; + +// CN: *.mbed.com => name: __mbed_com +// not valid before: 2023-12-15 00:00:00 +// not valid after: 2025-01-12 23:59:59 +const char fingerprint___mbed_com [] PROGMEM = "cf:a3:3a:98:de:77:ee:a0:d8:2d:b1:0e:c9:eb:d3:5d:71:5c:4d:1c"; +const char pubkey___mbed_com [] PROGMEM = R"PUBKEY( +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnte0NyyUAM7CJHORnzqZ +0vYhz9K1wdi0Fkc11gypDgyaEXmLY3m0X+mXayEbhw/Xkn04uQ0/6WyK/pWTeTeu +MPKD1Gr5xjBNELs0GLdRdfZGhUyFkTgQLtDrbEpD8gNO2bfVOiJh/tMZ43NNmJUj +lJftSW3ZivBO5621NC9gbfqAQJZNkMoSV1c9JNIPzZCv4aPR/XuZVeKNWQKzAULf +wRsfz5Ti37EWUQ2BNPUOIYQQvOqI0y4FETIUmA4UhjUmb3/KsOTIUx0HML0MYkxe +SCfSzO8zjJaFujrC82LQvwFfIfRbGCK63GREzT4B5SGUgIgOGe1NSfEBqioRNtig +SwIDAQAB +-----END PUBLIC KEY----- +)PUBKEY"; + +// http://crt.r2m02.amazontrust.com/r2m02.cer +// CN: Amazon RSA 2048 M02 => name: Amazon_RSA_2048_M02 +// not valid before: 2022-08-23 22:25:30 +// not valid after: 2030-08-23 22:25:30 +const char cert_Amazon_RSA_2048_M02 [] PROGMEM = R"CERT( +-----BEGIN CERTIFICATE----- +MIIEXjCCA0agAwIBAgITB3MSSkvL1E7HtTvq8ZSELToPoTANBgkqhkiG9w0BAQsF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAxMB4XDTIyMDgyMzIyMjUzMFoXDTMwMDgyMzIyMjUzMFowPDEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEcMBoGA1UEAxMTQW1hem9uIFJT +QSAyMDQ4IE0wMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALtDGMZa +qHneKei1by6+pUPPLljTB143Si6VpEWPc6mSkFhZb/6qrkZyoHlQLbDYnI2D7hD0 +sdzEqfnuAjIsuXQLG3A8TvX6V3oFNBFVe8NlLJHvBseKY88saLwufxkZVwk74g4n +WlNMXzla9Y5F3wwRHwMVH443xGz6UtGSZSqQ94eFx5X7Tlqt8whi8qCaKdZ5rNak ++r9nUThOeClqFd4oXych//Rc7Y0eX1KNWHYSI1Nk31mYgiK3JvH063g+K9tHA63Z +eTgKgndlh+WI+zv7i44HepRZjA1FYwYZ9Vv/9UkC5Yz8/yU65fgjaE+wVHM4e/Yy +C2osrPWE7gJ+dXMCAwEAAaOCAVowggFWMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYD +VR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNV +HQ4EFgQUwDFSzVpQw4J8dHHOy+mc+XrrguIwHwYDVR0jBBgwFoAUhBjMhTTsvAyU +lC4IWZzHshBOCggwewYIKwYBBQUHAQEEbzBtMC8GCCsGAQUFBzABhiNodHRwOi8v +b2NzcC5yb290Y2ExLmFtYXpvbnRydXN0LmNvbTA6BggrBgEFBQcwAoYuaHR0cDov +L2NydC5yb290Y2ExLmFtYXpvbnRydXN0LmNvbS9yb290Y2ExLmNlcjA/BgNVHR8E +ODA2MDSgMqAwhi5odHRwOi8vY3JsLnJvb3RjYTEuYW1hem9udHJ1c3QuY29tL3Jv +b3RjYTEuY3JsMBMGA1UdIAQMMAowCAYGZ4EMAQIBMA0GCSqGSIb3DQEBCwUAA4IB +AQAtTi6Fs0Azfi+iwm7jrz+CSxHH+uHl7Law3MQSXVtR8RV53PtR6r/6gNpqlzdo +Zq4FKbADi1v9Bun8RY8D51uedRfjsbeodizeBB8nXmeyD33Ep7VATj4ozcd31YFV +fgRhvTSxNrrTlNpWkUk0m3BMPv8sg381HhA6uEYokE5q9uws/3YkKqRiEz3TsaWm +JqIRZhMbgAfp7O7FUwFIb7UIspogZSKxPIWJpxiPo3TcBambbVtQOcNRWz5qCQdD +slI2yayq0n2TXoHyNCLEH8rpsJRVILFsg0jc7BaFrMnF462+ajSehgj12IidNeRN +4zl+EoNaWdpnWndvSpAEkq2P +-----END CERTIFICATE----- +)CERT"; + +// http://crt.rootca1.amazontrust.com/rootca1.cer +// CN: Amazon Root CA 1 => name: Amazon_Root_CA_1 +// not valid before: 2015-05-25 12:00:00 +// not valid after: 2037-12-31 01:00:00 +const char cert_Amazon_Root_CA_1 [] PROGMEM = R"CERT( +-----BEGIN CERTIFICATE----- +MIIEkjCCA3qgAwIBAgITBn+USionzfP6wq4rAfkI7rnExjANBgkqhkiG9w0BAQsF +ADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNj +b3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4x +OzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1 +dGhvcml0eSAtIEcyMB4XDTE1MDUyNTEyMDAwMFoXDTM3MTIzMTAxMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj +ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM +9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw +IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 +VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L +93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm +jgSubJrIqg0CAwEAAaOCATEwggEtMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgGGMB0GA1UdDgQWBBSEGMyFNOy8DJSULghZnMeyEE4KCDAfBgNVHSMEGDAW +gBScXwDfqgHXMCs4iKK4bUqc8hGRgzB4BggrBgEFBQcBAQRsMGowLgYIKwYBBQUH +MAGGImh0dHA6Ly9vY3NwLnJvb3RnMi5hbWF6b250cnVzdC5jb20wOAYIKwYBBQUH +MAKGLGh0dHA6Ly9jcnQucm9vdGcyLmFtYXpvbnRydXN0LmNvbS9yb290ZzIuY2Vy +MD0GA1UdHwQ2MDQwMqAwoC6GLGh0dHA6Ly9jcmwucm9vdGcyLmFtYXpvbnRydXN0 +LmNvbS9yb290ZzIuY3JsMBEGA1UdIAQKMAgwBgYEVR0gADANBgkqhkiG9w0BAQsF +AAOCAQEAYjdCXLwQtT6LLOkMm2xF4gcAevnFWAu5CIw+7bMlPLVvUOTNNWqnkzSW +MiGpSESrnO09tKpzbeR/FoCJbM8oAxiDR3mjEH4wW6w7sGDgd9QIpuEdfF7Au/ma +eyKdpwAJfqxGF4PcnCZXmTA5YpaP7dreqsXMGz7KQ2hsVxa81Q4gLv7/wmpdLqBK +bRRYh5TmOTFffHPLkIhqhBGWJ6bt2YFGpn6jcgAKUj6DiAdjd4lpFw85hdKrCEVN +0FE6/V1dN2RMfjCyVSRCnTawXZwXgWHxyvkQAiSr6w10kY17RSlQOYiypok1JR4U +akcjMS9cmvqtmg5iUaQqqcT5NJ0hGA== +-----END CERTIFICATE----- +)CERT"; + +// http://crt.rootg2.amazontrust.com/rootg2.cer +// CN: Starfield Services Root Certificate Authority - G2 => name: Starfield_Services_Root_Certificate_Authority___G2 +// not valid before: 2009-09-02 00:00:00 +// not valid after: 2034-06-28 17:39:16 +const char cert_Starfield_Services_Root_Certificate_Authority___G2 [] PROGMEM = R"CERT( +-----BEGIN CERTIFICATE----- +MIIEdTCCA12gAwIBAgIJAKcOSkw0grd/MA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNV +BAYTAlVTMSUwIwYDVQQKExxTdGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTIw +MAYDVQQLEylTdGFyZmllbGQgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eTAeFw0wOTA5MDIwMDAwMDBaFw0zNDA2MjgxNzM5MTZaMIGYMQswCQYDVQQGEwJV +UzEQMA4GA1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTElMCMGA1UE +ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjE7MDkGA1UEAxMyU3RhcmZp +ZWxkIFNlcnZpY2VzIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDVDDrEKvlO4vW+GZdfjohTsR8/ +y8+fIBNtKTrID30892t2OGPZNmCom15cAICyL1l/9of5JUOG52kbUpqQ4XHj2C0N +Tm/2yEnZtvMaVq4rtnQU68/7JuMauh2WLmo7WJSJR1b/JaCTcFOD2oR0FMNnngRo +Ot+OQFodSk7PQ5E751bWAHDLUu57fa4657wx+UX2wmDPE1kCK4DMNEffud6QZW0C +zyyRpqbn3oUYSXxmTqM6bam17jQuug0DuDPfR+uxa40l2ZvOgdFFRjKWcIfeAg5J +Q4W2bHO7ZOphQazJ1FTfhy/HIrImzJ9ZVGif/L4qL8RVHHVAYBeFAlU5i38FAgMB +AAGjgfAwge0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0O +BBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMB8GA1UdIwQYMBaAFL9ft9HO3R+G9FtV +rNzXEMIOqYjnME8GCCsGAQUFBwEBBEMwQTAcBggrBgEFBQcwAYYQaHR0cDovL28u +c3MyLnVzLzAhBggrBgEFBQcwAoYVaHR0cDovL3guc3MyLnVzL3guY2VyMCYGA1Ud +HwQfMB0wG6AZoBeGFWh0dHA6Ly9zLnNzMi51cy9yLmNybDARBgNVHSAECjAIMAYG +BFUdIAAwDQYJKoZIhvcNAQELBQADggEBACMd44pXyn3pF3lM8R5V/cxTbj5HD9/G +VfKyBDbtgB9TxF00KGu+x1X8Z+rLP3+QsjPNG1gQggL4+C/1E2DUBc7xgQjB3ad1 +l08YuW3e95ORCLp+QCztweq7dp4zBncdDQh/U90bZKuCJ/Fp1U1ervShw3WnWEQt +8jxwmKy6abaVd38PMV4s/KCHOkdp8Hlf9BRUpJVeEXgSYCfOn8J3/yNTd126/+pZ +59vPr5KW7ySaNRB6nJHGDn2Z9j8Z3/VyVOEVqQdZe4O/Ui5GjLIAZHYcSNPYeehu +VsyuLAOQ1xk4meTKCRlb/weWsKh/NEnfVqn3sF/tM+2MR7cwA130A4w= +-----END CERTIFICATE----- +)CERT"; + +// http://x.ss2.us/x.cer +// CN: => name: +// not valid before: 2004-06-29 17:39:16 +// not valid after: 2024-06-29 17:39:16 +const char cert_ [] PROGMEM = R"CERT( +-----BEGIN CERTIFICATE----- +MIIFEjCCBHugAwIBAgICAQwwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1Zh +bGlDZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIElu +Yy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24g +QXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAe +BgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTA0MDYyOTE3MzkxNloX +DTI0MDYyOTE3MzkxNlowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0YXJmaWVs +ZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBDbGFzcyAy +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0A +MIIBCAKCAQEAtzLI/ulxpgSFrQwRZN/OTe/IAxiHP6Gr+zymn/DDodrU2G4rU5D7 +JKQ+hPCe6F/s5SdE9SimP3ve4CrwyK9TL57KBQGTHo9mHDmnTfpatnMEJWbrd3/n +WcZKmSUUVOsmx/N/GdUwcI+vsEYq/63rKe3Xn6oEh6PU+YmlNF/bQ5GCNtlmPLG4 +uYL9nDo+EMg77wZlZnqbGRg9/3FRPDAuX749d3OyXQZswyNWmiuFJpIcpwKz5D8N +rwh5grg2Peqc0zWzvGnK9cyd6P1kjReAM25eSl2ZyR6HtJ0awNVuEzUjXt+bXz3v +1vd2wuo+u3gNHEJnawTY+Nbab4vyRKABqwIBA6OCAfMwggHvMB0GA1UdDgQWBBS/ +X7fRzt0fhvRbVazc1xDCDqmI5zCB0gYDVR0jBIHKMIHHoYHBpIG+MIG7MSQwIgYD +VQQHExtWYWxpQ2VydCBWYWxpZGF0aW9uIE5ldHdvcmsxFzAVBgNVBAoTDlZhbGlD +ZXJ0LCBJbmMuMTUwMwYDVQQLEyxWYWxpQ2VydCBDbGFzcyAyIFBvbGljeSBWYWxp +ZGF0aW9uIEF1dGhvcml0eTEhMB8GA1UEAxMYaHR0cDovL3d3dy52YWxpY2VydC5j +b20vMSAwHgYJKoZIhvcNAQkBFhFpbmZvQHZhbGljZXJ0LmNvbYIBATAPBgNVHRMB +Af8EBTADAQH/MDkGCCsGAQUFBwEBBC0wKzApBggrBgEFBQcwAYYdaHR0cDovL29j +c3Auc3RhcmZpZWxkdGVjaC5jb20wSgYDVR0fBEMwQTA/oD2gO4Y5aHR0cDovL2Nl +cnRpZmljYXRlcy5zdGFyZmllbGR0ZWNoLmNvbS9yZXBvc2l0b3J5L3Jvb3QuY3Js +MFEGA1UdIARKMEgwRgYEVR0gADA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY2VydGlm +aWNhdGVzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9zaXRvcnkwDgYDVR0PAQH/BAQD +AgEGMA0GCSqGSIb3DQEBBQUAA4GBAKVi8afCXSWlcD284ipxs33kDTcdVWptobCr +mADkhWBKIMuh8D1195TaQ39oXCUIuNJ9MxB73HZn8bjhU3zhxoNbKXuNSm8uf0So +GkVrMgfHeMpkksK0hAzc3S1fTbvdiuo43NlmouxBulVtWmQ9twPMHOKRUJ7jCUSV +FxdzPcwl +-----END CERTIFICATE----- +)CERT"; + + + + +// end of certificate chain for tls.mbed.org:443 +//////////////////////////////////////////////////////////// + diff --git a/libraries/ESP8266SdFat b/libraries/ESP8266SdFat index 46151c90a4..eaab1369d5 160000 --- a/libraries/ESP8266SdFat +++ b/libraries/ESP8266SdFat @@ -1 +1 @@ -Subproject commit 46151c90a410a6f983f2f8c147e13086aaecdd8e +Subproject commit eaab1369d5b988d844888bc560967ae143847d5d diff --git a/libraries/ESP8266WebServer/README.rst b/libraries/ESP8266WebServer/README.rst index 94bb6c5865..c37c6d5e64 100644 --- a/libraries/ESP8266WebServer/README.rst +++ b/libraries/ESP8266WebServer/README.rst @@ -54,8 +54,10 @@ Client request handlers .. code:: cpp - void on(); + RequestHandler& on(); + bool removeRoute(); void addHandler(); + bool removeHandler(); void onNotFound(); void onFileUpload(); @@ -64,9 +66,26 @@ Client request handlers .. code:: cpp server.on("/", handlerFunction); + server.removeRoute("/"); // Removes any route which points to "/" and has HTTP_ANY attribute + server.removeRoute("/", HTTP_GET); // Removes any route which points to "/" and has HTTP_GET attribute server.onNotFound(handlerFunction); // called when handler is not assigned server.onFileUpload(handlerFunction); // handle file uploads +Client request filters +^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: cpp + + RequestHandler& setFilter(); + +*Example:* + +More details about this in `Filters.ino` example. + +.. code:: cpp + + server.on("/", handlerFunction).setFilter(ON_AP_FILTER) + Sending responses to the client ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/libraries/ESP8266WebServer/examples/Filters/Filters.ino b/libraries/ESP8266WebServer/examples/Filters/Filters.ino new file mode 100644 index 0000000000..24c26fc276 --- /dev/null +++ b/libraries/ESP8266WebServer/examples/Filters/Filters.ino @@ -0,0 +1,101 @@ +#include +#include +#include +#include + +// Your STA WiFi Credentials +// ( This is the AP your ESP will connect to ) +const char *ssid = "..."; +const char *password = "..."; + +// Your AP WiFi Credentials +// ( This is the AP your ESP will broadcast ) +const char *ap_ssid = "ESP8266_Demo"; +const char *ap_password = ""; + +ESP8266WebServer server(80); + +const int led = 13; + +// ON_STA_FILTER - Only accept requests coming from STA interface +bool ON_STA_FILTER(ESP8266WebServer &server) { + return WiFi.localIP() == server.client().localIP(); +} + +// ON_AP_FILTER - Only accept requests coming from AP interface +bool ON_AP_FILTER(ESP8266WebServer &server) { + return WiFi.softAPIP() == server.client().localIP(); +} + +void handleNotFound() { + digitalWrite(led, 1); + String message = "File Not Found\n\n"; + message += "URI: "; + message += server.uri(); + message += "\nMethod: "; + message += (server.method() == HTTP_GET) ? "GET" : "POST"; + message += "\nArguments: "; + message += server.args(); + message += "\n"; + for (uint8_t i = 0; i < server.args(); i++) { + message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; + } + server.send(404, "text/plain", message); + digitalWrite(led, 0); +} + +void setup(void) { + pinMode(led, OUTPUT); + digitalWrite(led, 0); + Serial.begin(115200); + WiFi.mode(WIFI_AP_STA); + // Connect to STA + WiFi.begin(ssid, password); + // Start AP + WiFi.softAP(ap_ssid, ap_password); + Serial.println(""); + + // Wait for connection + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(""); + Serial.print("Connected to "); + Serial.println(ssid); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + + if (MDNS.begin("esp8266")) { + Serial.println("MDNS responder started"); + } + + // This route will be accessible by STA clients only + server.on("/", [&]() { + digitalWrite(led, 1); + server.send(200, "text/plain", "Hi!, This route is accessible for STA clients only"); + digitalWrite(led, 0); + }) + .setFilter(ON_STA_FILTER); + + // This route will be accessible by AP clients only + server.on("/", [&]() { + digitalWrite(led, 1); + server.send(200, "text/plain", "Hi!, This route is accessible for AP clients only"); + digitalWrite(led, 0); + }) + .setFilter(ON_AP_FILTER); + + server.on("/inline", []() { + server.send(200, "text/plain", "this works as well"); + }); + + server.onNotFound(handleNotFound); + + server.begin(); + Serial.println("HTTP server started"); +} + +void loop(void) { + server.handleClient(); +} diff --git a/libraries/ESP8266WebServer/examples/WebServer/README.md b/libraries/ESP8266WebServer/examples/WebServer/README.md index 68999100ab..0c7b1143da 100644 --- a/libraries/ESP8266WebServer/examples/WebServer/README.md +++ b/libraries/ESP8266WebServer/examples/WebServer/README.md @@ -21,8 +21,6 @@ It features * Only files in the root folder are supported for simplicity - no directories. - - ## Implementing a web server The ESP8266WebServer library offers a simple path to implement a web server on a ESP8266 board. @@ -90,7 +88,7 @@ that actually has only one line of functionality by sending a string as result t > }); > ``` -Here the text from a static String with html code is returned instead of a file from the filesystem. +Here the text from a static string with html code is returned instead of a file from the filesystem. The content of this string can be found in the file `builtinfiles.h`. It contains a small html+javascript implementation that allows uploading new files into the empty filesystem. @@ -100,14 +98,14 @@ Just open and drag some files from the data folde ## Registering a function to handle requests to the server without a path Often servers are addressed by using the base URL like where no further path details is given. -Of course we like the user to be redirected to something usable. Therefore the `handleRoot()` function is registered: +Of course we like the user to be redirected to something usable. Therefore the `handleRedirect()` function is registered: > ```CPP -> server.on("/$upload.htm", handleRoot); +> server.on("/", HTTP_GET, handleRedirect); > ``` -The `handleRoot()` function checks the filesystem for the file named **/index.htm** and creates a redirect to this file when the file exists. -Otherwise the redirection goes to the built-in **/$upload.htm** web page. +The `handleRedirect()` function checks the filesystem for the file named **/index.htm** and creates a redirect +response to this file when the file exists. Otherwise the redirection goes to the built-in **/$upload.htm** web page. @@ -122,7 +120,7 @@ The **serveStatic** plug in is part of the library and handles delivering files > ``` -### Cross-Origin Ressource Sharing (CORS) +### Cross-Origin Resource Sharing (CORS) The `enableCORS(true)` function adds a `Access-Control-Allow-Origin: *` http-header to all responses to the client to inform that it is allowed to call URLs and services on this server from other web sites. diff --git a/libraries/ESP8266WebServer/examples/WebServer/WebServer.ino b/libraries/ESP8266WebServer/examples/WebServer/WebServer.ino index 81a58478f9..9e88e96f1f 100644 --- a/libraries/ESP8266WebServer/examples/WebServer/WebServer.ino +++ b/libraries/ESP8266WebServer/examples/WebServer/WebServer.ino @@ -41,10 +41,9 @@ void handleRedirect() { TRACE("Redirect..."); String url = "/index.htm"; - if (!LittleFS.exists(url)) { url = "/$update.htm"; } + if (!LittleFS.exists(url)) { url = "/$upload.htm"; } - server.sendHeader("Location", url, true); - server.send(302); + server.redirect(url); } // handleRedirect() @@ -105,7 +104,7 @@ public: // @brief check incoming request. Can handle POST for uploads and DELETE. // @param requestMethod method of the http request line. - // @param requestUri request ressource from the http request line. + // @param requestUri request resource from the http request line. // @return true when method can be handled. bool canHandle(HTTPMethod requestMethod, const String UNUSED &_uri) override { return ((requestMethod == HTTP_POST) || (requestMethod == HTTP_DELETE)); @@ -154,7 +153,7 @@ public: // Close the file if (_fsUploadFile) { _fsUploadFile.close(); } } // if - } // upload() + } // upload() protected: File _fsUploadFile; diff --git a/libraries/ESP8266WebServer/examples/WebServer/builtinfiles.h b/libraries/ESP8266WebServer/examples/WebServer/builtinfiles.h index 210b18c1a5..2daf9e3819 100644 --- a/libraries/ESP8266WebServer/examples/WebServer/builtinfiles.h +++ b/libraries/ESP8266WebServer/examples/WebServer/builtinfiles.h @@ -54,10 +54,10 @@ R"==( static const char notFoundContent[] PROGMEM = R"==( - Ressource not found + Resource not found -

The ressource was not found.

+

The resource was not found.

Start again

)=="; diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h b/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h index f7a95da0af..dbcb251135 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h @@ -230,18 +230,61 @@ void ESP8266WebServerTemplate::requestAuthentication(HTTPAuthMethod } template -void ESP8266WebServerTemplate::on(const Uri &uri, ESP8266WebServerTemplate::THandlerFunction handler) { - on(uri, HTTP_ANY, handler); +RequestHandler& ESP8266WebServerTemplate::on(const Uri &uri, ESP8266WebServerTemplate::THandlerFunction handler) { + return on(uri, HTTP_ANY, handler); } template -void ESP8266WebServerTemplate::on(const Uri &uri, HTTPMethod method, ESP8266WebServerTemplate::THandlerFunction fn) { - on(uri, method, fn, _fileUploadHandler); +RequestHandler& ESP8266WebServerTemplate::on(const Uri &uri, HTTPMethod method, ESP8266WebServerTemplate::THandlerFunction fn) { + return on(uri, method, fn, _fileUploadHandler); } template -void ESP8266WebServerTemplate::on(const Uri &uri, HTTPMethod method, ESP8266WebServerTemplate::THandlerFunction fn, ESP8266WebServerTemplate::THandlerFunction ufn) { - _addRequestHandler(new FunctionRequestHandler(fn, ufn, uri, method)); +RequestHandler& ESP8266WebServerTemplate::on(const Uri &uri, HTTPMethod method, ESP8266WebServerTemplate::THandlerFunction fn, ESP8266WebServerTemplate::THandlerFunction ufn) { + RequestHandler *handler = new FunctionRequestHandler(fn, ufn, uri, method); + _addRequestHandler(handler); + return *handler; +} + +template +bool ESP8266WebServerTemplate::removeRoute(const char *uri) { + return removeRoute(String(uri), HTTP_ANY); +} + +template +bool ESP8266WebServerTemplate::removeRoute(const char *uri, HTTPMethod method) { + return removeRoute(String(uri), method); +} + +template +bool ESP8266WebServerTemplate::removeRoute(const String &uri) { + return removeRoute(uri, HTTP_ANY); +} + +template +bool ESP8266WebServerTemplate::removeRoute(const String &uri, HTTPMethod method) { + bool anyHandlerRemoved = false; + RequestHandlerType *handler = _firstHandler; + RequestHandlerType *previousHandler = nullptr; + + while (handler) { + if (handler->canHandle(method, uri)) { + if (_removeRequestHandler(handler)) { + anyHandlerRemoved = true; + // Move to the next handler + if (previousHandler) { + handler = previousHandler->next(); + } else { + handler = _firstHandler; + } + continue; + } + } + previousHandler = handler; + handler = handler->next(); + } + + return anyHandlerRemoved; } template @@ -249,6 +292,11 @@ void ESP8266WebServerTemplate::addHandler(RequestHandlerType* handle _addRequestHandler(handler); } +template +bool ESP8266WebServerTemplate::removeHandler(RequestHandlerType *handler) { + return _removeRequestHandler(handler); +} + template void ESP8266WebServerTemplate::_addRequestHandler(RequestHandlerType* handler) { if (!_lastHandler) { @@ -261,6 +309,33 @@ void ESP8266WebServerTemplate::_addRequestHandler(RequestHandlerType } } +template +bool ESP8266WebServerTemplate::_removeRequestHandler(RequestHandlerType *handler) { + RequestHandlerType *current = _firstHandler; + RequestHandlerType *previous = nullptr; + + while (current != nullptr) { + if (current == handler) { + if (previous == nullptr) { + _firstHandler = current->next(); + } else { + previous->next(current->next()); + } + + if (current == _lastHandler) { + _lastHandler = previous; + } + + // Delete 'matching' handler + delete current; + return true; + } + previous = current; + current = current->next(); + } + return false; +} + template void ESP8266WebServerTemplate::serveStatic(const char* uri, FS& fs, const char* path, const char* cache_header) { bool is_file = false; diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.h b/libraries/ESP8266WebServer/src/ESP8266WebServer.h index 397132f161..f9eca1c8bc 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.h @@ -116,11 +116,17 @@ class ESP8266WebServerTemplate typedef std::function THandlerFunction; typedef std::function ETagFunction; - - void on(const Uri &uri, THandlerFunction handler); - void on(const Uri &uri, HTTPMethod method, THandlerFunction fn); - void on(const Uri &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn); + typedef std::function &server)> FilterFunction; + + RequestHandler& on(const Uri &uri, THandlerFunction handler); + RequestHandler& on(const Uri &uri, HTTPMethod method, THandlerFunction fn); + RequestHandler& on(const Uri &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn); + bool removeRoute(const char *uri); + bool removeRoute(const char *uri, HTTPMethod method); + bool removeRoute(const String &uri); + bool removeRoute(const String &uri, HTTPMethod method); void addHandler(RequestHandlerType* handler); + bool removeHandler(RequestHandlerType* handler); void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = NULL ); void onNotFound(THandlerFunction fn); //called when handler is not assigned void onFileUpload(THandlerFunction fn); //handle file uploads @@ -207,6 +213,30 @@ class ESP8266WebServerTemplate sendContent(emptyString); } + /** + * @brief Redirect to another URL, e.g. + * webserver.on("/index.html", HTTP_GET, []() { webserver.redirect("/"); }); + * There are 3 points of redirection here: + * 1) "Location" element in the header + * 2) Disable client caching + * 3) HTML "content" element to redirect + * If the "Location" header element works the HTML content is never seen. + * https://tools.ietf.org/html/rfc7231#section-6.4.3 + * @param url URL to redirect to + * @param content Optional redirect content + */ + void redirect(const String& url, const String& content = emptyString) { + sendHeader(F("Location"), url, true); + sendHeader(F("Cache-Control"), F("no-cache, no-store, must-revalidate")); + sendHeader(F("Pragma"), F("no-cache")); + sendHeader(F("Expires"), F("-1")); + send(302, F("text/html"), content); // send 302: "Found" + if (content.isEmpty()) { + // Empty content inhibits Content-length header so we have to close the socket ourselves. + client().stop(); // Stop is needed because we sent no content length + } + } + // Whether other requests should be accepted from the client on the // same socket after a response is sent. // This will automatically configure the "Connection" header of the response. @@ -282,6 +312,7 @@ class ESP8266WebServerTemplate protected: void _addRequestHandler(RequestHandlerType* handler); + bool _removeRequestHandler(RequestHandlerType *handler); void _handleRequest(); void _finalizeResponse(); ClientFuture _parseRequest(ClientType& client); diff --git a/libraries/ESP8266WebServer/src/Parsing-impl.h b/libraries/ESP8266WebServer/src/Parsing-impl.h index 238b7b72d3..672682706e 100644 --- a/libraries/ESP8266WebServer/src/Parsing-impl.h +++ b/libraries/ESP8266WebServer/src/Parsing-impl.h @@ -106,7 +106,7 @@ typename ESP8266WebServerTemplate::ClientFuture ESP8266WebServerTemp //attach handler RequestHandlerType* handler; for (handler = _firstHandler; handler; handler = handler->next()) { - if (handler->canHandle(_currentMethod, _currentUri)) + if (handler->canHandle(*this, _currentMethod, _currentUri)) break; } _currentHandler = handler; @@ -324,7 +324,7 @@ int ESP8266WebServerTemplate::_parseArgumentsPrivate(const String& d template void ESP8266WebServerTemplate::_uploadWriteByte(uint8_t b){ if (_currentUpload->currentSize == HTTP_UPLOAD_BUFLEN){ - if(_currentHandler && _currentHandler->canUpload(_currentUri)) + if(_currentHandler && _currentHandler->canUpload(*this, _currentUri)) _currentHandler->upload(*this, _currentUri, *_currentUpload); _currentUpload->totalSize += _currentUpload->currentSize; _currentUpload->currentSize = 0; @@ -425,7 +425,7 @@ bool ESP8266WebServerTemplate::_parseForm(ClientType& client, const _currentUpload->currentSize = 0; _currentUpload->contentLength = len; DBGWS("Start File: %s Type: %s\n", _currentUpload->filename.c_str(), _currentUpload->type.c_str()); - if(_currentHandler && _currentHandler->canUpload(_currentUri)) + if(_currentHandler && _currentHandler->canUpload(*this, _currentUri)) _currentHandler->upload(*this, _currentUri, *_currentUpload); _currentUpload->status = UPLOAD_FILE_WRITE; @@ -463,11 +463,11 @@ bool ESP8266WebServerTemplate::_parseForm(ClientType& client, const } } // Found the boundary string, finish processing this file upload - if (_currentHandler && _currentHandler->canUpload(_currentUri)) + if (_currentHandler && _currentHandler->canUpload(*this, _currentUri)) _currentHandler->upload(*this, _currentUri, *_currentUpload); _currentUpload->totalSize += _currentUpload->currentSize; _currentUpload->status = UPLOAD_FILE_END; - if (_currentHandler && _currentHandler->canUpload(_currentUri)) + if (_currentHandler && _currentHandler->canUpload(*this, _currentUri)) _currentHandler->upload(*this, _currentUri, *_currentUpload); DBGWS("End File: %s Type: %s Size: %d\n", _currentUpload->filename.c_str(), @@ -542,7 +542,7 @@ String ESP8266WebServerTemplate::urlDecode(const String& text) template bool ESP8266WebServerTemplate::_parseFormUploadAborted(){ _currentUpload->status = UPLOAD_FILE_ABORTED; - if(_currentHandler && _currentHandler->canUpload(_currentUri)) + if(_currentHandler && _currentHandler->canUpload(*this, _currentUri)) _currentHandler->upload(*this, _currentUri, *_currentUpload); return false; } diff --git a/libraries/ESP8266WebServer/src/detail/RequestHandler.h b/libraries/ESP8266WebServer/src/detail/RequestHandler.h index 4195f0ff3f..c373c58f1b 100644 --- a/libraries/ESP8266WebServer/src/detail/RequestHandler.h +++ b/libraries/ESP8266WebServer/src/detail/RequestHandler.h @@ -12,13 +12,60 @@ class RequestHandler { using WebServerType = ESP8266WebServerTemplate; public: virtual ~RequestHandler() { } - virtual bool canHandle(HTTPMethod method, const String& uri) { (void) method; (void) uri; return false; } - virtual bool canUpload(const String& uri) { (void) uri; return false; } - virtual bool handle(WebServerType& server, HTTPMethod requestMethod, const String& requestUri) { (void) server; (void) requestMethod; (void) requestUri; return false; } - virtual void upload(WebServerType& server, const String& requestUri, HTTPUpload& upload) { (void) server; (void) requestUri; (void) upload; } - RequestHandler* next() { return _next; } - void next(RequestHandler* r) { _next = r; } + /* + note: old handler API for backward compatibility + */ + + virtual bool canHandle(HTTPMethod method, const String& uri) { + (void) method; + (void) uri; + return false; + } + virtual bool canUpload(const String& uri) { + (void) uri; + return false; + } + + /* + note: new handler API with support for filters etc. + */ + + virtual bool canHandle(WebServerType& server, HTTPMethod method, const String& uri) { + (void) server; + (void) method; + (void) uri; + return false; + } + virtual bool canUpload(WebServerType& server, const String& uri) { + (void) server; + (void) uri; + return false; + } + virtual bool handle(WebServerType& server, HTTPMethod requestMethod, const String& requestUri) { + (void) server; + (void) requestMethod; + (void) requestUri; + return false; + } + virtual void upload(WebServerType& server, const String& requestUri, HTTPUpload& upload) { + (void) server; + (void) requestUri; + (void) upload; + } + + RequestHandler* next() { + return _next; + } + + void next(RequestHandler* r) { + _next = r; + } + + virtual RequestHandler& setFilter(std::function&)> filter) { + (void)filter; + return *this; + } private: RequestHandler* _next = nullptr; diff --git a/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h b/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h index bb06033dea..db1266f520 100644 --- a/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h +++ b/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h @@ -9,25 +9,7 @@ namespace esp8266webserver { -// calculate an ETag for a file in filesystem based on md5 checksum -// that can be used in the http headers - include quotes. -static String calcETag(FS &fs, const String &path) { - String result; - - // calculate eTag using md5 checksum - uint8_t md5_buf[16]; - File f = fs.open(path, "r"); - MD5Builder calcMD5; - calcMD5.begin(); - calcMD5.addStream(f, f.size()); - calcMD5.calculate(); - calcMD5.getBytes(md5_buf); - f.close(); - // create a minimal-length eTag using base64 byte[]->text encoding. - result = "\"" + base64::encode(md5_buf, 16, false) + "\""; - return(result); -} // calcETag - +String calcETag(FS &, const String &); template class FunctionRequestHandler : public RequestHandler { @@ -59,9 +41,22 @@ class FunctionRequestHandler : public RequestHandler { return true; } + bool canHandle(WebServerType& server, HTTPMethod requestMethod, const String& requestUri) override { + if (_method != HTTP_ANY && _method != requestMethod) + return false; + + return _uri->canHandle(requestUri, RequestHandler::pathArgs) && (_filter != NULL ? _filter(server) : true); + } + + bool canUpload(WebServerType& server, const String& requestUri) override { + if (!_ufn || !canHandle(server, HTTP_POST, requestUri)) + return false; + + return true; + } + bool handle(WebServerType& server, HTTPMethod requestMethod, const String& requestUri) override { - (void) server; - if (!canHandle(requestMethod, requestUri)) + if (!canHandle(server, requestMethod, requestUri)) return false; _fn(); @@ -69,15 +64,22 @@ class FunctionRequestHandler : public RequestHandler { } void upload(WebServerType& server, const String& requestUri, HTTPUpload& upload) override { - (void) server; (void) upload; - if (canUpload(requestUri)) + if (canUpload(server, requestUri)) _ufn(); } + FunctionRequestHandler& setFilter(typename WebServerType::FilterFunction filter) { + _filter = filter; + return *this; + } + protected: typename WebServerType::THandlerFunction _fn; typename WebServerType::THandlerFunction _ufn; + // _filter should return 'true' when the request should be handled + // and 'false' when the request should be ignored + typename WebServerType::FilterFunction _filter; Uri *_uri; HTTPMethod _method; }; @@ -115,7 +117,6 @@ class StaticRequestHandler : public RequestHandler { // serve all files within a given directory template class StaticDirectoryRequestHandler : public StaticRequestHandler { - using SRH = StaticRequestHandler; using WebServerType = ESP8266WebServerTemplate; @@ -130,9 +131,12 @@ class StaticDirectoryRequestHandler : public StaticRequestHandler { return SRH::validMethod(requestMethod) && requestUri.startsWith(SRH::_uri); } - bool handle(WebServerType& server, HTTPMethod requestMethod, const String& requestUri) override { + bool canHandle(WebServerType& server, HTTPMethod requestMethod, const String& requestUri) override { + return SRH::validMethod(requestMethod) && requestUri.startsWith(SRH::_uri) && (_filter != NULL ? _filter(server) : true); + } - if (!canHandle(requestMethod, requestUri)) + bool handle(WebServerType& server, HTTPMethod requestMethod, const String& requestUri) override { + if (!canHandle(server, requestMethod, requestUri)) return false; DEBUGV("DirectoryRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), SRH::_uri.c_str()); @@ -203,8 +207,14 @@ class StaticDirectoryRequestHandler : public StaticRequestHandler { return true; } + StaticDirectoryRequestHandler& setFilter(typename WebServerType::FilterFunction filter) { + _filter = filter; + return *this; + } + protected: size_t _baseUriLength; + typename WebServerType::FilterFunction _filter; }; @@ -228,8 +238,12 @@ public StaticRequestHandler { return SRH::validMethod(requestMethod) && requestUri == SRH::_uri; } + bool canHandle(WebServerType& server, HTTPMethod requestMethod, const String& requestUri) override { + return SRH::validMethod(requestMethod) && requestUri == SRH::_uri && (_filter != NULL ? _filter(server) : true); + } + bool handle(WebServerType& server, HTTPMethod requestMethod, const String & requestUri) override { - if (!canHandle(requestMethod, requestUri)) + if (!canHandle(server, requestMethod, requestUri)) return false; if (server._eTagEnabled) { @@ -266,10 +280,16 @@ public StaticRequestHandler { return true; } + StaticFileRequestHandler& setFilter(typename WebServerType::FilterFunction filter) { + _filter = filter; + return *this; + } + protected: String _eTagCode; // ETag code calculated for this file as used in http header include quotes. + typename WebServerType::FilterFunction _filter; }; } // namespace -#endif //REQUESTHANDLERSIMPL_H \ No newline at end of file +#endif //REQUESTHANDLERSIMPL_H diff --git a/libraries/ESP8266WebServer/src/detail/etag.cpp b/libraries/ESP8266WebServer/src/detail/etag.cpp new file mode 100644 index 0000000000..77c9e83b0e --- /dev/null +++ b/libraries/ESP8266WebServer/src/detail/etag.cpp @@ -0,0 +1,24 @@ +#include + +namespace esp8266webserver { + +// calculate an ETag for a file in filesystem based on md5 checksum +// that can be used in the http headers - include quotes. +String calcETag(FS &fs, const String &path) { + String result; + + // calculate eTag using md5 checksum + uint8_t md5_buf[16]; + File f = fs.open(path, "r"); + MD5Builder calcMD5; + calcMD5.begin(); + calcMD5.addStream(f, f.size()); + calcMD5.calculate(); + calcMD5.getBytes(md5_buf); + f.close(); + // create a minimal-length eTag using base64 byte[]->text encoding. + result = "\"" + base64::encode(md5_buf, 16, false) + "\""; + return(result); +} + +} // namespace esp8266webserver diff --git a/libraries/ESP8266WebServer/src/uri/UriRegex.h b/libraries/ESP8266WebServer/src/uri/UriRegex.h index eef1b516d4..e29eeb5cd6 100644 --- a/libraries/ESP8266WebServer/src/uri/UriRegex.h +++ b/libraries/ESP8266WebServer/src/uri/UriRegex.h @@ -2,8 +2,9 @@ #define URI_REGEX_H #include "Uri.h" + +#include #include -#include #ifndef REGEX_MAX_GROUPS #define REGEX_MAX_GROUPS 10 @@ -12,13 +13,20 @@ class UriRegex : public Uri { private: - regex_t _regexCompiled; + regex_t _regexCompiled{}; + int _regexErr{REG_EMPTY}; public: - explicit UriRegex(const char *uri) : Uri(uri) { - assert(regcomp(&_regexCompiled, uri, REG_EXTENDED) == 0); - }; - explicit UriRegex(const String &uri) : UriRegex(uri.c_str()) {}; + UriRegex() = delete; + + explicit UriRegex(const char *uri) : + Uri(uri), + _regexErr(regcomp(&_regexCompiled, uri, REG_EXTENDED)) + { + assert(_regexErr == 0); + } + + explicit UriRegex(const String &uri) : UriRegex(uri.c_str()) {} ~UriRegex() { regfree(&_regexCompiled); @@ -26,15 +34,17 @@ class UriRegex : public Uri { Uri* clone() const override final { return new UriRegex(_uri); - }; + } bool canHandle(const String &requestUri, std::vector &pathArgs) override final { if (Uri::canHandle(requestUri, pathArgs)) return true; + if (_regexErr != 0) + return false; + regmatch_t groupArray[REGEX_MAX_GROUPS]; if (regexec(&_regexCompiled, requestUri.c_str(), REGEX_MAX_GROUPS, groupArray, 0) == 0) { - // matches pathArgs.clear(); unsigned int g = 1; diff --git a/libraries/ESP8266WiFi/examples/BearSSL_CertStore/certs-from-mozilla.py b/libraries/ESP8266WiFi/examples/BearSSL_CertStore/certs-from-mozilla.py index 80dbc48387..edf3aa705d 100755 --- a/libraries/ESP8266WiFi/examples/BearSSL_CertStore/certs-from-mozilla.py +++ b/libraries/ESP8266WiFi/examples/BearSSL_CertStore/certs-from-mozilla.py @@ -13,15 +13,9 @@ import sys from shutil import which -from subprocess import Popen, PIPE, call -try: - from urllib.request import urlopen -except Exception: - from urllib2 import urlopen -try: - from StringIO import StringIO -except Exception: - from io import StringIO +from io import StringIO +from subprocess import Popen, PIPE, call, CalledProcessError +from urllib.request import urlopen # check if ar and openssl are available if which('ar') is None and not os.path.isfile('./ar') and not os.path.isfile('./ar.exe'): @@ -62,10 +56,10 @@ thisPem = pems[i].replace("'", "") print(names[i] + " -> " + certName) ssl = Popen(['openssl','x509','-inform','PEM','-outform','DER','-out', certName], shell = False, stdin = PIPE) - pipe = ssl.stdin - pipe.write(thisPem.encode('utf-8')) - pipe.close() - ssl.wait() + ssl.communicate(thisPem.encode('utf-8')) + ret = ssl.wait() + if ret != 0: + raise CalledProcessError(ret, certName) if os.path.exists(certName): derFiles.append(certName) idx = idx + 1 diff --git a/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino b/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino index cc76e5d24e..2f6c08a51a 100644 --- a/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino +++ b/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino @@ -12,7 +12,7 @@ #define FINGERPRINT fingerprint_www_example_org #define PUBKEY pubkey_www_example_org -#define CERT cert_DigiCert_TLS_RSA_SHA256_2020_CA1 +#define CERT cert_DigiCert_Global_G2_TLS_RSA_SHA256_2020_CA1 #ifndef STASSID #define STASSID "your-ssid" diff --git a/libraries/ESP8266WiFi/examples/BearSSL_Validation/certs.h b/libraries/ESP8266WiFi/examples/BearSSL_Validation/certs.h index 74f244c4eb..c6b99041a4 100644 --- a/libraries/ESP8266WiFi/examples/BearSSL_Validation/certs.h +++ b/libraries/ESP8266WiFi/examples/BearSSL_Validation/certs.h @@ -1,7 +1,7 @@ // this file is autogenerated - any modification will be overwritten // unused symbols will not be linked in the final binary -// generated on 2023-03-20 23:02:42 +// generated on 2024-07-30 23:28:11 // by ['../../../../tools/cert.py', '-s', 'www.example.com', '-n', 'SSL'] #pragma once @@ -13,82 +13,82 @@ const char* SSL_host = "www.example.com"; const uint16_t SSL_port = 443; // CN: www.example.org => name: www_example_org -// not valid before: 2023-01-13 00:00:00 -// not valid after: 2024-02-13 23:59:59 -const char fingerprint_www_example_org [] PROGMEM = "f2:aa:d7:3d:32:68:3b:71:6d:2a:7d:61:b5:1c:6d:57:64:ab:38:99"; +// not valid before: 2024-01-30 00:00:00+00:00 +// not valid after: 2025-03-01 23:59:59+00:00 +const char fingerprint_www_example_org [] PROGMEM = "4d:a2:5a:6d:5e:f6:2c:5f:95:c7:bd:0a:73:ea:3c:17:7b:36:99:9d"; const char pubkey_www_example_org [] PROGMEM = R"PUBKEY( -----BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwoB3iVm4RW+6StkR+nut -x1fQevu2+t0Fu6KBcbvhfyHSXy7w0nJOdTT4jWLjStpRkNQBPZwMwHH35i+21gdn -JtDe/xfO8IX9McFmyodlBUcqX8CruIzDv9AXf2OjXPBG+4aq+03XKl5/muATl32+ -+301Vw1dXoGYNeoWQqLTsHT3WS3tOOf+ehuzNuZ+rj+ephaD3lMBToEArrtC9R91 -KTTN6YSAOK48NxTA8CfOMFK5itxfIqB5+E9OSQTidXyqLyoeA+xxTKMqYfxvypEe -k1oueAhY9u67NCBdmuavxtfyvwp7+o6Sd+NsewxAhmRKFexw13KOYzDhC+9aMJcu -JQIDAQAB +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhoUPuw75yl/Z9eAKMiwz +2aoOBymoLwiteL3CBr/3LSumpyc9U6ZMw0uyJ3cg1sFUSbgI2vlwqWH2skmdaVfa ++20kNHIuR/AEP52xW+K8ZjFZMuapfr/UsNRk9WvKe/9yW16a2D/UBrLzyNyPZlpG +hGaoGBV5pwjOBTz7OYnvbfpOcVJ7t+SgpJyWwGE9pApwTcOOzW6zMmzyx0QJBN2g +Vf0jpSB4soVe2DutF/+Fxbl0jTO5uFdutbxpZdsLPJJVmfRztGQkymdMKJnM3Gc9 +eccWnCvmq6qqNXI39oEqSOg/Thmav55GqjKT/6WyWrSxLx5phJIdsLmNr/IxbJWG +8wIDAQAB -----END PUBLIC KEY----- )PUBKEY"; -// http://cacerts.digicert.com/DigiCertTLSRSASHA2562020CA1-1.crt -// CN: DigiCert TLS RSA SHA256 2020 CA1 => name: DigiCert_TLS_RSA_SHA256_2020_CA1 -// not valid before: 2021-04-14 00:00:00 -// not valid after: 2031-04-13 23:59:59 -const char cert_DigiCert_TLS_RSA_SHA256_2020_CA1 [] PROGMEM = R"CERT( +// http://cacerts.digicert.com/DigiCertGlobalG2TLSRSASHA2562020CA1-1.crt +// CN: DigiCert Global G2 TLS RSA SHA256 2020 CA1 => name: DigiCert_Global_G2_TLS_RSA_SHA256_2020_CA1 +// not valid before: 2021-03-30 00:00:00+00:00 +// not valid after: 2031-03-29 23:59:59+00:00 +const char cert_DigiCert_Global_G2_TLS_RSA_SHA256_2020_CA1 [] PROGMEM = R"CERT( -----BEGIN CERTIFICATE----- -MIIEvjCCA6agAwIBAgIQBtjZBNVYQ0b2ii+nVCJ+xDANBgkqhkiG9w0BAQsFADBh +MIIEyDCCA7CgAwIBAgIQDPW9BitWAvR6uFAsI8zwZjANBgkqhkiG9w0BAQsFADBh MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD -QTAeFw0yMTA0MTQwMDAwMDBaFw0zMTA0MTMyMzU5NTlaME8xCzAJBgNVBAYTAlVT -MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxKTAnBgNVBAMTIERpZ2lDZXJ0IFRMUyBS -U0EgU0hBMjU2IDIwMjAgQ0ExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC -AQEAwUuzZUdwvN1PWNvsnO3DZuUfMRNUrUpmRh8sCuxkB+Uu3Ny5CiDt3+PE0J6a -qXodgojlEVbbHp9YwlHnLDQNLtKS4VbL8Xlfs7uHyiUDe5pSQWYQYE9XE0nw6Ddn -g9/n00tnTCJRpt8OmRDtV1F0JuJ9x8piLhMbfyOIJVNvwTRYAIuE//i+p1hJInuW -raKImxW8oHzf6VGo1bDtN+I2tIJLYrVJmuzHZ9bjPvXj1hJeRPG/cUJ9WIQDgLGB -Afr5yjK7tI4nhyfFK3TUqNaX3sNk+crOU6JWvHgXjkkDKa77SU+kFbnO8lwZV21r -eacroicgE7XQPUDTITAHk+qZ9QIDAQABo4IBgjCCAX4wEgYDVR0TAQH/BAgwBgEB -/wIBADAdBgNVHQ4EFgQUt2ui6qiqhIx56rTaD5iyxZV2ufQwHwYDVR0jBBgwFoAU -A95QNVbRTLtm8KPiGxvDl7I90VUwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQG -CCsGAQUFBwMBBggrBgEFBQcDAjB2BggrBgEFBQcBAQRqMGgwJAYIKwYBBQUHMAGG -GGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBABggrBgEFBQcwAoY0aHR0cDovL2Nh -Y2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xvYmFsUm9vdENBLmNydDBCBgNV -HR8EOzA5MDegNaAzhjFodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRH -bG9iYWxSb290Q0EuY3JsMD0GA1UdIAQ2MDQwCwYJYIZIAYb9bAIBMAcGBWeBDAEB -MAgGBmeBDAECATAIBgZngQwBAgIwCAYGZ4EMAQIDMA0GCSqGSIb3DQEBCwUAA4IB -AQCAMs5eC91uWg0Kr+HWhMvAjvqFcO3aXbMM9yt1QP6FCvrzMXi3cEsaiVi6gL3z -ax3pfs8LulicWdSQ0/1s/dCYbbdxglvPbQtaCdB73sRD2Cqk3p5BJl+7j5nL3a7h -qG+fh/50tx8bIKuxT8b1Z11dmzzp/2n3YWzW2fP9NsarA4h20ksudYbj/NhVfSbC -EXffPgK2fPOre3qGNm+499iTcc+G33Mw+nur7SpZyEKEOxEXGlLzyQ4UfaJbcme6 -ce1XR2bFuAJKZTRei9AqPCCcUZlM51Ke92sRKw2Sfh3oius2FkOH6ipjv3U/697E -A7sKPPcw7+uvTPyLNhBzPvOk +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0yMTAzMzAwMDAwMDBaFw0zMTAzMjkyMzU5NTlaMFkxCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxMzAxBgNVBAMTKkRpZ2lDZXJ0IEdsb2Jh +bCBHMiBUTFMgUlNBIFNIQTI1NiAyMDIwIENBMTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAMz3EGJPprtjb+2QUlbFbSd7ehJWivH0+dbn4Y+9lavyYEEV +cNsSAPonCrVXOFt9slGTcZUOakGUWzUb+nv6u8W+JDD+Vu/E832X4xT1FE3LpxDy +FuqrIvAxIhFhaZAmunjZlx/jfWardUSVc8is/+9dCopZQ+GssjoP80j812s3wWPc +3kbW20X+fSP9kOhRBx5Ro1/tSUZUfyyIxfQTnJcVPAPooTncaQwywa8WV0yUR0J8 +osicfebUTVSvQpmowQTCd5zWSOTOEeAqgJnwQ3DPP3Zr0UxJqyRewg2C/Uaoq2yT +zGJSQnWS+Jr6Xl6ysGHlHx+5fwmY6D36g39HaaECAwEAAaOCAYIwggF+MBIGA1Ud +EwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFHSFgMBmx9833s+9KTeqAx2+7c0XMB8G +A1UdIwQYMBaAFE4iVCAYlebjbuYP+vq5Eu0GF485MA4GA1UdDwEB/wQEAwIBhjAd +BgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwdgYIKwYBBQUHAQEEajBoMCQG +CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQAYIKwYBBQUHMAKG +NGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RH +Mi5jcnQwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2NybDMuZGlnaWNlcnQuY29t +L0RpZ2lDZXJ0R2xvYmFsUm9vdEcyLmNybDA9BgNVHSAENjA0MAsGCWCGSAGG/WwC +ATAHBgVngQwBATAIBgZngQwBAgEwCAYGZ4EMAQICMAgGBmeBDAECAzANBgkqhkiG +9w0BAQsFAAOCAQEAkPFwyyiXaZd8dP3A+iZ7U6utzWX9upwGnIrXWkOH7U1MVl+t +wcW1BSAuWdH/SvWgKtiwla3JLko716f2b4gp/DA/JIS7w7d7kwcsr4drdjPtAFVS +slme5LnQ89/nD/7d+MS5EHKBCQRfz5eeLjJ1js+aWNJXMX43AYGyZm0pGrFmCW3R +bpD0ufovARTFXFZkAdl9h6g4U5+LXUZtXMYnhIHUfoyMo5tS58aI7Dd8KvvwVVo4 +chDYABPPTHPbqjc1qCmBaZx2vN4Ye5DUys/vZwP9BFohFrH/6j/f3IL16/RZkiMN +JCqVJUzKoZHm1Lesh3Sz8W2jmdv51b2EQJ8HmA== -----END CERTIFICATE----- )CERT"; -// http://cacerts.digicert.com/DigiCertGlobalRootCA.crt -// CN: DigiCert Global Root CA => name: DigiCert_Global_Root_CA -// not valid before: 2006-11-10 00:00:00 -// not valid after: 2031-11-10 00:00:00 -const char cert_DigiCert_Global_Root_CA [] PROGMEM = R"CERT( +// http://cacerts.digicert.com/DigiCertGlobalRootG2.crt +// CN: DigiCert Global Root G2 => name: DigiCert_Global_Root_G2 +// not valid before: 2013-08-01 12:00:00+00:00 +// not valid after: 2038-01-15 12:00:00+00:00 +const char cert_DigiCert_Global_Root_G2 [] PROGMEM = R"CERT( -----BEGIN CERTIFICATE----- -MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD -QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j -b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB -CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 -nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt -43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P -T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 -gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO -BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR -TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw -DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr -hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg -06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF -PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls -YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk -CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI +2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx +1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ +q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz +tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ +vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV +5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY +1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 +NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG +Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 +8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe +pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= -----END CERTIFICATE----- )CERT"; diff --git a/libraries/ESP8266WiFi/examples/HTTPSRequest/HTTPSRequest.ino b/libraries/ESP8266WiFi/examples/HTTPSRequest/HTTPSRequest.ino index 696d513b52..750abfa733 100644 --- a/libraries/ESP8266WiFi/examples/HTTPSRequest/HTTPSRequest.ino +++ b/libraries/ESP8266WiFi/examples/HTTPSRequest/HTTPSRequest.ino @@ -13,6 +13,7 @@ #include #include + #include "certs.h" #ifndef STASSID @@ -23,7 +24,7 @@ const char* ssid = STASSID; const char* password = STAPSK; -X509List cert(cert_DigiCert_Global_Root_CA); +X509List cert(cert_Sectigo_ECC_Domain_Validation_Secure_Server_CA); void setup() { Serial.begin(115200); @@ -62,7 +63,7 @@ void setup() { Serial.print("Connecting to "); Serial.println(github_host); - Serial.printf("Using certificate: %s\n", cert_DigiCert_Global_Root_CA); + Serial.printf("Using certificate: %s\n", cert_Sectigo_ECC_Domain_Validation_Secure_Server_CA); client.setTrustAnchors(&cert); if (!client.connect(github_host, github_port)) { diff --git a/libraries/ESP8266WiFi/examples/HTTPSRequest/certs.h b/libraries/ESP8266WiFi/examples/HTTPSRequest/certs.h index f59ae673b3..97012d7a59 100644 --- a/libraries/ESP8266WiFi/examples/HTTPSRequest/certs.h +++ b/libraries/ESP8266WiFi/examples/HTTPSRequest/certs.h @@ -1,7 +1,7 @@ // this file is autogenerated - any modification will be overwritten // unused symbols will not be linked in the final binary -// generated on 2023-03-20 23:02:43 +// generated on 2024-07-30 23:28:12 // by ['../../../../tools/cert.py', '-s', 'api.github.com', '-n', 'github'] #pragma once @@ -13,73 +13,72 @@ const char* github_host = "api.github.com"; const uint16_t github_port = 443; // CN: *.github.com => name: __github_com -// not valid before: 2023-02-16 00:00:00 -// not valid after: 2024-03-15 23:59:59 -const char fingerprint___github_com [] PROGMEM = "2d:79:6c:90:2d:ad:8a:2e:4f:d1:e2:99:ed:e8:91:29:36:40:f8:58"; +// not valid before: 2024-03-07 00:00:00+00:00 +// not valid after: 2025-03-07 23:59:59+00:00 +const char fingerprint___github_com [] PROGMEM = "0d:f6:ec:50:fa:ed:ae:6e:13:af:82:94:52:f7:11:1b:0a:cf:7c:20"; const char pubkey___github_com [] PROGMEM = R"PUBKEY( -----BEGIN PUBLIC KEY----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEVB9Da7ntQj93REi9YoUrTvO/wrkz -xLleXrDjJGe1OI3tsrk+cWbZYAbwL6unbhZsAqbyBcr5BHK5M3vxZVSGRg== +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEcAMYSUSbAQpBM6MJN5kRD5gVpxvK +QgpD4jQ4jY1CqNOeWP7fOkn+PxdiJq76Qv5bPmv3tTxD6plhoNDYDohvMg== -----END PUBLIC KEY----- )PUBKEY"; -// http://cacerts.digicert.com/DigiCertTLSHybridECCSHA3842020CA1-1.crt -// CN: DigiCert TLS Hybrid ECC SHA384 2020 CA1 => name: DigiCert_TLS_Hybrid_ECC_SHA384_2020_CA1 -// not valid before: 2021-04-14 00:00:00 -// not valid after: 2031-04-13 23:59:59 -const char cert_DigiCert_TLS_Hybrid_ECC_SHA384_2020_CA1 [] PROGMEM = R"CERT( +// http://crt.sectigo.com/SectigoECCDomainValidationSecureServerCA.crt +// CN: Sectigo ECC Domain Validation Secure Server CA => name: Sectigo_ECC_Domain_Validation_Secure_Server_CA +// not valid before: 2018-11-02 00:00:00+00:00 +// not valid after: 2030-12-31 23:59:59+00:00 +const char cert_Sectigo_ECC_Domain_Validation_Secure_Server_CA [] PROGMEM = R"CERT( -----BEGIN CERTIFICATE----- -MIIEFzCCAv+gAwIBAgIQB/LzXIeod6967+lHmTUlvTANBgkqhkiG9w0BAQwFADBh -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD -QTAeFw0yMTA0MTQwMDAwMDBaFw0zMTA0MTMyMzU5NTlaMFYxCzAJBgNVBAYTAlVT -MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxMDAuBgNVBAMTJ0RpZ2lDZXJ0IFRMUyBI -eWJyaWQgRUNDIFNIQTM4NCAyMDIwIENBMTB2MBAGByqGSM49AgEGBSuBBAAiA2IA -BMEbxppbmNmkKaDp1AS12+umsmxVwP/tmMZJLwYnUcu/cMEFesOxnYeJuq20ExfJ -qLSDyLiQ0cx0NTY8g3KwtdD3ImnI8YDEe0CPz2iHJlw5ifFNkU3aiYvkA8ND5b8v -c6OCAYIwggF+MBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFAq8CCkXjKU5 -bXoOzjPHLrPt+8N6MB8GA1UdIwQYMBaAFAPeUDVW0Uy7ZvCj4hsbw5eyPdFVMA4G -A1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwdgYI -KwYBBQUHAQEEajBoMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5j -b20wQAYIKwYBBQUHMAKGNGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdp -Q2VydEdsb2JhbFJvb3RDQS5jcnQwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2Ny -bDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xvYmFsUm9vdENBLmNybDA9BgNVHSAE -NjA0MAsGCWCGSAGG/WwCATAHBgVngQwBATAIBgZngQwBAgEwCAYGZ4EMAQICMAgG -BmeBDAECAzANBgkqhkiG9w0BAQwFAAOCAQEAR1mBf9QbH7Bx9phdGLqYR5iwfnYr -6v8ai6wms0KNMeZK6BnQ79oU59cUkqGS8qcuLa/7Hfb7U7CKP/zYFgrpsC62pQsY -kDUmotr2qLcy/JUjS8ZFucTP5Hzu5sn4kL1y45nDHQsFfGqXbbKrAjbYwrwsAZI/ -BKOLdRHHuSm8EdCGupK8JvllyDfNJvaGEwwEqonleLHBTnm8dqMLUeTF0J5q/hos -Vq4GNiejcxwIfZMy0MJEGdqN9A57HSgDKwmKdsp33Id6rHtSJlWncg+d0ohP/rEh -xRqhqjn1VtvChMQ1H3Dau0bwhr9kAMQ+959GG50jBbl9s08PqUU643QwmA== +MIIDqDCCAy6gAwIBAgIRAPNkTmtuAFAjfglGvXvh9R0wCgYIKoZIzj0EAwMwgYgx +CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5MRQwEgYDVQQHEwtKZXJz +ZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMS4wLAYDVQQD +EyVVU0VSVHJ1c3QgRUNDIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTE4MTEw +MjAwMDAwMFoXDTMwMTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAkdCMRswGQYDVQQI +ExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGDAWBgNVBAoT +D1NlY3RpZ28gTGltaXRlZDE3MDUGA1UEAxMuU2VjdGlnbyBFQ0MgRG9tYWluIFZh +bGlkYXRpb24gU2VjdXJlIFNlcnZlciBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEH +A0IABHkYk8qfbZ5sVwAjBTcLXw9YWsTef1Wj6R7W2SUKiKAgSh16TwUwimNJE4xk +IQeV/To14UrOkPAY9z2vaKb71EijggFuMIIBajAfBgNVHSMEGDAWgBQ64QmG1M8Z +wpZ2dEl23OA1xmNjmjAdBgNVHQ4EFgQU9oUKOxGG4QR9DqoLLNLuzGR7e64wDgYD +VR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0lBBYwFAYIKwYB +BQUHAwEGCCsGAQUFBwMCMBsGA1UdIAQUMBIwBgYEVR0gADAIBgZngQwBAgEwUAYD +VR0fBEkwRzBFoEOgQYY/aHR0cDovL2NybC51c2VydHJ1c3QuY29tL1VTRVJUcnVz +dEVDQ0NlcnRpZmljYXRpb25BdXRob3JpdHkuY3JsMHYGCCsGAQUFBwEBBGowaDA/ +BggrBgEFBQcwAoYzaHR0cDovL2NydC51c2VydHJ1c3QuY29tL1VTRVJUcnVzdEVD +Q0FkZFRydXN0Q0EuY3J0MCUGCCsGAQUFBzABhhlodHRwOi8vb2NzcC51c2VydHJ1 +c3QuY29tMAoGCCqGSM49BAMDA2gAMGUCMEvnx3FcsVwJbZpCYF9z6fDWJtS1UVRs +cS0chWBNKPFNpvDKdrdKRe+oAkr2jU+ubgIxAODheSr2XhcA7oz9HmedGdMhlrd9 +4ToKFbZl+/OnFFzqnvOhcjHvClECEQcKmc8fmA== -----END CERTIFICATE----- )CERT"; -// http://cacerts.digicert.com/DigiCertGlobalRootCA.crt -// CN: DigiCert Global Root CA => name: DigiCert_Global_Root_CA -// not valid before: 2006-11-10 00:00:00 -// not valid after: 2031-11-10 00:00:00 -const char cert_DigiCert_Global_Root_CA [] PROGMEM = R"CERT( +// http://crt.usertrust.com/USERTrustECCAddTrustCA.crt +// CN: USERTrust ECC Certification Authority => name: USERTrust_ECC_Certification_Authority +// not valid before: 2019-03-12 00:00:00+00:00 +// not valid after: 2028-12-31 23:59:59+00:00 +const char cert_USERTrust_ECC_Certification_Authority [] PROGMEM = R"CERT( -----BEGIN CERTIFICATE----- -MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD -QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT -MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j -b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB -CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 -nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt -43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P -T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 -gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO -BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR -TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw -DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr -hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg -06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF -PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls -YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk -CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +MIID0zCCArugAwIBAgIQVmcdBOpPmUxvEIFHWdJ1lDANBgkqhkiG9w0BAQwFADB7 +MQswCQYDVQQGEwJHQjEbMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYD +VQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UE +AwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTE5MDMxMjAwMDAwMFoXDTI4 +MTIzMTIzNTk1OVowgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5 +MRQwEgYDVQQHEwtKZXJzZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBO +ZXR3b3JrMS4wLAYDVQQDEyVVU0VSVHJ1c3QgRUNDIENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEGqxUWqn5aCPnetUkb1PGWthL +q8bVttHmc3Gu3ZzWDGH926CJA7gFFOxXzu5dP+Ihs8731Ip54KODfi2X0GHE8Znc +JZFjq38wo7Rw4sehM5zzvy5cU7Ffs30yf4o043l5o4HyMIHvMB8GA1UdIwQYMBaA +FKARCiM+lvEH7OKvKe+CpX/QMKS0MB0GA1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1 +xmNjmjAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zARBgNVHSAECjAI +MAYGBFUdIAAwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5j +b20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNAYIKwYBBQUHAQEEKDAmMCQG +CCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wDQYJKoZIhvcNAQEM +BQADggEBABns652JLCALBIAdGN5CmXKZFjK9Dpx1WywV4ilAbe7/ctvbq5AfjJXy +ij0IckKJUAfiORVsAYfZFhr1wHUrxeZWEQff2Ji8fJ8ZOd+LygBkc7xGEJuTI42+ +FsMuCIKchjN0djsoTI0DQoWz4rIjQtUfenVqGtF8qmchxDM6OW1TyaLtYiKou+JV +bJlsQ2uRl9EMC5MCHdK8aXdJ5htN978UeAOwproLtOGFfy/cQjutdAFI3tZs4RmY +CV4Ks2dH/hzg1cEo70qLRDEmBDeNiXQ2Lu+lIg+DdEmSx/cQwgwp+7e9un/jX9Wf +8qn0dNW44bOwgeThpWOjzOoEeJBuv/c= -----END CERTIFICATE----- )CERT"; diff --git a/libraries/ESP8266WiFi/examples/WiFiEvents/WiFiEvents.ino b/libraries/ESP8266WiFi/examples/WiFiEvents/WiFiEvents.ino index dddfde9f5b..7d66553b61 100644 --- a/libraries/ESP8266WiFi/examples/WiFiEvents/WiFiEvents.ino +++ b/libraries/ESP8266WiFi/examples/WiFiEvents/WiFiEvents.ino @@ -24,6 +24,9 @@ const char* ssid = APSSID; const char* password = APPSK; +// WiFi.on* methods **must** only be called **after** entering setup(). +// Assigning immediately in global scope is not adviced, neither is assigning them within any other object constructors. +// These variables **may** exist in function block, but **only** if they are declared as `static` WiFiEventHandler stationConnectedHandler; WiFiEventHandler stationDisconnectedHandler; WiFiEventHandler probeRequestPrintHandler; @@ -43,12 +46,12 @@ void setup() { WiFi.mode(WIFI_AP); WiFi.softAP(ssid, password); - // Register event handlers. - // Callback functions will be called as long as these handler objects exist. // Call "onStationConnected" each time a station connects stationConnectedHandler = WiFi.onSoftAPModeStationConnected(&onStationConnected); + // Call "onStationDisconnected" each time a station disconnects stationDisconnectedHandler = WiFi.onSoftAPModeStationDisconnected(&onStationDisconnected); + // Call "onProbeRequestPrint" and "onProbeRequestBlink" each time // a probe request is received. // Former will print MAC address of the station and RSSI to Serial, diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h index d0525176bd..e0ad560d0e 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h @@ -71,15 +71,15 @@ class ESP8266WiFiGenericClass { void onEvent(WiFiEventCb cb, WiFiEvent_t event = WIFI_EVENT_ANY) __attribute__((deprecated)); // Subscribe to specific event and get event information as an argument to the callback - WiFiEventHandler onStationModeConnected(std::function); - WiFiEventHandler onStationModeDisconnected(std::function); - WiFiEventHandler onStationModeAuthModeChanged(std::function); - WiFiEventHandler onStationModeGotIP(std::function); - WiFiEventHandler onStationModeDHCPTimeout(std::function); - WiFiEventHandler onSoftAPModeStationConnected(std::function); - WiFiEventHandler onSoftAPModeStationDisconnected(std::function); - WiFiEventHandler onSoftAPModeProbeRequestReceived(std::function); - WiFiEventHandler onWiFiModeChange(std::function); + [[nodiscard]] WiFiEventHandler onStationModeConnected(std::function); + [[nodiscard]] WiFiEventHandler onStationModeDisconnected(std::function); + [[nodiscard]] WiFiEventHandler onStationModeAuthModeChanged(std::function); + [[nodiscard]] WiFiEventHandler onStationModeGotIP(std::function); + [[nodiscard]] WiFiEventHandler onStationModeDHCPTimeout(std::function); + [[nodiscard]] WiFiEventHandler onSoftAPModeStationConnected(std::function); + [[nodiscard]] WiFiEventHandler onSoftAPModeStationDisconnected(std::function); + [[nodiscard]] WiFiEventHandler onSoftAPModeProbeRequestReceived(std::function); + [[nodiscard]] WiFiEventHandler onWiFiModeChange(std::function); uint8_t channel(void); diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.h b/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.h index fab5829afd..3c77df02d4 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.h @@ -69,7 +69,7 @@ class ESP8266WiFiMulti wl_status_t run(uint32_t connectTimeoutMs=WIFI_CONNECT_TIMEOUT_MS); void cleanAPlist(); - + int count() { return _APlist.size(); } private: WifiAPlist _APlist; bool _firstRun; diff --git a/libraries/ESP8266WiFi/src/enable_wifi_at_boot_time.cpp b/libraries/ESP8266WiFi/src/enable_wifi_at_boot_time.cpp index 5eb62eec15..a8589c4d25 100644 --- a/libraries/ESP8266WiFi/src/enable_wifi_at_boot_time.cpp +++ b/libraries/ESP8266WiFi/src/enable_wifi_at_boot_time.cpp @@ -21,7 +21,7 @@ extern "C" void __disableWiFiAtBootTime() // Does (almost) nothing: WiFi is enabled by default in nonos-sdk // ... but restores legacy WiFi credentials persistence to true at boot time - // (can be still overrisden by user before setting up WiFi, like before) + // (can be still overridden by user before setting up WiFi, like before) // (note: c++ ctors not called yet at this point) ESP8266WiFiClass::persistent(true); diff --git a/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.cpp b/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.cpp index 81cffe48eb..9ec32e9e60 100644 --- a/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.cpp +++ b/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.cpp @@ -535,7 +535,7 @@ void ESP8266WiFiMesh::attemptTransmission(const String &message, bool concluding if(WiFi.status() == WL_CONNECTED) { transmission_status_t transmissionResult = attemptDataTransfer(); - latestTransmissionOutcomes.push_back(TransmissionResult(connectionQueue.back(), transmissionResult)); + latestTransmissionOutcomes.emplace_back(connectionQueue.back(), transmissionResult); } else { @@ -600,7 +600,7 @@ void ESP8266WiFiMesh::attemptTransmission(const String &message, bool concluding transmission_status_t transmissionResult = connectToNode(currentSSID, currentWiFiChannel, currentBSSID); - latestTransmissionOutcomes.push_back(TransmissionResult{.origin = currentNetwork, .transmissionStatus = transmissionResult}); + latestTransmissionOutcomes.emplace_back(currentNetwork, transmissionResult); } } diff --git a/libraries/ESP8266WiFiMesh/src/EspnowMeshBackend.cpp b/libraries/ESP8266WiFiMesh/src/EspnowMeshBackend.cpp index f89f300813..e0b4f98e40 100644 --- a/libraries/ESP8266WiFiMesh/src/EspnowMeshBackend.cpp +++ b/libraries/ESP8266WiFiMesh/src/EspnowMeshBackend.cpp @@ -826,7 +826,7 @@ void EspnowMeshBackend::attemptTransmission(const String &message, const bool sc { TransmissionStatusType transmissionResult = initiateTransmission(getMessage(), currentNetwork); - latestTransmissionOutcomes().push_back(TransmissionOutcome{.origin = currentNetwork, .transmissionStatus = transmissionResult}); + latestTransmissionOutcomes().emplace_back(currentNetwork, transmissionResult); if(!getTransmissionOutcomesUpdateHook()(*this)) break; @@ -897,7 +897,7 @@ void EspnowMeshBackend::attemptAutoEncryptingTransmission(const String &message, TransmissionStatusType transmissionResult = initiateAutoEncryptingTransmission(getMessage(), currentBSSID, connectionStatus); - latestTransmissionOutcomes().push_back(TransmissionOutcome{.origin = currentNetwork, .transmissionStatus = transmissionResult}); + latestTransmissionOutcomes().emplace_back(currentNetwork, transmissionResult); _encryptionBroker.finalizeAutoEncryptingConnection(currentBSSID, existingEncryptedConnection, requestPermanentConnections); diff --git a/libraries/ESP8266WiFiMesh/src/TcpIpMeshBackend.cpp b/libraries/ESP8266WiFiMesh/src/TcpIpMeshBackend.cpp index 1e3c285c17..6368ab7d85 100644 --- a/libraries/ESP8266WiFiMesh/src/TcpIpMeshBackend.cpp +++ b/libraries/ESP8266WiFiMesh/src/TcpIpMeshBackend.cpp @@ -451,7 +451,7 @@ void TcpIpMeshBackend::attemptTransmission(const String &message, const bool sca if(WiFi.status() == WL_CONNECTED) { TransmissionStatusType transmissionResult = attemptDataTransfer(); - latestTransmissionOutcomes().push_back(TransmissionOutcome(constConnectionQueue().back(), transmissionResult)); + latestTransmissionOutcomes().emplace_back(constConnectionQueue().back(), transmissionResult); getTransmissionOutcomesUpdateHook()(*this); } @@ -474,7 +474,7 @@ void TcpIpMeshBackend::attemptTransmission(const String &message, const bool sca { TransmissionStatusType transmissionResult = initiateTransmission(currentNetwork); - latestTransmissionOutcomes().push_back(TransmissionOutcome{.origin = currentNetwork, .transmissionStatus = transmissionResult}); + latestTransmissionOutcomes().emplace_back(currentNetwork, transmissionResult); if(!getTransmissionOutcomesUpdateHook()(*this)) break; diff --git a/libraries/ESP8266mDNS/src/LEAmDNS.h b/libraries/ESP8266mDNS/src/LEAmDNS.h index 092fcd5cf2..3fc4dd98da 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS.h +++ b/libraries/ESP8266mDNS/src/LEAmDNS.h @@ -492,8 +492,7 @@ namespace MDNSImplementation { MDNSServiceInfo(MDNSResponder& p_pM, MDNSResponder::hMDNSServiceQuery p_hS, uint32_t p_u32A) : - p_pMDNSResponder(p_pM), - p_hServiceQuery(p_hS), p_u32AnswerIndex(p_u32A) {}; + p_pMDNSResponder(p_pM), p_hServiceQuery(p_hS), p_u32AnswerIndex(p_u32A) {}; struct CompareKey { bool operator()(char const* a, char const* b) const diff --git a/libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp b/libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp index f575983ef7..2cc32b053b 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp +++ b/libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp @@ -631,8 +631,8 @@ namespace MDNSImplementation } } } // service tiebreak possibility - } // for services - } // ANY answers + } // for services + } // ANY answers } else { @@ -955,7 +955,7 @@ namespace MDNSImplementation } pRRAnswer = pRRAnswer->m_pNext; // Next collected answer - } // while (answers) + } // while (answers) } while ((bFoundNewKeyAnswer) && (bResult)); } // else: No answers provided DEBUG_EX_ERR(if (!bResult) { @@ -1110,7 +1110,7 @@ namespace MDNSImplementation } pServiceQuery = pServiceQuery->m_pNext; } // while(service query) - } // else: No p_pSRVAnswer + } // else: No p_pSRVAnswer DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processSRVAnswer: FAILED!\n")); }); @@ -1173,7 +1173,7 @@ namespace MDNSImplementation } pServiceQuery = pServiceQuery->m_pNext; } // while(service query) - } // else: No p_pTXTAnswer + } // else: No p_pTXTAnswer DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processTXTAnswer: FAILED!\n")); }); @@ -1261,7 +1261,7 @@ namespace MDNSImplementation } pServiceQuery = pServiceQuery->m_pNext; } // while(service query) - } // else: No p_pAAnswer + } // else: No p_pAAnswer DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: FAILED!\n")); }); @@ -1349,7 +1349,7 @@ namespace MDNSImplementation } pServiceQuery = pServiceQuery->m_pNext; } // while(service query) - } // else: No p_pAAAAAnswer + } // else: No p_pAAAAAnswer return bResult; } @@ -2121,7 +2121,7 @@ namespace MDNSImplementation } // IP4 flagged pIP4Address = pNextIP4Address; // Next - } // while + } // while #endif #ifdef MDNS_IP6_SUPPORT // IP6Address (from AAAA) @@ -2185,7 +2185,7 @@ namespace MDNSImplementation } // IP6 flagged pIP6Address = pNextIP6Address; // Next - } // while + } // while #endif pSQAnswer = pNextSQAnswer; } diff --git a/libraries/ESP8266mDNS/src/LEAmDNS_Structs.cpp b/libraries/ESP8266mDNS/src/LEAmDNS_Structs.cpp index 977a924394..fbdfe148ea 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS_Structs.cpp +++ b/libraries/ESP8266mDNS/src/LEAmDNS_Structs.cpp @@ -55,8 +55,7 @@ namespace MDNSImplementation MDNSResponder::stcMDNSServiceTxt::stcMDNSServiceTxt(const char* p_pcKey /*= 0*/, const char* p_pcValue /*= 0*/, bool p_bTemp /*= false*/) : - m_pNext(0), - m_pcKey(0), m_pcValue(0), m_bTemp(p_bTemp) + m_pNext(0), m_pcKey(0), m_pcValue(0), m_bTemp(p_bTemp) { setKey(p_pcKey); setValue(p_pcValue); @@ -67,8 +66,7 @@ namespace MDNSImplementation */ MDNSResponder::stcMDNSServiceTxt::stcMDNSServiceTxt( const MDNSResponder::stcMDNSServiceTxt& p_Other) : - m_pNext(0), - m_pcKey(0), m_pcValue(0), m_bTemp(false) + m_pNext(0), m_pcKey(0), m_pcValue(0), m_bTemp(false) { operator=(p_Other); } @@ -614,9 +612,8 @@ namespace MDNSImplementation bool p_bRA /*= false*/, unsigned char p_ucRCode /*= 0*/, uint16_t p_u16QDCount /*= 0*/, uint16_t p_u16ANCount /*= 0*/, uint16_t p_u16NSCount /*= 0*/, uint16_t p_u16ARCount /*= 0*/) : - m_u16ID(p_u16ID), - m_1bQR(p_bQR), m_4bOpcode(p_ucOpcode), m_1bAA(p_bAA), m_1bTC(p_bTC), m_1bRD(p_bRD), - m_1bRA(p_bRA), m_3bZ(0), m_4bRCode(p_ucRCode), m_u16QDCount(p_u16QDCount), + m_u16ID(p_u16ID), m_1bQR(p_bQR), m_4bOpcode(p_ucOpcode), m_1bAA(p_bAA), m_1bTC(p_bTC), + m_1bRD(p_bRD), m_1bRA(p_bRA), m_3bZ(0), m_4bRCode(p_ucRCode), m_u16QDCount(p_u16QDCount), m_u16ANCount(p_u16ANCount), m_u16NSCount(p_u16NSCount), m_u16ARCount(p_u16ARCount) { } @@ -813,8 +810,7 @@ namespace MDNSImplementation */ MDNSResponder::stcMDNS_RRAttributes::stcMDNS_RRAttributes( uint16_t p_u16Type /*= 0*/, uint16_t p_u16Class /*= 1 DNS_RRCLASS_IN Internet*/) : - m_u16Type(p_u16Type), - m_u16Class(p_u16Class) + m_u16Type(p_u16Type), m_u16Class(p_u16Class) { } @@ -910,8 +906,7 @@ namespace MDNSImplementation MDNSResponder::stcMDNS_RRAnswer::stcMDNS_RRAnswer( enuAnswerType p_AnswerType, const MDNSResponder::stcMDNS_RRHeader& p_Header, uint32_t p_u32TTL) : - m_pNext(0), - m_AnswerType(p_AnswerType), m_Header(p_Header), m_u32TTL(p_u32TTL) + m_pNext(0), m_AnswerType(p_AnswerType), m_Header(p_Header), m_u32TTL(p_u32TTL) { // Extract 'cache flush'-bit m_bCacheFlush = (m_Header.m_Attributes.m_u16Class & 0x8000); @@ -955,8 +950,7 @@ namespace MDNSImplementation */ MDNSResponder::stcMDNS_RRAnswerA::stcMDNS_RRAnswerA( const MDNSResponder::stcMDNS_RRHeader& p_Header, uint32_t p_u32TTL) : - stcMDNS_RRAnswer(AnswerType_A, p_Header, p_u32TTL), - m_IPAddress(0, 0, 0, 0) + stcMDNS_RRAnswer(AnswerType_A, p_Header, p_u32TTL), m_IPAddress(0, 0, 0, 0) { } @@ -1094,8 +1088,8 @@ namespace MDNSImplementation */ MDNSResponder::stcMDNS_RRAnswerSRV::stcMDNS_RRAnswerSRV( const MDNSResponder::stcMDNS_RRHeader& p_Header, uint32_t p_u32TTL) : - stcMDNS_RRAnswer(AnswerType_SRV, p_Header, p_u32TTL), - m_u16Priority(0), m_u16Weight(0), m_u16Port(0) + stcMDNS_RRAnswer(AnswerType_SRV, p_Header, p_u32TTL), m_u16Priority(0), m_u16Weight(0), + m_u16Port(0) { } @@ -1132,8 +1126,7 @@ namespace MDNSImplementation */ MDNSResponder::stcMDNS_RRAnswerGeneric::stcMDNS_RRAnswerGeneric( const stcMDNS_RRHeader& p_Header, uint32_t p_u32TTL) : - stcMDNS_RRAnswer(AnswerType_Generic, p_Header, p_u32TTL), - m_u16RDLength(0), m_pu8RDData(0) + stcMDNS_RRAnswer(AnswerType_Generic, p_Header, p_u32TTL), m_u16RDLength(0), m_pu8RDData(0) { } @@ -1212,8 +1205,7 @@ namespace MDNSImplementation MDNSResponder::stcMDNSService::stcMDNSService(const char* p_pcName /*= 0*/, const char* p_pcService /*= 0*/, const char* p_pcProtocol /*= 0*/) : - m_pNext(0), - m_pcName(0), m_bAutoName(false), m_pcService(0), m_pcProtocol(0), m_u16Port(0), + m_pNext(0), m_pcName(0), m_bAutoName(false), m_pcService(0), m_pcProtocol(0), m_u16Port(0), m_u8ReplyMask(0), m_fnTxtCallback(0) { setName(p_pcName); @@ -1538,9 +1530,7 @@ namespace MDNSImplementation MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address::stcIP4Address constructor */ MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address::stcIP4Address( - IPAddress p_IPAddress, uint32_t p_u32TTL /*= 0*/) : - m_pNext(0), - m_IPAddress(p_IPAddress) + IPAddress p_IPAddress, uint32_t p_u32TTL /*= 0*/) : m_pNext(0), m_IPAddress(p_IPAddress) { m_TTL.set(p_u32TTL); } @@ -1745,15 +1735,6 @@ namespace MDNSImplementation const MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address( const IPAddress& p_IPAddress) const - { - return (stcIP4Address*)(((const stcAnswer*)this)->findIP4Address(p_IPAddress)); - } - - /* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address - */ - MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address(const IPAddress& p_IPAddress) { stcIP4Address* pIP4Address = m_pIP4Addresses; while (pIP4Address) @@ -1767,6 +1748,16 @@ namespace MDNSImplementation return pIP4Address; } + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address + */ + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address(const IPAddress& p_IPAddress) + { + const auto& cref = static_cast(*this); + return const_cast(cref.findIP4Address(p_IPAddress)); + } + /* MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressCount */ @@ -2043,7 +2034,8 @@ namespace MDNSImplementation MDNSResponder::stcMDNSServiceQuery::stcAnswer* MDNSResponder::stcMDNSServiceQuery::answerAtIndex(uint32_t p_u32Index) { - return (stcAnswer*)(((const stcMDNSServiceQuery*)this)->answerAtIndex(p_u32Index)); + const auto& cref = static_cast(*this); + return const_cast(cref.answerAtIndex(p_u32Index)); } /* @@ -2172,9 +2164,8 @@ namespace MDNSImplementation */ MDNSResponder::stcMDNSSendParameter::stcDomainCacheItem::stcDomainCacheItem( const void* p_pHostnameOrService, bool p_bAdditionalData, uint32_t p_u16Offset) : - m_pNext(0), - m_pHostnameOrService(p_pHostnameOrService), m_bAdditionalData(p_bAdditionalData), - m_u16Offset(p_u16Offset) + m_pNext(0), m_pHostnameOrService(p_pHostnameOrService), + m_bAdditionalData(p_bAdditionalData), m_u16Offset(p_u16Offset) { } diff --git a/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino b/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino index 50b3206cc7..18f1cad1f9 100644 --- a/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino +++ b/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino @@ -21,8 +21,7 @@ void setup() { // start I2S at 8 kHz with 24-bits per sample if (!I2S.begin(I2S_PHILIPS_MODE, 8000, 24)) { Serial.println("Failed to initialize I2S!"); - while (1) - ; // do nothing + while (1); // do nothing } } diff --git a/libraries/I2S/examples/SimpleTone/SimpleTone.ino b/libraries/I2S/examples/SimpleTone/SimpleTone.ino index 689a50d579..e2dcb7b3f6 100644 --- a/libraries/I2S/examples/SimpleTone/SimpleTone.ino +++ b/libraries/I2S/examples/SimpleTone/SimpleTone.ino @@ -26,8 +26,7 @@ void setup() { // start I2S at the sample rate with 16-bits per sample if (!I2S.begin(I2S_PHILIPS_MODE, sampleRate, 16)) { Serial.println("Failed to initialize I2S!"); - while (1) - ; // do nothing + while (1); // do nothing } } diff --git a/libraries/LittleFS/src/LittleFS.h b/libraries/LittleFS/src/LittleFS.h index a3afbfe77f..d5203ae6e0 100644 --- a/libraries/LittleFS/src/LittleFS.h +++ b/libraries/LittleFS/src/LittleFS.h @@ -167,6 +167,14 @@ class LittleFSImpl : public FSImpl return false; } int rc = lfs_mkdir(&_lfs, path); + if ((rc == 0) && _timeCallback) { + time_t now = _timeCallback(); + // Add metadata with creation time to the directory marker + int rc = lfs_setattr(&_lfs, path, 'c', (const void *)&now, sizeof(now)); + if (rc < 0) { + DEBUGV("Unable to set creation time on '%s' to %ld\n", path, (long)now); + } + } return (rc==0); } diff --git a/libraries/SD/examples/listfilesEnhanced/listfilesEnhanced.ino b/libraries/SD/examples/listfilesEnhanced/listfilesEnhanced.ino new file mode 100644 index 0000000000..5daa3294ad --- /dev/null +++ b/libraries/SD/examples/listfilesEnhanced/listfilesEnhanced.ino @@ -0,0 +1,134 @@ +/* + Listfiles Enhanced + + This example demonstrates how to list files on an SDcard in the following way: + 1) collect all directories + 2) build full path of directories and keep in mind + 3) then print all files with the help of the directorie pathes + + Wiring: + SDcard attached to SPI bus as follows: + - MOSI: pin 11 + - MISO: pin 12 + - CLK : pin 13 + - CS : pin 4 + + Created: + 18. Nov 2024 by Frank Häfele + + This example code is in the public domain. + +*/ +#include +#include +#include + +#define SD_CS_PIN 4 + + +void dir(String path) { + std::vector directories; + collectDirectories(path, directories); + for (auto directory : directories) { + printDirectoryName(directory.c_str(), 1); + File fs = SD.open(directory); + printFilesInDirectory(fs); + Serial.println("\n==============="); + fs.close(); + } +} + +void setup() { + // Open serial communications and wait for port to open: + Serial.begin(115200); + + Serial.print("\n\n==== List Directory ====\n\n"); + listDirectory(); + + Serial.println("done!"); +} + +void loop() { + // nothing happens after setup finishes. +} + +void listDirectory() { + Serial.print("\n\nInitializing SD card..."); + if (!SD.begin(SD_CS_PIN)) { + Serial.println("initialization failed!"); + return; + } + Serial.print("initialization successful.\n"); + Serial.print("List Files:\n"); + dir("/"); +} + + +void printDirectoryName(const char *name, uint8_t level) { + for (uint8_t i = 0; i < level; ++i) { + Serial.print(" "); + } + Serial.println(name); +} + + + +// helper function: combine path +String joinPath(const String &base, const String &name) { + if (base.endsWith("/")) { + return base + name; + } + return base + "/" + name; +} + +// recusive function to collect directory names +void collectDirectories(const String &dirname, std::vector &directories) { + File root = SD.open(dirname); + if (!root || !root.isDirectory()) { + Serial.printf("Error: Cannot open %s\n", dirname.c_str()); + return; + } + directories.push_back(dirname); + + File file = root.openNextFile(); + while (file) { + if (file.isDirectory()) { + String fullPath = joinPath(dirname, file.name()); + collectDirectories(fullPath, directories); + } + file = root.openNextFile(); + } + root.close(); +} + +// print filenames +void printFileName(File file) { + Serial.print("\t"); + Serial.printf("%30s", file.name()); + // files have sizes, directories do not + Serial.print(" - "); + Serial.print(file.size(), DEC); + time_t cr = file.getCreationTime(); + time_t lw = file.getLastWrite(); + struct tm *tmstruct = localtime(&cr); + Serial.printf("\tCREATION: %d-%02d-%02d %02d:%02d:%02d", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec); + tmstruct = localtime(&lw); + Serial.printf("\tLAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec); +} + + +// print files in directories +void printFilesInDirectory(File dir) { + while (true) { + auto file = dir.openNextFile(); + if (!file) { + // no more files + break; + } + if (file.isDirectory()) { + continue; + } else { + printFileName(file); + } + } +} diff --git a/libraries/TFT_Touch_Shield_V2/examples/tftbmp/tftbmp.ino b/libraries/TFT_Touch_Shield_V2/examples/tftbmp/tftbmp.ino index 691f5be7ca..55e4981df9 100644 --- a/libraries/TFT_Touch_Shield_V2/examples/tftbmp/tftbmp.ino +++ b/libraries/TFT_Touch_Shield_V2/examples/tftbmp/tftbmp.ino @@ -55,8 +55,7 @@ void setup() { if (!SD.begin(PIN_SD_CS)) { Serial.println("failed!"); - while (1) - ; // init fail, die here + while (1); // init fail, die here } Serial.println("SD OK!"); @@ -69,8 +68,7 @@ void loop() { bmpFile = SD.open(__Gsbmp_files[i]); if (!bmpFile) { Serial.println("didn't find image"); - while (1) - ; + while (1); } if (!bmpReadHeader(bmpFile)) { diff --git a/libraries/TFT_Touch_Shield_V2/examples/tftbmp2/tftbmp2.ino b/libraries/TFT_Touch_Shield_V2/examples/tftbmp2/tftbmp2.ino index 0a698fb150..8e24600481 100644 --- a/libraries/TFT_Touch_Shield_V2/examples/tftbmp2/tftbmp2.ino +++ b/libraries/TFT_Touch_Shield_V2/examples/tftbmp2/tftbmp2.ino @@ -99,8 +99,7 @@ void setup() { if (!SD.begin(PIN_SD_CS)) { Serial.println("failed!"); - while (1) - ; // init fail, die here + while (1); // init fail, die here } Serial.println("SD OK!"); @@ -141,8 +140,7 @@ void loop() { if (!bmpFile) { Serial.println("didn't find image"); - while (1) - ; + while (1); } if (!bmpReadHeader(bmpFile)) { @@ -153,8 +151,7 @@ void loop() { bmpdraw(bmpFile, 0, 0, 1); bmpFile.close(); - while (1) - ; + while (1); } /*********************************************/ diff --git a/libraries/esp8266/examples/ConfigFile/ConfigFile.ino b/libraries/esp8266/examples/ConfigFile/ConfigFile.ino index d0406a3882..c5d8ee8ed2 100644 --- a/libraries/esp8266/examples/ConfigFile/ConfigFile.ino +++ b/libraries/esp8266/examples/ConfigFile/ConfigFile.ino @@ -12,7 +12,7 @@ #include // more and possibly updated information can be found at: -// https://arduinojson.org/v6/example/config/ +// https://arduinojson.org/v7/example/config/ bool loadConfig() { File configFile = LittleFS.open("/config.json", "r"); @@ -21,7 +21,7 @@ bool loadConfig() { return false; } - StaticJsonDocument<200> doc; + JsonDocument doc; auto error = deserializeJson(doc, configFile); if (error) { Serial.println("Failed to parse config file"); @@ -42,7 +42,7 @@ bool loadConfig() { } bool saveConfig() { - StaticJsonDocument<200> doc; + JsonDocument doc; doc["serverName"] = "api.example.com"; doc["accessToken"] = "128du9as8du12eoue8da98h123ueh9h98"; diff --git a/libraries/esp8266/examples/SerialStress/SerialStress.ino b/libraries/esp8266/examples/SerialStress/SerialStress.ino index 68a6122582..f491ba04e5 100644 --- a/libraries/esp8266/examples/SerialStress/SerialStress.ino +++ b/libraries/esp8266/examples/SerialStress/SerialStress.ino @@ -81,8 +81,7 @@ void setup() { // bind RX and TX USC0(0) |= (1 << UCLBE); - while (Serial.read() == -1) - ; + while (Serial.read() == -1); if (Serial.hasOverrun()) { logger->print("overrun?\n"); } timeout = (start_ms = last_ms = millis()) + TIMEOUT; diff --git a/libraries/esp8266/examples/StreamString/StreamString.ino b/libraries/esp8266/examples/StreamString/StreamString.ino index f177b53edc..73512cf2af 100644 --- a/libraries/esp8266/examples/StreamString/StreamString.ino +++ b/libraries/esp8266/examples/StreamString/StreamString.ino @@ -54,7 +54,7 @@ void testStreamString() { { // We use a a lighter StreamConstPtr(input) to make a read-only Stream out of // a String that obviously should not be modified during the time the - // StreamConstPtr instance is used. It is used as a source to be sent to + // StreamConstPtr instance is used. It is used as a read-only source to be sent to // 'result'. result.clear(); @@ -77,7 +77,7 @@ void testStreamString() { // Now inputString is made into a Stream using S2Stream, // and set in non-consume mode (using ::resetPointer()). - // Then, after that input is read once, it won't be anymore readable + // Then, after input is read once, it won't be anymore readable // until the pointer is reset. S2Stream input(inputString); @@ -87,7 +87,7 @@ void testStreamString() { input.sendAll(result); input.sendAll(result); check("S2Stream.sendAll(StreamString)", result.c_str(), "hello"); - check("unmodified String given to S2Stream", inputString.c_str(), "hello"); + check("String given to S2Stream is unmodified", inputString.c_str(), "hello"); } { @@ -103,6 +103,17 @@ void testStreamString() { check("S2Stream.resetPointer(2):", result.c_str(), "llo"); } + { + // Streaming to a regular String + + String someSource{ F("hello") }; + String someDestString; + + StreamConstPtr(someSource).sendAll(S2Stream(someDestString)); + StreamConstPtr(someSource).sendAll(S2Stream(someDestString)); + check("StreamConstPtr(someSource).sendAll(S2Stream(someDestString))", someDestString.c_str(), "hellohello"); + } + { // inputString made into a Stream // reading the Stream consumes the String @@ -181,7 +192,8 @@ void setup() { testStreamString(); - Serial.printf("sizeof: String:%d Stream:%d StreamString:%d SStream:%d\n", (int)sizeof(String), (int)sizeof(Stream), (int)sizeof(StreamString), (int)sizeof(S2Stream)); + Serial.printf("sizeof: String:%zu Stream:%zu StreamString:%zu S2Stream:%zu StreamConstPtr:%zu\n", + sizeof(String), sizeof(Stream), sizeof(StreamString), sizeof(S2Stream), sizeof(StreamConstPtr)); } #endif diff --git a/libraries/lwIP_Ethernet/src/EthernetCompat.h b/libraries/lwIP_Ethernet/src/EthernetCompat.h index ee8d1fadc2..9ebd75ca15 100644 --- a/libraries/lwIP_Ethernet/src/EthernetCompat.h +++ b/libraries/lwIP_Ethernet/src/EthernetCompat.h @@ -103,6 +103,21 @@ class ArduinoEthernet: public LwipIntfDev return DHCP_CHECK_NONE; } + void MACAddress(uint8_t* mac) + { + LwipIntfDev::macAddress(mac); + } + + IPAddress dnsServerIP() const + { + return LwipIntfDev::dnsIP(0); + } + + void setDnsServerIP(const IPAddress dnsIP) + { + LwipIntfDev::setDNS(dnsIP); + } + protected: HardwareStatus _hardwareStatus; }; diff --git a/package/build_boards_manager_package.sh b/package/build_boards_manager_package.sh index 40050f4437..dc15f90fc5 100755 --- a/package/build_boards_manager_package.sh +++ b/package/build_boards_manager_package.sh @@ -117,7 +117,7 @@ $SED -E "s/name=([a-zA-Z0-9\ -]+).*/name=\1(${ver})/g"\ #echo "#define ARDUINO_ESP8266_GIT_DESC `git describe --tags 2>/dev/null`" >>${outdir}/cores/esp8266/core_version.h #echo "#define ARDUINO_ESP8266_RELEASE_${ver_define}" >>${outdir}/cores/esp8266/core_version.h #echo "#define ARDUINO_ESP8266_RELEASE \"${ver_define}\"" >>${outdir}/cores/esp8266/core_version.h -python3 ${srcdir}/tools/makecorever.py -b ${outdir} -i cores/esp8266 -p ${srcdir} -v ${plain_ver} -r +python3 ${srcdir}/tools/makecorever.py --git-root ${srcdir} --version ${plain_ver} --release ${outdir}/cores/esp8266/core_version.h # Zip the package pushd package/versions/${visiblever} diff --git a/package/package_esp8266com_index.template.json b/package/package_esp8266com_index.template.json index 323db7081f..4d44ddb0a2 100644 --- a/package/package_esp8266com_index.template.json +++ b/package/package_esp8266com_index.template.json @@ -47,6 +47,9 @@ { "name": "ESPresso Lite 2.0" }, + { + "name": "Mercury" + }, { "name": "Phoenix 1.0" }, diff --git a/platform.txt b/platform.txt index 3ca4ffae29..f3392d652d 100644 --- a/platform.txt +++ b/platform.txt @@ -93,7 +93,7 @@ compiler.c.elf.cmd=xtensa-lx106-elf-gcc compiler.c.elf.libs=-lhal -lphy -lpp -lnet80211 {build.lwip_lib} -lwpa -lcrypto -lmain -lwps -lbearssl -lespnow -lsmartconfig -lairkiss -lwpa2 {build.stdcpp_lib} -lm -lc -lgcc compiler.cpp.cmd=xtensa-lx106-elf-g++ -compiler.cpp.flags=-c "{compiler.warning_flags}-cppflags" {build.stacksmash_flags} -g -free -fipa-pta -Werror=return-type -mlongcalls -mtext-section-literals -fno-rtti -falign-functions=4 {build.stdcpp_level} -MMD -ffunction-sections -fdata-sections {build.exception_flags} {build.sslflags} {build.mmuflags} {build.non32xferflags} {build.iramfloat} +compiler.cpp.flags=-c "{compiler.warning_flags}-cxxflags" {build.stacksmash_flags} -g -free -fipa-pta -Werror=return-type -mlongcalls -mtext-section-literals -fno-rtti -falign-functions=4 {build.stdcpp_level} -MMD -ffunction-sections -fdata-sections {build.exception_flags} {build.sslflags} {build.mmuflags} {build.non32xferflags} {build.iramfloat} compiler.as.cmd=xtensa-lx106-elf-as @@ -121,7 +121,7 @@ compiler.elf2hex.extra_flags= ## needs git recipe.hooks.sketch.prebuild.pattern="{runtime.tools.python3.path}/python3" -I "{runtime.tools.signing}" --mode header --publickey "{build.source.path}/public.key" --out "{build.path}/core/Updater_Signing.h" # This is quite a working hack. This form of prebuild hook, while intuitive, is not explicitly documented. -recipe.hooks.prebuild.1.pattern="{runtime.tools.python3.path}/python3" -I "{runtime.tools.makecorever}" --build_path "{build.path}" --platform_path "{runtime.platform.path}" --version "{version}" +recipe.hooks.prebuild.1.pattern="{runtime.tools.python3.path}/python3" -I "{runtime.tools.makecorever}" --git-root "{runtime.platform.path}" --version "{version}" "{build.path}/core/core_version.h" # Handle processing sketch global options recipe.hooks.prebuild.2.pattern="{runtime.tools.python3.path}/python3" -I "{runtime.tools.mkbuildoptglobals}" "{runtime.ide.path}" {runtime.ide.version} "{build.path}" "{build.opt.fqfn}" "{globals.h.source.fqfn}" "{commonhfile.fqfn}" {mkbuildoptglobals.extra_flags} diff --git a/tests/build.sh b/tests/build.sh index 6e544b9b18..af34287a8a 100755 --- a/tests/build.sh +++ b/tests/build.sh @@ -1,19 +1,40 @@ #!/usr/bin/env bash +# expect to have git available root=$(git rev-parse --show-toplevel) +# general configuration related to the builder itself ESP8266_ARDUINO_BUILD_DIR=${ESP8266_ARDUINO_BUILD_DIR:-$root} ESP8266_ARDUINO_BUILDER=${ESP8266_ARDUINO_BUILDER:-arduino} ESP8266_ARDUINO_PRESERVE_CACHE=${ESP8266_ARDUINO_PRESERVE_CACHE:-} -ESP8266_ARDUINO_IDE=${ESP8266_ARDUINO_IDE:-$HOME/arduino_ide} -ESP8266_ARDUINO_HARDWARE=${ESP8266_ARDUINO_HARDWARE:-$HOME/Arduino/hardware} -ESP8266_ARDUINO_LIBRARIES=${ESP8266_ARDUINO_LIBRARIES:-$HOME/Arduino/libraries} - +# sketch build options ESP8266_ARDUINO_DEBUG=${ESP8266_ARDUINO_DEBUG:-nodebug} ESP8266_ARDUINO_LWIP=${ESP8266_ARDUINO_LWIP:-default} ESP8266_ARDUINO_SKETCHES=${ESP8266_ARDUINO_SKETCHES:-} +ESP8266_ARDUINO_CLI=${ESP8266_ARDUINO_CLI:-$HOME/.local/bin/arduino-cli} + +# ref. https://arduino.github.io/arduino-cli/1.2/configuration/#default-directories +case "${RUNNER_OS:-Linux}" in +"Linux") + ESP8266_ARDUINO_HARDWARE=${ESP8266_ARDUINO_HARDWARE:-$HOME/Arduino/hardware} + ESP8266_ARDUINO_LIBRARIES=${ESP8266_ARDUINO_LIBRARIES:-$HOME/Arduino/libraries} + ;; +"macOS") + ESP8266_ARDUINO_HARDWARE=${ESP8266_ARDUINO_HARDWARE:-$HOME/Documents/Arduino/hardware} + ESP8266_ARDUINO_LIBRARIES=${ESP8266_ARDUINO_LIBRARIES:-$HOME/Documents/Arduino/libraries} + ;; +"Windows") + ESP8266_ARDUINO_HARDWARE=${ESP8266_ARDUINO_HARDWARE:-$HOME/Documents/Arduino/hardware} + ESP8266_ARDUINO_LIBRARIES=${ESP8266_ARDUINO_LIBRARIES:-$HOME/Documents/Arduino/libraries} + ;; +*) + echo 'Unknown ${RUNNER_OS} = "' ${RUNNER_OS} '"' + exit 2 +esac + +source "$root/tests/lib-skip-ino.sh" source "$root/tests/common.sh" cmd=${0##*/} @@ -22,8 +43,7 @@ ENVIRONMENT: ESP8266_ARDUINO_SKETCHES - list of .ino files; defaults to **all available examples** ESP8266_ARDUINO_BUILDER - arduino or platformio - For Arduino IDE: - ESP8266_ARDUINO_IDE - path to the IDE (portable) + For Arduino CLI: ESP8266_ARDUINO_HARDWARE - path to the hardware directory (usually, containing our repo) ESP8266_ARDUINO_LIBRATIES - path to the libraries directory (external dependencies) ESP8266_ARDUINO_DEBUG - debug or nodebug @@ -31,12 +51,14 @@ ENVIRONMENT: USAGE: $cmd <[even | odd]> - build every Nth, when ' % 2' is either even or odd - $cmd - build every Nth, when ' % ' is equal to 'rem' + $cmd <[cnt]> - build every Nth, when ' % ' is equal to 'rem' + optionally, set to start with the Nth sketch $cmd - build every .ino file from ESP8266_ARDUINO_SKETCHES " mod=1 rem=0 +cnt=0 if [ "$#" -eq 1 ] ; then case "$1" in @@ -60,6 +82,10 @@ if [ "$#" -eq 1 ] ; then elif [ "$#" -eq 2 ] ; then mod=$1 rem=$2 +elif [ "$#" -eq 3 ] ; then + mod=$1 + rem=$2 + cnt=$3 elif [ "$#" -gt 2 ] ; then echo "$usage" exit 1 @@ -72,14 +98,17 @@ fi case "$ESP8266_ARDUINO_BUILDER" in "arduino") install_arduino "$ESP8266_ARDUINO_DEBUG" - build_sketches_with_arduino "$mod" "$rem" "$ESP8266_ARDUINO_LWIP" + build_sketches_with_arduino "$ESP8266_ARDUINO_LWIP" "$mod" "$rem" "$cnt" ;; "platformio") install_platformio nodemcuv2 - build_sketches_with_platformio "$mod" "$rem" + build_sketches_with_platformio "$mod" "$rem" "$cnt" + ;; +"print") + print_sketch_info "$mod" "$rem" ;; *) - echo "Unknown builder! Must be either arduino or platformio" + echo "Unknown builder! Must be one of - arduino, platformio or print" exit 1 ;; esac diff --git a/tests/ci/build_docs.sh b/tests/ci/build_docs.sh index b26852e553..7a5ae2a632 100755 --- a/tests/ci/build_docs.sh +++ b/tests/ci/build_docs.sh @@ -5,4 +5,4 @@ set -ev root=$(git rev-parse --show-toplevel) -env SPHINXOPTS="-W" make -C $root/doc html +make SPHINXOPTS="--fail-on-warning" SPHINXBUILD="${SPHINXBUILD:?sphinx-build}" -C $root/doc html diff --git a/tests/clang-format-core.yaml b/tests/clang-format-core.yaml index 540037d13e..0df633d245 100644 --- a/tests/clang-format-core.yaml +++ b/tests/clang-format-core.yaml @@ -27,3 +27,4 @@ BreakBeforeBraces: Allman IndentWidth: 4 IndentCaseLabels: false ReflowComments: false +SkipMacroDefinitionBody: true diff --git a/tests/common.sh b/tests/common.sh index 1799f7b481..c52756d177 100755 --- a/tests/common.sh +++ b/tests/common.sh @@ -16,6 +16,16 @@ function trap_exit() exit $exit_code } +if [ "${CI-}" = "true" ] ; then + ci_group="::group::" + ci_end_group="::endgroup::" + ci_error="::error::" +else + ci_group="==> " + ci_end_group="" + ci_error=">>> " +fi + function step_summary() { local header=$1 @@ -31,48 +41,6 @@ function step_summary() fi } -# return 0 if this sketch should not be built in CI (for other archs, not needed, etc.) -function skip_ino() -{ - case $1 in - *"/#attic/"* | \ - *"/AvrAdcLogger/"* | \ - *"/examplesV1/"* | \ - *"/RtcTimestampTest/"* | \ - *"/SoftwareSpi/"* | \ - *"/TeensyDmaAdcLogger/"* | \ - *"/TeensyRtcTimestamp/"* | \ - *"/TeensySdioDemo/"* | \ - *"/TeensySdioLogger/"* | \ - *"/UserChipSelectFunction/"* | \ - *"/UserSPIDriver/"*) - return 0 - ;; - *"Teensy"*) - return 0 - ;; - *) - ;; - esac - - return 1 -} - -# return reason if this sketch is not the main one or it is explicitly disabled with .test.skip in its directory -function skip_sketch() -{ - local sketch=$1 - local sketchname=$2 - local sketchdir=$3 - local sketchdirname=$4 - - if [[ "${sketchdirname}.ino" != "$sketchname" ]]; then - echo "Skipping $sketch (not the main sketch file)" - fi - if skip_ino "$sketch" || [[ -f "$sketchdir/.test.skip" ]]; then - echo "Skipping $sketch" - fi -} function print_size_info_header() { @@ -105,37 +73,75 @@ END { awk -v sketch_name="${elf_name%.*}" "$awk_script" - } +function print_sketch_info() +{ + local build_mod=$1 + local build_rem=$2 + + local testcnt=0 + local cnt=0 + + for sketch in $ESP8266_ARDUINO_SKETCHES; do + testcnt=$(( ($testcnt + 1) % $build_mod )) + if [ $testcnt -ne $build_rem ]; then + continue # Not ours to do + fi + + local skip + skip=$(skip_sketch "$sketch") + if [ -n "$skip" ]; then + continue # Should be skipped / cannot be built + fi + + cnt=$(( $cnt + 1 )) + printf '%2d\t%s\n' "$cnt" "$sketch" + done +} + +function format_fqbn() +{ + local board_name=$1 + local flash_size=$2 + local lwip=$3 + + echo "esp8266com:esp8266:${board_name}:"\ +"eesz=${flash_size},"\ +"ip=${lwip}" +} + function build_sketches() { local core_path=$1 - local ide_path=$2 - local hardware_path=$3 - local library_path=$4 + local cli_path=$2 + local library_path=$3 + local lwip=$4 local build_mod=$5 local build_rem=$6 - local lwip=$7 + local build_cnt=$7 local build_dir="$cache_dir"/build mkdir -p "$build_dir" - local build_cache="$cache_dir"/cache - mkdir -p "$build_cache" + local build_out="$cache_dir"/out + mkdir -p "$build_out" + + local fqbn=$(format_fqbn "generic" "4M1M" "$lwip") + echo "FQBN: $fqbn" local build_cmd - build_cmd="python3 tools/build.py"\ -" --build_cache $build_cache"\ -" --build_path $build_dir"\ -" --hardware_path $hardware_path"\ -" --ide_path $ide_path"\ -" --library_path $library_path"\ -" --lwIP $lwip"\ -" --board_name generic --verbose --warnings all"\ -" --flash_size 4M1M --keep" + build_cmd+=${cli_path} + build_cmd+=" compile"\ +" --warnings=all"\ +" --build-path $build_dir"\ +" --fqbn $fqbn"\ +" --libraries $library_path"\ +" --output-dir $build_out" print_size_info_header >"$cache_dir"/size.log - local mk_clean_core=1 + local clean_core=1 local testcnt=0 + local cnt=0 for sketch in $ESP8266_ARDUINO_SKETCHES; do testcnt=$(( ($testcnt + 1) % $build_mod )) @@ -143,44 +149,24 @@ function build_sketches() continue # Not ours to do fi - # mkbuildoptglobals.py is optimized around the Arduino IDE 1.x - # behaviour. One way the CI differs from the Arduino IDE is in the - # handling of core and caching core. With the Arduino IDE, each sketch - # has a private copy of core and contributes to a core cache. With the - # CI, there is one shared copy of core for all sketches. When global - # options are used, the shared copy of core and cache are removed before - # and after the build. - # - # Do we need a clean core build? $build_dir/core/* cannot be shared - # between sketches when global options are present. - if [ -s ${sketch}.globals.h ]; then - mk_clean_core=1 - fi - if [ $mk_clean_core -ne 0 ]; then - rm -rf "$build_dir"/core/* - else - # Remove sketch specific files from ./core/ between builds. - rm -rf "$build_dir/core/build.opt" "$build_dir"/core/*.ino.globals.h + local skip + skip=$(skip_sketch "$sketch") + if [ -n "$skip" ]; then + echo "$skip" + continue # Should be skipped / cannot be built fi - if [ -e $cache_dir/core/*.a ]; then - # We need to preserve the build.options.json file and replace the last .ino - # with this sketch's ino file, or builder will throw everything away. - jq '."sketchLocation" = "'$sketch'"' $build_dir/build.options.json \ - > "$build_dir"/build.options.json.tmp - mv "$build_dir"/build.options.json.tmp "$build_dir"/build.options.json - if [ $mk_clean_core -ne 0 ]; then - # Hack workaround for CI not handling core rebuild for global options - rm $cache_dir/core/*.a + cnt=$(( $cnt + 1 )) + if [ $build_cnt != 0 ] ; then + if [ $build_cnt != $cnt ] ; then + continue # Haven't reached the $cnt yet fi + build_cnt=0 fi - if [ -s ${sketch}.globals.h ]; then - # Set to cleanup core at the start of the next build. - mk_clean_core=1 - else - mk_clean_core=0 - fi + # Do we need a clean core build? $build_dir/core/* cannot be shared + # between sketches when global options are present. + clean_core=$(arduino_mkbuildoptglobals_cleanup "$clean_core" "$build_dir" "$sketch") # Clear out the last built sketch, map, elf, bin files, but leave the compiled # objects in the core and libraries available for use so we don't need to rebuild @@ -190,23 +176,7 @@ function build_sketches() "$build_dir"/*.map \ "$build_dir"/*.elf - local sketchdir - sketchdir=$(dirname $sketch) - - local sketchdirname - sketchdirname=$(basename $sketchdir) - - local sketchname - sketchname=$(basename $sketch) - - local skip - skip=$(skip_sketch "$sketch" "$sketchname" "$sketchdir" "$sketchdirname") - if [ -n "$skip" ]; then - echo "$skip" - continue - fi - - echo ::group::Building $sketch + echo ${ci_group}Building $cnt $sketch echo "$build_cmd $sketch" local result @@ -214,9 +184,9 @@ function build_sketches() && result=0 || result=1 if [ $result -ne 0 ]; then - echo ::error::Build failed for $sketch + echo ${ci_error}Build failed for $cnt $sketch cat "$cache_dir/build.log" - echo ::endgroup:: + echo $ci_end_group return $result else grep -s -c warning: "$cache_dir"/build.log \ @@ -226,7 +196,7 @@ function build_sketches() print_size_info "$core_path"/tools/xtensa-lx106-elf/bin/xtensa-lx106-elf-size \ $build_dir/*.elf >>$cache_dir/size.log - echo ::endgroup:: + echo $ci_end_group done } @@ -276,14 +246,15 @@ function install_library() { local lib_path=$1 local name=$2 - local archive=$3 - local hash=$4 - local url=$5 + local extract=$3 + local archive=$4 + local hash=$5 + local url=$6 fetch_and_unpack "$archive" "$hash" "$url" mkdir -p "$lib_path" rm -rf "$lib_path/$name" - mv "$name" "$lib_path/$name" + mv "$extract" "$lib_path/$name" } function install_libraries() @@ -294,49 +265,30 @@ function install_libraries() mkdir -p "$core_path"/tools/dist pushd "$core_path"/tools/dist - install_library "$lib_path" \ - "ArduinoJson" \ - "ArduinoJson-v6.11.5.zip" \ - "8b836c862e69e60c4357a5ed7cbcf1310a3bb1c6bd284fe028faaa3d9d7eed319d10febc8a6a3e06040d1c73aaba5ca487aeffe87ae9388dc4ae1677a64d602c" \ - "https://github.com/bblanchon/ArduinoJson/releases/download/v6.11.5/ArduinoJson-v6.11.5.zip" + source "$root/tests/dep-libraries.sh" popd } -function install_ide() +function install_arduino_cli() { - # TODO replace ide distribution + arduino-builder with arduino-cli - local idever='1.8.19' - local ideurl="https://downloads.arduino.cc/arduino-$idever" + local path=$1 + local core_path=$2 - echo "Arduino IDE ${idever}" + local ver='1.2.2' + local urlbase="https://github.com/arduino/arduino-cli/releases/download/v${ver}/arduino-cli_${ver}_" - local core_path=$1 - local ide_path=$2 + echo "Arduino CLI ${ver}" - mkdir -p ${core_path}/tools/dist - pushd ${core_path}/tools/dist + mkdir -p ${core_path}/dist + pushd ${core_path}/dist - if [ "${RUNNER_OS-}" = "Windows" ]; then - fetch_and_unpack "arduino-windows.zip" \ - "c4072d808aea3848bceff5772f9d1e56a0fde02366b5aa523d10975c54eee2ca8def25ee466abbc88995aa323d475065ad8eb30bf35a2aaf07f9473f9168e2da" \ - "${ideurl}-windows.zip" - mv arduino-$idever arduino-distrib - elif [ "${RUNNER_OS-}" = "macOS" ]; then - fetch_and_unpack "arduino-macos.zip" \ - "053b0c1e70da9176680264e40fcb9502f45ca5a879aeb8b6f71282b38bfdb87c63ebc6b88e35ea70a73720ad439d828cc8cb110e4c6ab07357126a36ee396325" \ - "${ideurl}-macosx.zip" - # Hack to place arduino-builder in the same spot as sane OSes - mv Arduino.app arduino-distrib - mv arduino-distrib/Contents/Java/* arduino-distrib/. - else - fetch_and_unpack "arduino-linux.tar.xz" \ - "9328abf8778200019ed40d4fc0e6afb03a4cee8baaffbcea7dd3626477e14243f779eaa946c809fb153a542bf2ed60cf11a5f135c91ecccb1243c1387be95328" \ - "${ideurl}-linux64.tar.xz" - mv arduino-$idever arduino-distrib - fi + source "$root/tests/dep-arduino-cli.sh" + + mkdir -p $(dirname $path) + cp -v arduino-cli $path + chmod +x $path - mv arduino-distrib "$ide_path" popd } @@ -356,20 +308,24 @@ function install_core() fi # Set our custom warnings for all builds - { echo "compiler.c.extra_flags=-Wall -Wextra -Werror $debug_flags"; - echo "compiler.cpp.extra_flags=-Wall -Wextra -Werror $debug_flags"; - echo "mkbuildoptglobals.extra_flags=--ci --cache_core"; } \ - > platform.local.txt + printf "%s\n" \ + "compiler.c.extra_flags=-Wall -Wextra $debug_flags" \ + "compiler.cpp.extra_flags=-Wall -Wextra $debug_flags" \ + "mkbuildoptglobals.extra_flags=--ci --cache_core" \ + "recipe.hooks.prebuild.1.pattern=\"{runtime.tools.python3.path}/python3\" -I \"{runtime.tools.makecorever}\" --git-root \"{runtime.platform.path}\" --version \"{version}\" \"{runtime.platform.path}/cores/esp8266/core_version.h\"" \ + > ${core_path}/platform.local.txt echo -e "\n----platform.local.txt----" cat platform.local.txt echo -e "\n----\n" + # Fetch toolchain & filesystem utils pushd tools python3 get.py -q popd popd + # todo: windows runners are using copied tree local core_dir core_dir=$(dirname "$hardware_core_path") mkdir -p "$core_dir" @@ -383,19 +339,19 @@ function install_core() function install_arduino() { - echo ::group::Install arduino + echo ${ci_group}Install arduino local debug=$1 - test -d "$ESP8266_ARDUINO_IDE" \ - || install_ide "$ESP8266_ARDUINO_BUILD_DIR" "$ESP8266_ARDUINO_IDE" - local hardware_core_path="$ESP8266_ARDUINO_HARDWARE/esp8266com/esp8266" test -d "$hardware_core_path" \ || install_core "$ESP8266_ARDUINO_BUILD_DIR" "$hardware_core_path" "$debug" + command -v "${ESP8266_ARDUINO_CLI}" \ + || install_arduino_cli "${ESP8266_ARDUINO_CLI}" "$hardware_core_path" + install_libraries "$ESP8266_ARDUINO_BUILD_DIR" "$ESP8266_ARDUINO_LIBRARIES" - echo ::endgroup:: + echo $ci_end_group } function arduino_lwip_menu_option() @@ -410,25 +366,71 @@ function arduino_lwip_menu_option() esac } -function build_sketches_with_arduino() +# mkbuildoptglobals.py is optimized around the Arduino IDE 1.x +# behaviour. One way the CI differs from the Arduino IDE is in the +# handling of core and caching core. With the Arduino IDE, each sketch +# has a private copy of core and contributes to a core cache. With the +# CI, there is one shared copy of core for all sketches. When global +# options are used, the shared copy of core and cache are removed before +# and after the build. +function arduino_mkbuildoptglobals_cleanup() { - local build_mod=$1 - local build_rem=$2 + local clean_core=$1 + local build_dir=$2 + local sketch=$3 + + if [ -s ${sketch}.globals.h ]; then + clean_core=1 + fi + + # Remove sketch specific files from ./core/ between builds. + if [ $clean_core -ne 0 ]; then + rm -rf "$build_dir"/core/* + else + rm -rf "$build_dir/core/build.opt" "$build_dir"/core/*.ino.globals.h + fi + if [ -e ${build_dir}/core/*.a ]; then + # We need to preserve the build.options.json file and replace the last .ino + # with this sketch's ino file, or builder will throw everything away. + jq '."sketchLocation" = "'$sketch'"' $build_dir/build.options.json \ + > "$build_dir"/build.options.json.tmp + mv "$build_dir"/build.options.json.tmp "$build_dir"/build.options.json + if [ $clean_core -ne 0 ]; then + # Hack workaround for CI not handling core rebuild for global options + rm ${build_dir}/core/*.a + fi + fi + + if [ -s ${sketch}.globals.h ]; then + # Set to cleanup core at the start of the next build. + clean_core=1 + else + clean_core=0 + fi + + echo $clean_core +} + +function build_sketches_with_arduino() +{ local lwip - lwip=$(arduino_lwip_menu_option $3) + lwip=$(arduino_lwip_menu_option $1) + + local build_mod=$2 + local build_rem=$3 + local build_cnt=$4 build_sketches "$ESP8266_ARDUINO_BUILD_DIR" \ - "$ESP8266_ARDUINO_IDE" \ - "$ESP8266_ARDUINO_HARDWARE" \ + "$ESP8266_ARDUINO_CLI" \ "$ESP8266_ARDUINO_LIBRARIES" \ - "$build_mod" "$build_rem" "$lwip" + "$lwip" "$build_mod" "$build_rem" "$build_cnt" step_summary "Size report" "$cache_dir/size.log" } function install_platformio() { - echo ::group::Install PlatformIO + echo ${ci_group}Install PlatformIO local board=$1 @@ -436,6 +438,8 @@ function install_platformio() python3 get.py -q popd + install_libraries "$ESP8266_ARDUINO_BUILD_DIR" "$ESP8266_ARDUINO_LIBRARIES" + # we should reference our up-to-date build tools # ref. https://docs.platformio.org/en/latest/core/userguide/pkg/cmd_install.html pio pkg install --global --skip-dependencies --platform "https://github.com/platformio/platform-espressif8266.git" @@ -443,9 +447,11 @@ function install_platformio() local framework_symlink="framework-arduinoespressif8266 @ symlink://${ESP8266_ARDUINO_BUILD_DIR}" local toolchain_symlink="toolchain-xtensa @ symlink://${ESP8266_ARDUINO_BUILD_DIR}/tools/xtensa-lx106-elf/" - # pre-generate config; pio-ci with multiple '-O' replace each other instead of appending to the same named list - # (and, it is much nicer to write this instead of a multi-line cmdline with several large strings) + # pre-generate config; pio-ci with multiple '-O' options replace each other instead of appending to the same named list cat < $cache_dir/platformio.ini +[platformio] +lib_dir = + ${ESP8266_ARDUINO_LIBRARIES} [env:$board] platform = espressif8266 board = $board @@ -455,17 +461,14 @@ platform_packages = ${toolchain_symlink} EOF - # Install dependencies: - # - esp8266/examples/ConfigFile - pio pkg install --global --library "ArduinoJson@^6.11.0" - - echo ::endgroup:: + echo $ci_end_group } function build_sketches_with_platformio() { local build_mod=$1 local build_rem=$2 + local build_cnt=$3 local testcnt=0 for sketch in $ESP8266_ARDUINO_SKETCHES; do @@ -474,23 +477,25 @@ function build_sketches_with_platformio() continue # Not ours to do fi - local sketchdir - sketchdir=$(dirname $sketch) - - local sketchdirname - sketchdirname=$(basename $sketchdir) - - local sketchname - sketchname=$(basename $sketch) - local skip - skip=$(skip_sketch "$sketch" "$sketchname" "$sketchdir" "$sketchdirname") + skip=$(skip_sketch "$sketch") if [ -n "$skip" ]; then echo "$skip" - continue + continue # Should be skipped / cannot be built fi - echo ::group::Building $sketch + cnt=$(( $cnt + 1 )) + if [ $build_cnt != 0 ] ; then + if [ $build_cnt != $cnt ] ; then + continue # Haven't reached the $cnt yet + fi + build_cnt=0 + fi + + echo ${ci_group}Building $sketch + + local sketchdir + sketchdir=$(dirname $sketch) local result time pio ci \ @@ -500,13 +505,13 @@ function build_sketches_with_platformio() && result=0 || result=1 if [ $result -ne 0 ]; then - echo ::error::Build failed for $sketch + echo ${ci_error}Build failed for $sketch cat "$cache_dir/build.log" - echo ::endgroup:: + echo $ci_end_group return $result fi - echo ::endgroup:: + echo $ci_end_group done } diff --git a/tests/dep-arduino-cli.sh b/tests/dep-arduino-cli.sh new file mode 100644 index 0000000000..aa46e3d458 --- /dev/null +++ b/tests/dep-arduino-cli.sh @@ -0,0 +1,27 @@ +case "${RUNNER_OS-}" in +"Linux") + fetch_and_unpack "Linux_64bit.tar.gz" \ + "d421e2b1cbef59c41e46cf06d077214a1d24cb784030462763781c9d3911cc55257fbcc02a7ee6a2ddda5b459101dc83aeda6b3b5198805bfdce856f82774c93" \ + "${urlbase}Linux_64bit.tar.gz" + ;; +"Windows") + fetch_and_unpack "Windows_64bit.zip" \ + "05b4eb5820fbaf670de00399d40513ecf2de9d0c2c5593a1227be03b2d11ba53e9d14cf6f934110447d6fd15c6a09769606a34fcab32ec3c2dbaa42f4627b072" \ + "${urlbase}Windows_64bit.zip" + ;; +"macOS") + if [ "${RUNNER_ARCH-}" = "ARM64" ] ; then + fetch_and_unpack "macOS_ARM64.tar.gz" \ + "672693418b730d8ebc57cae2c892553e821706bee06312cc77a598e834afcba7d380df4d337138ecc03a4013a349d89b744b2a3b97fafc214b619856d9162827" \ + "${urlbase}macOS_ARM64.tar.gz" + else + fetch_and_unpack "macOS_64bit.tar.gz" \ + "5659f08d787840aa6689fd063477402b4ed572663fea20de496b249d86a440059e3e6f377bd8020fb6b67202c1bdea6f98a4c4e052c31f01b2c9027ebec10b04" \ + "${urlbase}macOS_64bit.tar.gz" + fi + ;; +*) + echo 'Unknown ${RUNNER_OS} = "' ${RUNNER_OS} '"' + exit 2 +esac + diff --git a/tests/dep-libraries.sh b/tests/dep-libraries.sh new file mode 100644 index 0000000000..cd3b2124c8 --- /dev/null +++ b/tests/dep-libraries.sh @@ -0,0 +1,6 @@ +install_library "$lib_path" \ + "ArduinoJson" \ + "ArduinoJson-7.4.1" \ + "ArduinoJson-v7.4.1.zip" \ + "1bfbc4aa3e4aa3c8e38f660333b6d5129b0443dbd0fd1eb448d14c7f041ffd2df782951ce622c6da50e2a07dcfcd268727b1f0a000211b9a5a92a806b1645bc0" \ + "https://github.com/bblanchon/ArduinoJson/archive/refs/tags/v7.4.1.zip" diff --git a/tests/device/Makefile b/tests/device/Makefile index cbbd34728a..f89c9ea93e 100644 --- a/tests/device/Makefile +++ b/tests/device/Makefile @@ -1,45 +1,65 @@ SHELL := /bin/bash -V ?= 0 -ESP8266_CORE_PATH ?= $(realpath ../..) -BUILD_DIR ?= $(PWD)/.build -HARDWARE_DIR ?= $(PWD)/.hardware + +ESP8266_CORE_PATH ?= $(shell git rev-parse --show-toplevel) + +BUILD_DIR ?= $(PWD)/build +BS_DIR ?= $(PWD)/libraries/BSTest + PYTHON ?= python3 ESPTOOL ?= $(PYTHON) $(ESP8266_CORE_PATH)/tools/esptool/esptool.py -MKSPIFFS ?= $(ESP8266_CORE_PATH)/tools/mkspiffs/mkspiffs + +VENV_PYTHON ?= $(BS_DIR)/virtualenv/bin/python +VENV_JUNIT2HTML ?= $(BS_DIR)/virtualenv/bin/junit2html + +MKFS ?= $(ESP8266_CORE_PATH)/tools/mklittlefs/mklittlefs + UPLOAD_PORT ?= $(shell ls /dev/tty* | grep -m 1 -i USB) UPLOAD_BAUD ?= 460800 -UPLOAD_BOARD ?= nodemcu -BS_DIR ?= libraries/BSTest -DEBUG_LEVEL ?= lvl=None____ -FQBN ?= esp8266com:esp8266:generic:xtal=160,FlashFreq=40,FlashMode=dio,baud=115200,eesz=4M1M,ip=lm2f,ResetMethod=nodemcu,dbg=Serial,$(DEBUG_LEVEL) -BUILD_TOOL := $(ARDUINO_IDE_PATH)/arduino-builder + +TEST_BAUD ?= 115200 +BUILD_TOOL ?= arduino-cli + +BUILD_BOARD ?= generic +BUILD_CPU ?= 160 +BUILD_SIZE ?= 4M1M +BUILD_LWIP ?= lm2f +BUILD_EXTRA ?= ,dbg=Serial,lvl=CORE + +FQBN ?= esp8266com:esp8266:$(BUILD_BOARD):xtal=$(BUILD_CPU),baud=$(TEST_BAUD),eesz=$(BUILD_SIZE),ip=$(BUILD_LWIP)$(BUILD_EXTRA) + TEST_CONFIG := test_env.cfg +TEST_RESULT_XML := test_result.xml TEST_REPORT_XML := test_report.xml TEST_REPORT_HTML := test_report.html -ifeq ("$(MOCK)", "1") -# To enable a test for mock testing, just rename dir+files to '*_sw_*' -TEST_LIST ?= $(wildcard test_sw_*/*.ino) -else TEST_LIST ?= $(wildcard test_*/*.ino) +BUILD_FLAGS ?= +BS_FLAGS ?= + +# To enable a test for mock testing, just rename dir+files to '*_sw_*' +ifeq ("$(MOCK)", "1") + TEST_LIST := $(filter test_sw_%.ino,$(TEST_LIST)) + NO_UPLOAD := 1 + NO_RUN := 1 endif -ifneq ("$(V)","1") - SILENT = @ - REDIR = >& /dev/null -else - BUILDER_DEBUG_FLAG = -verbose - RUNNER_DEBUG_FLAG = -d - #UPLOAD_VERBOSE_FLAG = -v +# To enable verbose mode, call `make V=1` ... +V ?= 0 +ifeq ("$(V)", "1") + BUILD_FLAGS += --verbose + BS_FLAGS += --debug endif +# ${sketch}.py helper script when building locally +mock_script = \ + `test -f $(addsuffix .py, $(basename $(1))) && echo "--mock $(addsuffix .py, $(basename $(1)))" || echo ""` + help: @echo @echo 'make list - show list of tests' @echo 'make sometest/sometest.ino - run one test' @echo 'make all - run all tests' @echo 'make MOCK=1 all - run all emulation-on-host compatible tests' - @echo 'variables needed: $$ARDUINO_IDE_PATH $$ESP8266_CORE_PATH' @echo 'make options: V=1 NO_BUILD=1 NO_UPLOAD=1 NO_RUN=1 MOCK=1' @echo @@ -49,110 +69,132 @@ all: count tests test_report $(TEST_LIST): | virtualenv $(TEST_CONFIG) $(BUILD_DIR) $(HARDWARE_DIR) +.NOTPARALLEL: $(TEST_LIST) + tests: showtestlist $(TEST_LIST) showtestlist: @echo "-------------------------------- test list:" - @echo $(TEST_LIST) + @printf '%s\n' $(TEST_LIST) @echo "--------------------------------" $(TEST_LIST): LOCAL_BUILD_DIR=$(BUILD_DIR)/$(notdir $@) - -$(TEST_LIST): - @echo "--------------------------------" - @echo "Running test '$@' of $(words $(TEST_LIST)) tests" - $(SILENT)mkdir -p $(LOCAL_BUILD_DIR) -ifeq ("$(MOCK)", "1") - @echo Compiling $(notdir $@) - (cd ../host; make D=$(V) ULIBDIRS=../device/libraries/BSTest ../device/$(@:%.ino=%)) - $(SILENT)$(BS_DIR)/virtualenv/bin/python \ - $(BS_DIR)/runner.py \ - $(RUNNER_DEBUG_FLAG) \ - -e "$(ESP8266_CORE_PATH)/tests/host/bin/$(@:%.ino=%)" \ - -n $(basename $(notdir $@)) \ - -o $(LOCAL_BUILD_DIR)/test_result.xml \ - --env-file $(TEST_CONFIG) \ - `test -f $(addsuffix .py, $(basename $@)) && echo "-m $(addsuffix .py, $(basename $@))" || echo ""` -else -ifneq ("$(NO_BUILD)","1") - @test -n "$(ARDUINO_IDE_PATH)" || (echo "Please export ARDUINO_IDE_PATH" && exit 1) - @echo Compiling $(notdir $@) - @rm -f $(LOCAL_BUILD_DIR)/build.options.json - $(SILENT)$(BUILD_TOOL) -compile -logger=human \ - -libraries "$(PWD)/libraries" \ - -core-api-version="10608" \ - -warnings=all \ - $(BUILDER_DEBUG_FLAG) \ - -build-path $(LOCAL_BUILD_DIR) \ - -tools $(ARDUINO_IDE_PATH)/tools-builder \ - -hardware $(HARDWARE_DIR)\ - -hardware $(ARDUINO_IDE_PATH)/hardware \ - -fqbn=$(FQBN) \ +$(TEST_LIST): LOCAL_DATA_IMG=data.img + +define build-arduino + rm -f $(LOCAL_BUILD_DIR)/build.options.json + $(BUILD_TOOL) compile \ + $(BUILD_FLAGS) \ + --libraries "$(PWD)/libraries" \ + --warnings=all \ + --build-path $(LOCAL_BUILD_DIR) \ + --fqbn=$(FQBN) \ $@ -endif -ifneq ("$(NO_UPLOAD)","1") - @test -n "$(UPLOAD_PORT)" || (echo "Failed to detect upload port, please export UPLOAD_PORT manually" && exit 1) - @test -e $(dir $@)/make_spiffs.py && ( \ - echo "Generating and uploading SPIFFS" && \ - (cd $(dir $@) && $(PYTHON) ./make_spiffs.py $(REDIR) ) && \ - $(MKSPIFFS) --create $(dir $@)data/ --size 0xFB000 \ - --block 8192 --page 256 $(LOCAL_BUILD_DIR)/spiffs.img $(REDIR) && \ - $(ESPTOOL) $(UPLOAD_VERBOSE_FLAG) \ +endef + +define build-mock + (cd $(ESP8266_CORE_PATH)/test/host; \ + $(MAKE) D=$(V) ULIBDIRS=$(PWD)/libraries/BSTest $(PWD)/$(@:%.ino=%)) + $(VENV_PYTHON) $(BS_DIR)/runner.py \ + $(BS_FLAGS) \ + --name $(basename $(notdir $@)) \ + --output $(LOCAL_BUILD_DIR)/$(TEST_RESULT_XML) \ + --env-file $(TEST_CONFIG) \ + $(call mock_script,$@) \ + executable "$(ESP8266_CORE_PATH)/tests/host/bin/$(@:%.ino=%)" || echo ""` +endef + +define upload-data + @test -n "$(UPLOAD_PORT)" \ + || (echo "Failed to detect upload port, please export UPLOAD_PORT manually" && exit 1) + @test ! \( -d $(dir $@)/data/ \) -a \( -e $(dir $@)/make_data.py \) && \ + (cd $(dir $@) && ./make_data.py ) || echo "Filesystem creation skipped" + @test -d $(dir $@)/data/ && ( \ + $(MKFS) \ + --create $(dir $@)/data/ \ + --size 0xFB000 \ + --block 8192 \ + --page 256 \ + $(LOCAL_BUILD_DIR)/$(LOCAL_DATA_IMG) && \ + $(ESPTOOL) \ --chip esp8266 \ --port $(UPLOAD_PORT) \ --baud $(UPLOAD_BAUD) \ --after no_reset \ - write_flash 0x300000 $(LOCAL_BUILD_DIR)/spiffs.img $(REDIR) ) \ - || (echo "No SPIFFS to upload") - @echo Uploading binary - $(SILENT)$(ESPTOOL) $(UPLOAD_VERBOSE_FLAG) \ + write_flash 0x300000 $(LOCAL_BUILD_DIR)/$(LOCAL_DATA_IMG) ) \ + && (echo "Uploaded filesystem") \ + || (echo "Filesystem upload skipped") +endef + +define upload-binary + @test -n "$(UPLOAD_PORT)" \ + || (echo "Failed to detect upload port, please export UPLOAD_PORT manually" && exit 1) + $(ESPTOOL) \ --chip esp8266 \ --port $(UPLOAD_PORT) \ --baud $(UPLOAD_BAUD) \ --after no_reset \ - write_flash 0x0 $(LOCAL_BUILD_DIR)/$(notdir $@).bin $(REDIR) # no reset -endif -ifneq ("$(NO_RUN)","1") - @test -n "$(UPLOAD_PORT)" || (echo "Failed to detect upload port, please export UPLOAD_PORT manually" && exit 1) + write_flash 0x0 $(LOCAL_BUILD_DIR)/$(notdir $@).bin +endef + +define run-test + @test -n "$(UPLOAD_PORT)" \ + || (echo "Failed to detect upload port, please export UPLOAD_PORT manually" && exit 1) @echo Running tests - $(SILENT)$(ESPTOOL) $(UPLOAD_VERBOSE_FLAG) \ + $(ESPTOOL) \ --chip esp8266 \ --port $(UPLOAD_PORT) \ --baud $(UPLOAD_BAUD) \ - read_flash_status $(REDIR) # reset - $(SILENT)$(BS_DIR)/virtualenv/bin/python \ - $(BS_DIR)/runner.py \ - $(RUNNER_DEBUG_FLAG) \ - -p $(UPLOAD_PORT) \ - -n $(basename $(notdir $@)) \ - -o $(LOCAL_BUILD_DIR)/test_result.xml \ - --env-file $(TEST_CONFIG) \ - `test -f $(addsuffix .py, $(basename $@)) && echo "-m $(addsuffix .py, $(basename $@))" || echo ""` + read_flash_status # reset via implicit stub reboot + $(VENV_PYTHON) $(BS_DIR)/runner.py \ + $(BS_FLAGS) \ + --name $(basename $(notdir $@)) \ + --output $(LOCAL_BUILD_DIR)/$(TEST_RESULT_XML) \ + --env-file $(TEST_CONFIG) \ + $(call mock_script,$@) \ + port $(UPLOAD_PORT) \ + --baudrate $(TEST_BAUD) +endef + +$(TEST_LIST): + @echo "--------------------------------" + @echo "Running test '$@' of $(words $(TEST_LIST)) tests" + mkdir -p $(LOCAL_BUILD_DIR) +ifneq ("$(NO_BUILD)","1") + @echo Building $(notdir $@) +ifeq ("$(MOCK)", "1") + $(build-mock) +else + $(build-arduino) endif endif +ifneq ("$(NO_UPLOAD)","1") + $(upload-filesystem) + $(upload-binary) +endif +ifneq ("$(NO_RUN)","1") + $(run-test) +endif -$(TEST_REPORT_XML): $(HARDWARE_DIR) virtualenv - $(SILENT)$(BS_DIR)/xunitmerge $(shell find $(BUILD_DIR) -name 'test_result.xml' | xargs echo) $(TEST_REPORT_XML) +$(TEST_REPORT_XML): virtualenv + $(BS_DIR)/xunitmerge \ + $(shell find $(BUILD_DIR) -name '$(TEST_RESULT_XML)' | xargs echo) \ + $(TEST_REPORT_XML) $(TEST_REPORT_HTML): $(TEST_REPORT_XML) | virtualenv - $(SILENT)$(BS_DIR)/virtualenv/bin/junit2html $< $@ + $(VENV_JUNIT2HTML) $< $@ test_report: $(TEST_REPORT_HTML) @echo "Test report generated in $(TEST_REPORT_HTML)" $(BUILD_DIR): - mkdir -p $(BUILD_DIR) - -$(HARDWARE_DIR): - mkdir -p $(HARDWARE_DIR)/esp8266com - cd $(HARDWARE_DIR)/esp8266com && ln -s $(realpath $(ESP8266_CORE_PATH)) esp8266 + @mkdir -p $(BUILD_DIR) virtualenv: @make -C $(BS_DIR) PYTHON=$(PYTHON) virtualenv clean: rm -rf $(BUILD_DIR) - rm -rf $(HARDWARE_DIR) rm -rf $(BS_DIR)/virtualenv rm -f $(TEST_REPORT_HTML) $(TEST_REPORT_XML) diff --git a/tests/device/libraries/BSTest/runner.py b/tests/device/libraries/BSTest/runner.py index 5c9dddfef9..30aa93c111 100644 --- a/tests/device/libraries/BSTest/runner.py +++ b/tests/device/libraries/BSTest/runner.py @@ -1,40 +1,32 @@ #!/usr/bin/env python3 -from __future__ import print_function + +import serial + import pexpect from pexpect import EOF, TIMEOUT, fdpexpect -import sys -import os -import time + +from junit_xml import TestSuite, TestCase + import argparse -import serial +import itertools +import os import subprocess +import sys +import time +from configparser import ConfigParser from importlib.machinery import SourceFileLoader +from io import StringIO +from urllib.parse import urlparse, urlencode -try: - from configparser import ConfigParser -except: - from ConfigParser import ConfigParser -import itertools -try: - from urllib.parse import urlparse, urlencode -except ImportError: - from urlparse import urlparse -from junit_xml import TestSuite, TestCase -try: - from cStringIO import StringIO -except: - try: - from StringIO import StringIO - except ImportError: - from io import StringIO import mock_decorators debug = False -#debug = True sys.path.append(os.path.abspath(__file__)) +IS_WSL = len(os.environ.get("WSL_DISTRO_NAME", "")) > 0 + def debug_print(*args, **kwargs): if not debug: return @@ -235,9 +227,11 @@ def request_env(self, key): ser = None -def spawn_port(port_name, baudrate=115200): +def spawn_port(port_name, baudrate=115200, close=True): global ser ser = serial.serial_for_url(port_name, baudrate=baudrate) + if not close: + ser.close = lambda: None return fdpexpect.fdspawn(ser, 'wb', timeout=0, encoding='cp437') def spawn_exec(name): @@ -250,30 +244,39 @@ def run_tests(spawn, name, mocks, env_vars): def parse_args(): parser = argparse.ArgumentParser(description='BS test runner') - parser.add_argument('-d', '--debug', help='Send test output to stderr', action='store_true') - parser.add_argument('-p', '--port', help='Talk to the test over serial') - parser.add_argument('-e', '--executable', help='Talk to the test executable') - parser.add_argument('-n', '--name', help='Test run name') - parser.add_argument('-o', '--output', help='Output JUnit format test report') - parser.add_argument('-m', '--mock', help='Set python script to use for mocking purposes') + parser.add_argument('--debug', help='Send test output to stderr', action='store_true') + parser.add_argument('--name', help='Test run name') + parser.add_argument('--output', help='Output JUnit format test report') + parser.add_argument('--mock', help='Set python script to use for mocking purposes') parser.add_argument('--env-file', help='File containing a list of environment variables to set', type=argparse.FileType('r')) + + sub = parser.add_subparsers() + + def as_spawn_port(args): + return spawn_port(args.port, args.baudrate, args.close) + + port = sub.add_parser('port') + port.add_argument('port', type=str) + port.add_argument('--baudrate', help='Serial port baudrate', type=int, default=115200) + port.add_argument('--close', help='Close serial port after the test', action=argparse.BooleanOptionalAction, default=not IS_WSL) + port.set_defaults(func=as_spawn_port) + + def as_spawn_exec(args): + return spawn_exec(args.executable) + + exe = sub.add_parser('executable') + exe.add_argument('executable', help='Talk to the test executable') + exe.set_defaults(func=as_spawn_exec) + return parser.parse_args() def main(): args = parse_args() - spawn_func = None - spawn_arg = None - if args.port is not None: - spawn_func = spawn_port - spawn_arg = args.port - elif args.executable is not None: - spawn_func = spawn_exec - spawn_arg = args.executable + name = args.name or "" global debug - if args.debug: - debug = True - if spawn_func is None: + debug = args.debug + if args.func is None: debug_print("Please specify port or executable", file=sys.stderr) return 1 env_vars = [] @@ -287,7 +290,8 @@ def main(): if args.mock is not None: mocks_mod = SourceFileLoader('mocks', args.mock).load_module() mocks = mock_decorators.env - with spawn_func(spawn_arg) as sp: + + with args.func(args) as sp: ts = run_tests(sp, name, mocks, env_vars) if args.output: with open(args.output, "w") as f: diff --git a/tests/device/libraries/BSTest/src/BSTest.h b/tests/device/libraries/BSTest/src/BSTest.h index f73de3e471..ba3c9a5c0d 100644 --- a/tests/device/libraries/BSTest/src/BSTest.h +++ b/tests/device/libraries/BSTest/src/BSTest.h @@ -27,8 +27,7 @@ class TestCase public: TestCase(TestCase* prev, test_case_func_t func, const char* file, size_t line, const char* name, const char* desc) : - m_func(func), - m_file(file), m_line(line), m_name(name), m_desc(desc) + m_func(func), m_file(file), m_line(line), m_name(name), m_desc(desc) { if (prev) { diff --git a/tests/device/test_BearSSL/make_data.py b/tests/device/test_BearSSL/make_data.py new file mode 120000 index 0000000000..987c479fbc --- /dev/null +++ b/tests/device/test_BearSSL/make_data.py @@ -0,0 +1 @@ +../../../libraries/ESP8266WiFi/examples/BearSSL_CertStore/certs-from-mozilla.py \ No newline at end of file diff --git a/tests/device/test_BearSSL/make_spiffs.py b/tests/device/test_BearSSL/make_spiffs.py deleted file mode 100755 index e423bc25b9..0000000000 --- a/tests/device/test_BearSSL/make_spiffs.py +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/python3 - -# This script pulls the list of Mozilla trusted certificate authorities -# from the web at the "mozurl" below, parses the file to grab the PEM -# for each cert, and then generates DER files in a new ./data directory -# Upload these to a SPIFFS filesystem and use the CertManager to parse -# and use them for your outgoing SSL connections. -# -# Script by Earle F. Philhower, III. Released to the public domain. -from __future__ import print_function -import csv -import os -import sys -from subprocess import Popen, PIPE, call -try: - from urllib.request import urlopen -except: - from urllib2 import urlopen -try: - from StringIO import StringIO -except: - from io import StringIO - -# Mozilla's URL for the CSV file with included PEM certs -mozurl = "https://ccadb-public.secure.force.com/mozilla/IncludedCACertificateReportPEMCSV" - -# Load the manes[] and pems[] array from the URL -names = [] -pems = [] -response = urlopen(mozurl) -csvData = response.read() -if sys.version_info[0] > 2: - csvData = csvData.decode('utf-8') -csvFile = StringIO(csvData) -csvReader = csv.reader(csvFile) -for row in csvReader: - names.append(row[0]+":"+row[1]+":"+row[2]) - pems.append(row[30]) -del names[0] # Remove headers -del pems[0] # Remove headers - -# Try and make ./data, skip if present -try: - os.mkdir("data") -except: - pass - -derFiles = [] -idx = 0 -# Process the text PEM using openssl into DER files -for i in range(0, len(pems)): - certName = "data/ca_%03d.der" % (idx); - thisPem = pems[i].replace("'", "") - print(names[i] + " -> " + certName) - ssl = Popen(['openssl','x509','-inform','PEM','-outform','DER','-out', certName], shell = False, stdin = PIPE) - pipe = ssl.stdin - pipe.write(thisPem.encode('utf-8')) - pipe.close() - ssl.wait() - if os.path.exists(certName): - derFiles.append(certName) - idx = idx + 1 - -if os.path.exists("data/certs.ar"): - os.unlink("data/certs.ar"); - -arCmd = ['ar', 'q', 'data/certs.ar'] + derFiles; -call( arCmd ) - -for der in derFiles: - os.unlink(der) diff --git a/tests/device/test_BearSSL/test_BearSSL.ino b/tests/device/test_BearSSL/test_BearSSL.ino index e4db2fdc0e..73ab226524 100644 --- a/tests/device/test_BearSSL/test_BearSSL.ino +++ b/tests/device/test_BearSSL/test_BearSSL.ino @@ -1,7 +1,7 @@ // Stress test the BearSSL connection options to determine // maximum memory use for different SSL connections and -// SPIFFS certstore usage. Before running you need to run -// certs-from-mozilla.py and upload the generated SPIFFS file. +// filesystem certstore usage. Before running the test you need to +// update them with certs-from-mozilla.py and upload the generated file. // // For more info on CertStores, see the BearSSL_CertStore example // @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include @@ -45,11 +45,11 @@ bool pretest() delay(500); } setClock(); - SPIFFS.begin(); - int numCerts = certStore.initCertStore(SPIFFS, "/certs.idx", "/certs.ar"); + LittleFS.begin(); + int numCerts = certStore.initCertStore(LittleFS, "/certs.idx", "/certs.ar"); Serial.printf("Number of CA certs read: %d\n", numCerts); if (numCerts == 0) { - Serial.printf("No certs found. Did you run certs-from-mozill.py and upload the SPIFFS directory before running?\n"); + Serial.printf("No certs found. Did you run upload script?\n"); return false; } return true; diff --git a/tests/device/test_libc/libm_string.c b/tests/device/test_libc/libm_string.c index 9a19da58a4..dd464fbd46 100644 --- a/tests/device/test_libc/libm_string.c +++ b/tests/device/test_libc/libm_string.c @@ -3,51 +3,130 @@ #include #include -#define memcmp memcmp_P -#define memcpy memcpy_P -#define memmem memmem_P -#define memchr memchr_P -#define strcat strcat_P -#define strncat strncat_P -#define strcpy strcpy_P -#define strncpy strncpy_P -#define strlen strlen_P -#define strnlen strnlen_P -#define strcmp strcmp_P -#define strncmp strncmp_P - -_CONST char* it = ""; /* Routine name for message routines. */ +#include + +/* esp8266/Arduino note + * + * Prevent the compiler from + * - solving test cases below at compile time, effectively removing any checks + * - optimizing out libc func calls, replacing them with bytewise memory access + * + * Plus, test framework cannot pass -fno-builtin-... per-file, only globally + */ + +#define xDST_SRC_N(T, NAME)\ +static T* __attribute__((used, noinline)) x ## NAME (T* dst, const T* src, size_t n)\ +{\ + return NAME (dst, src, n);\ +} + +xDST_SRC_N (void, memcpy) +xDST_SRC_N (void, memmove) +xDST_SRC_N (char, strncat) +xDST_SRC_N (char, strncpy) + +#define xDST_SRC(T, NAME)\ +static T* __attribute__((used, noinline)) x ## NAME (T* dst, const T* src)\ +{\ + return NAME (dst, src);\ +} + +xDST_SRC (char, strcat) +xDST_SRC (char, strcpy) + +#define xS1_S2_N(RET, T, NAME)\ +static RET __attribute__((used, noinline)) x ## NAME (const T *s1, const T *s2, size_t n)\ +{\ + return NAME (s1, s2, n);\ +} + +xS1_S2_N(int, void, memcmp) +xS1_S2_N(int, char, strncmp) + +static int __attribute__((used, noinline)) xstrcmp (const char *s1, const char *s2) +{ + return strcmp (s1, s2); +} + +static void* __attribute__((used, noinline)) xmemchr (const void* s, int c, size_t n) +{ + return memchr (s, c, n); +} + +static size_t __attribute__((used, noinline)) xstrlen (const char* s) +{ + return strlen (s); +} + +#if 0 + +/* TODO remove when libc supports pointers to flash */ +#undef PSTR +#define PSTR(X) X + +#define memcmp(s1,s2,n) xmemcmp(s1,PSTR(s2),n) +#define memcpy(dest,src,n) xmemcpy(dest,PSTR(src),n) +#define memmove(dest,src,n) xmemmove(dest,PSTR(src),n) +#define memchr(s,c,n) xmemchr(PSTR(s),c,n) +#define strcat(dst,src) xstrcat(dst,PSTR(src)) +#define strncat(dst,src,ssize) xstrncat(dst,PSTR(src),ssize) +#define strcpy(dst,src) xstrcpy(dst,PSTR(src)) +#define strncpy(dst,src,dsize) xstrncpy(dst,PSTR(src),dsize) +#define strlen(s) xstrlen(PSTR(s)) +#define strcmp(s1,s2) xstrcmp(s1,PSTR(s2)) +#define strncmp(s1,s2,n) xstrncmp(s1,PSTR(s2),n) + +#else + +/* in case wrapped calls are not required */ + +#define memcmp(s1,s2,n) memcmp_P(s1,PSTR(s2),n) +#define memcpy(dest,src,n) memcpy_P(dest,PSTR(src),n) +#define memmove(dest,src,n) memmove_P(dest,PSTR(src),n) +#define memchr(s,c,n) memchr_P(PSTR(s),c,n) +#define strcat(dst,src) strcat_P(dst,PSTR(src)) +#define strncat(dst,src,ssize) strncat_P(dst,PSTR(src),ssize) +#define strcpy(dst,src) strcpy_P(dst,PSTR(src)) +#define strncpy(dst,src,dsize) strncpy_P(dst,PSTR(src),dsize) +#define strlen(s) strlen_P(PSTR(s)) +#define strcmp(s1,s2) strcmp_P(s1,PSTR(s2)) +#define strncmp(s1,s2,n) strncmp_P(s1,PSTR(s2),n) + +#endif + +static const char Unset[] PROGMEM = ""; + +const char* it = Unset; /* Routine name for message routines. */ static int errors = 0; /* Complain if condition is not true. */ #define check(thing) checkit(thing, __LINE__) -static void _DEFUN(checkit, (ok, l), int ok _AND int l) - +static void checkit(int ok, int l) { // newfunc(it); // line(l); if (!ok) { - printf("string.c:%d %s\n", l, it); + printf(PSTR("string.c:%d %s\n"), l, it); ++errors; } } /* Complain if first two args don't strcmp as equal. */ -#define equal(a, b) funcqual(a, b, __LINE__); +#define equal(a, b) funcqual(a, PSTR(b), __LINE__); -static void _DEFUN(funcqual, (a, b, l), char* a _AND char* b _AND int l) +static void funcqual(const char *a, const char *b, int l) { // newfunc(it); // line(l); if (a == NULL && b == NULL) return; - if (strcmp(a, b)) + if (strcmp_P(a, b)) { - printf("string.c:%d (%s)\n", l, it); + printf(PSTR("string.c:%d (%s)\n"), l, it); } } @@ -57,7 +136,7 @@ static char two[50]; void libm_test_string() { /* Test strcmp first because we use it to test other things. */ - it = "strcmp"; + it = PSTR("strcmp"); check(strcmp("", "") == 0); /* Trivial case. */ check(strcmp("a", "a") == 0); /* Identity. */ check(strcmp("abc", "abc") == 0); /* Multicharacter. */ @@ -69,7 +148,7 @@ void libm_test_string() check(strcmp("a\103", "a\003") > 0); /* Test strcpy next because we need it to set up other tests. */ - it = "strcpy"; + it = PSTR("strcpy"); check(strcpy(one, "abcd") == one); /* Returned value. */ equal(one, "abcd"); /* Basic test. */ @@ -78,7 +157,7 @@ void libm_test_string() equal(one + 2, "cd"); /* Wrote too much? */ (void)strcpy(two, "hi there"); - (void)strcpy(one, two); + (void)xstrcpy(one, two); equal(one, "hi there"); /* Basic test encore. */ equal(two, "hi there"); /* Stomped on source? */ @@ -86,7 +165,7 @@ void libm_test_string() equal(one, ""); /* Boundary condition. */ /* strcat. */ - it = "strcat"; + it = PSTR("strcat"); (void)strcpy(one, "ijk"); check(strcat(one, "lmn") == one); /* Returned value. */ equal(one, "ijklmn"); /* Basic test. */ @@ -98,7 +177,7 @@ void libm_test_string() (void)strcpy(one, "gh"); (void)strcpy(two, "ef"); - (void)strcat(one, two); + (void)xstrcpy(one, two); equal(one, "ghef"); /* Basic test encore. */ equal(two, "ef"); /* Stomped on source? */ @@ -114,42 +193,67 @@ void libm_test_string() /* strncat - first test it as strcat, with big counts, then test the count mechanism. */ - it = "strncat"; + it = PSTR("strncat"); (void)strcpy(one, "ijk"); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-overflow=" check(strncat(one, "lmn", 99) == one); /* Returned value. */ +#pragma GCC diagnostic pop equal(one, "ijklmn"); /* Basic test. */ (void)strcpy(one, "x"); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-overflow=" (void)strncat(one, "yz", 99); +#pragma GCC diagnostic pop equal(one, "xyz"); /* Writeover. */ equal(one + 4, "mn"); /* Wrote too much? */ (void)strcpy(one, "gh"); (void)strcpy(two, "ef"); - (void)strncat(one, two, 99); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds=" +#pragma GCC diagnostic ignored "-Wstringop-overflow=" + (void)xstrncat(one, two, 99); +#pragma GCC diagnostic pop equal(one, "ghef"); /* Basic test encore. */ equal(two, "ef"); /* Stomped on source? */ (void)strcpy(one, ""); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-overflow=" (void)strncat(one, "", 99); +#pragma GCC diagnostic pop equal(one, ""); /* Boundary conditions. */ (void)strcpy(one, "ab"); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-overflow=" (void)strncat(one, "", 99); +#pragma GCC diagnostic pop equal(one, "ab"); (void)strcpy(one, ""); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-overflow=" (void)strncat(one, "cd", 99); +#pragma GCC diagnostic pop equal(one, "cd"); (void)strcpy(one, "ab"); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-truncation" (void)strncat(one, "cdef", 2); +#pragma GCC diagnostic pop equal(one, "abcd"); /* Count-limited. */ (void)strncat(one, "gh", 0); equal(one, "abcd"); /* Zero count. */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-overflow" (void)strncat(one, "gh", 2); +#pragma GCC diagnostic pop equal(one, "abcdgh"); /* Count _AND length equal. */ - it = "strncmp"; + it = PSTR("strncmp"); /* strncmp - first test as strcmp with big counts";*/ check(strncmp("", "", 99) == 0); /* Trivial case. */ check(strncmp("a", "a", 99) == 0); /* Identity. */ @@ -164,16 +268,22 @@ void libm_test_string() check(strncmp("abc", "def", 0) == 0); /* Zero count. */ /* strncpy - testing is a bit different because of odd semantics. */ - it = "strncpy"; + it = PSTR("strncpy"); check(strncpy(one, "abc", 4) == one); /* Returned value. */ equal(one, "abc"); /* Did the copy go right? */ (void)strcpy(one, "abcdefgh"); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-truncation" (void)strncpy(one, "xyz", 2); +#pragma GCC diagnostic pop equal(one, "xycdefgh"); /* Copy cut by count. */ (void)strcpy(one, "abcdefgh"); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-truncation" (void)strncpy(one, "xyz", 3); /* Copy cut just before NUL. */ +#pragma GCC diagnostic pop equal(one, "xyzdefgh"); (void)strcpy(one, "abcdefgh"); @@ -188,7 +298,10 @@ void libm_test_string() equal(one + 5, "fgh"); (void)strcpy(one, "abc"); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-truncation" (void)strncpy(one, "xyz", 0); /* Zero-length copy. */ +#pragma GCC diagnostic pop equal(one, "abc"); (void)strncpy(one, "", 2); /* Zero-length source. */ @@ -197,18 +310,18 @@ void libm_test_string() equal(one + 2, "c"); (void)strcpy(one, "hi there"); - (void)strncpy(two, one, 9); + (void)xstrncpy(two, one, 9); equal(two, "hi there"); /* Just paranoia. */ equal(one, "hi there"); /* Stomped on source? */ /* strlen. */ - it = "strlen"; + it = PSTR("strlen"); check(strlen("") == 0); /* Empty. */ check(strlen("a") == 1); /* Single char. */ check(strlen("abcd") == 4); /* Multiple chars. */ /* strchr. */ - it = "strchr"; + it = PSTR("strchr"); check(strchr("abcd", 'z') == NULL); /* Not found. */ (void)strcpy(one, "abcd"); check(strchr(one, 'c') == one + 2); /* Basic test. */ @@ -222,7 +335,7 @@ void libm_test_string() check(strchr(one, '\0') == one); /* NUL in empty string. */ /* index - just like strchr. */ - it = "index"; + it = PSTR("index"); check(index("abcd", 'z') == NULL); /* Not found. */ (void)strcpy(one, "abcd"); check(index(one, 'c') == one + 2); /* Basic test. */ @@ -236,7 +349,7 @@ void libm_test_string() check(index(one, '\0') == one); /* NUL in empty string. */ /* strrchr. */ - it = "strrchr"; + it = PSTR("strrchr"); check(strrchr("abcd", 'z') == NULL); /* Not found. */ (void)strcpy(one, "abcd"); check(strrchr(one, 'c') == one + 2); /* Basic test. */ @@ -250,7 +363,7 @@ void libm_test_string() check(strrchr(one, '\0') == one); /* NUL in empty string. */ /* rindex - just like strrchr. */ - it = "rindex"; + it = PSTR("rindex"); check(rindex("abcd", 'z') == NULL); /* Not found. */ (void)strcpy(one, "abcd"); check(rindex(one, 'c') == one + 2); /* Basic test. */ @@ -264,7 +377,7 @@ void libm_test_string() check(rindex(one, '\0') == one); /* NUL in empty string. */ /* strpbrk - somewhat like strchr. */ - it = "strpbrk"; + it = PSTR("strpbrk"); check(strpbrk("abcd", "z") == NULL); /* Not found. */ (void)strcpy(one, "abcd"); check(strpbrk(one, "c") == one + 2); /* Basic test. */ @@ -281,7 +394,7 @@ void libm_test_string() check(strpbrk(one, "") == NULL); /* Both strings empty. */ /* strstr - somewhat like strchr. */ - it = "strstr"; + it = PSTR("strstr"); check(strstr("z", "abcd") == NULL); /* Not found. */ check(strstr("abx", "abcd") == NULL); /* Dead end. */ (void)strcpy(one, "abcd"); @@ -304,7 +417,7 @@ void libm_test_string() check(strstr(one, "bbca") == one + 1); /* With overlap. */ /* strspn. */ - it = "strspn"; + it = PSTR("strspn"); check(strspn("abcba", "abc") == 5); /* Whole string. */ check(strspn("abcba", "ab") == 2); /* Partial. */ check(strspn("abc", "qx") == 0); /* None. */ @@ -312,7 +425,7 @@ void libm_test_string() check(strspn("abc", "") == 0); /* Null search list. */ /* strcspn. */ - it = "strcspn"; + it = PSTR("strcspn"); check(strcspn("abcba", "qx") == 5); /* Whole string. */ check(strcspn("abcba", "cx") == 2); /* Partial. */ check(strcspn("abc", "abc") == 0); /* None. */ @@ -320,7 +433,7 @@ void libm_test_string() check(strcspn("abc", "") == 3); /* Null search list. */ /* strtok - the hard one. */ - it = "strtok"; + it = PSTR("strtok"); (void)strcpy(one, "first, second, third"); equal(strtok(one, ", "), "first"); /* Basic test. */ equal(one, "first"); @@ -367,7 +480,7 @@ void libm_test_string() equal(one + 4, "c"); /* memcmp. */ - it = "memcmp"; + it = PSTR("memcmp"); check(memcmp("a", "a", 1) == 0); /* Identity. */ check(memcmp("abc", "abc", 3) == 0); /* Multicharacter. */ check(memcmp("abcd", "abce", 4) < 0); /* Honestly unequal. */ @@ -379,25 +492,25 @@ void libm_test_string() /* memcmp should test strings as unsigned */ one[0] = 0xfe; two[0] = 0x03; - check(memcmp(one, two, 1) > 0); + check(xmemcmp(one, two, 1) > 0); /* memchr. */ - it = "memchr"; + it = PSTR("memchr"); check(memchr("abcd", 'z', 4) == NULL); /* Not found. */ (void)strcpy(one, "abcd"); - check(memchr(one, 'c', 4) == one + 2); /* Basic test. */ - check(memchr(one, 'd', 4) == one + 3); /* End of string. */ - check(memchr(one, 'a', 4) == one); /* Beginning. */ - check(memchr(one, '\0', 5) == one + 4); /* Finding NUL. */ + check(xmemchr(one, 'c', 4) == one + 2); /* Basic test. */ + check(xmemchr(one, 'd', 4) == one + 3); /* End of string. */ + check(xmemchr(one, 'a', 4) == one); /* Beginning. */ + check(xmemchr(one, '\0', 5) == one + 4); /* Finding NUL. */ (void)strcpy(one, "ababa"); - check(memchr(one, 'b', 5) == one + 1); /* Finding first. */ - check(memchr(one, 'b', 0) == NULL); /* Zero count. */ - check(memchr(one, 'a', 1) == one); /* Singleton case. */ + check(xmemchr(one, 'b', 5) == one + 1); /* Finding first. */ + check(xmemchr(one, 'b', 0) == NULL); /* Zero count. */ + check(xmemchr(one, 'a', 1) == one); /* Singleton case. */ (void)strcpy(one, "a\203b"); - check(memchr(one, 0203, 3) == one + 1); /* Unsignedness. */ + check(xmemchr(one, 0203, 3) == one + 1); /* Unsignedness. */ /* memcpy - need not work for overlap. */ - it = "memcpy"; + it = PSTR("memcpy"); check(memcpy(one, "abc", 4) == one); /* Returned value. */ equal(one, "abc"); /* Did the copy go right? */ @@ -411,13 +524,13 @@ void libm_test_string() (void)strcpy(one, "hi there"); (void)strcpy(two, "foo"); - (void)memcpy(two, one, 9); + (void)xmemcpy(two, one, 9); equal(two, "hi there"); /* Just paranoia. */ equal(one, "hi there"); /* Stomped on source? */ -#if 0 +#if 1 /* memmove - must work on overlap. */ - it = "memmove"; - check(memmove(one, "abc", 4) == one); /* Returned value. */ + it = PSTR("memmove"); + check(xmemmove(one, "abc", 4) == one); /* Returned value. */ equal(one, "abc"); /* Did the copy go right? */ (void) strcpy(one, "abcdefgh"); @@ -430,20 +543,20 @@ void libm_test_string() (void) strcpy(one, "hi there"); (void) strcpy(two, "foo"); - (void) memmove(two, one, 9); + (void) xmemmove(two, one, 9); equal(two, "hi there"); /* Just paranoia. */ equal(one, "hi there"); /* Stomped on source? */ (void) strcpy(one, "abcdefgh"); - (void) memmove(one+1, one, 9); + (void) xmemmove(one+1, one, 9); equal(one, "aabcdefgh"); /* Overlap, right-to-left. */ (void) strcpy(one, "abcdefgh"); - (void) memmove(one+1, one+2, 7); + (void) xmemmove(one+1, one+2, 7); equal(one, "acdefgh"); /* Overlap, left-to-right. */ (void) strcpy(one, "abcdefgh"); - (void) memmove(one, one, 9); + (void) xmemmove(one, one, 9); equal(one, "abcdefgh"); /* 100% overlap. */ #endif #if 0 @@ -451,7 +564,7 @@ void libm_test_string() The SVID, the only place where memccpy is mentioned, says overlap might fail, so we don't try it. Besides, it's hard to see the rationale for a non-left-to-right memccpy. */ - it = "memccpy"; + it = PSTR("memccpy"); check(memccpy(one, "abc", 'q', 4) == NULL); /* Returned value. */ equal(one, "abc"); /* Did the copy go right? */ @@ -486,12 +599,15 @@ void libm_test_string() equal(two, "xbcdlebee"); #endif /* memset. */ - it = "memset"; + it = PSTR("memset"); (void)strcpy(one, "abcdefgh"); check(memset(one + 1, 'x', 3) == one + 1); /* Return value. */ equal(one, "axxxefgh"); /* Basic test. */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmemset-transposed-args" (void)memset(one + 2, 'y', 0); +#pragma GCC diagnostic pop equal(one, "axxxefgh"); /* Zero-length set. */ (void)memset(one + 5, 0, 1); @@ -503,7 +619,7 @@ void libm_test_string() /* bcopy - much like memcpy. Berklix manual is silent about overlap, so don't test it. */ - it = "bcopy"; + it = PSTR("bcopy"); (void)bcopy("abc", one, 4); equal(one, "abc"); /* Simple copy. */ @@ -522,7 +638,7 @@ void libm_test_string() equal(one, "hi there"); /* Stomped on source? */ /* bzero. */ - it = "bzero"; + it = PSTR("bzero"); (void)strcpy(one, "abcdef"); bzero(one + 2, 2); equal(one, "ab"); /* Basic test. */ @@ -534,7 +650,7 @@ void libm_test_string() equal(one, "abcdef"); /* Zero-length copy. */ /* bcmp - somewhat like memcmp. */ - it = "bcmp"; + it = PSTR("bcmp"); check(bcmp("a", "a", 1) == 0); /* Identity. */ check(bcmp("abc", "abc", 3) == 0); /* Multicharacter. */ check(bcmp("abcd", "abce", 4) != 0); /* Honestly unequal. */ diff --git a/tests/device/test_libc/memmove1.c b/tests/device/test_libc/memmove1.c index 8a6403ee94..8e357002d9 100644 --- a/tests/device/test_libc/memmove1.c +++ b/tests/device/test_libc/memmove1.c @@ -62,6 +62,7 @@ int errors = 0; printf /* A safe target-independent memmove. */ +void mymemmove(unsigned char* dest, unsigned char* src, size_t n) __attribute__((__noinline__)); void mymemmove(unsigned char* dest, unsigned char* src, size_t n) { diff --git a/tests/device/test_libc/strcmp-1.c b/tests/device/test_libc/strcmp-1.c index e8663b9ba9..b76324a92e 100644 --- a/tests/device/test_libc/strcmp-1.c +++ b/tests/device/test_libc/strcmp-1.c @@ -31,18 +31,8 @@ #include #include -#define memcmp memcmp_P #define memcpy memcpy_P -#define memmem memmem_P -#define memchr memchr_P -#define strcat strcat_P -#define strncat strncat_P -#define strcpy strcpy_P -#define strncpy strncpy_P -#define strlen strlen_P -#define strnlen strnlen_P #define strcmp strcmp_P -#define strncmp strncmp_P #define BUFF_SIZE 256 diff --git a/tests/device/test_libc/test_libc.ino.globals.h b/tests/device/test_libc/test_libc.ino.globals.h new file mode 100644 index 0000000000..1bfe3ba4d5 --- /dev/null +++ b/tests/device/test_libc/test_libc.ino.globals.h @@ -0,0 +1,4 @@ +/*@create-file:build.opt@ + +-fno-builtin +*/ diff --git a/tests/device/test_libc/tstring.c b/tests/device/test_libc/tstring.c index 7d508d4c6e..43adfc3bed 100644 --- a/tests/device/test_libc/tstring.c +++ b/tests/device/test_libc/tstring.c @@ -13,6 +13,7 @@ #define MAX_1 50 #define memcmp memcmp_P #define memcpy memcpy_P +#define memmove memmove_P #define memmem memmem_P #define memchr memchr_P #define strcat strcat_P @@ -86,6 +87,8 @@ void tstring_main(void) tmp2[0] = 'Z'; tmp2[1] = '\0'; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmemset-transposed-args" if (memset(target, 'X', 0) != target || memcpy(target, "Y", 0) != target || memmove(target, "K", 0) != target || strncpy(tmp2, "4", 0) != tmp2 || strncat(tmp2, "123", 0) != tmp2 || strcat(target, "") != target) @@ -93,6 +96,7 @@ void tstring_main(void) eprintf(__LINE__, target, "A", 0); test_failed = 1; } +#pragma GCC diagnostic pop if (strcmp(target, "A") || strlen(target) != 1 || memchr(target, 'A', 0) != NULL || memcmp(target, "J", 0) || strncmp(target, "A", 1) || strncmp(target, "J", 0) diff --git a/tests/device/test_sw_FS/test_sw_FS.ino b/tests/device/test_sw_FS/test_sw_FS.ino index 0a33dc8586..41d7d188fc 100644 --- a/tests/device/test_sw_FS/test_sw_FS.ino +++ b/tests/device/test_sw_FS/test_sw_FS.ino @@ -1,5 +1,5 @@ #include -#include "FS.h" +#include #include BS_ENV_DECLARE(); @@ -18,17 +18,17 @@ bool pretest() TEST_CASE("read-write test","[fs]") { - REQUIRE(SPIFFS.begin()); + REQUIRE(LittleFS.begin()); String text = "write test"; { - File out = SPIFFS.open("/tmp.txt", "w"); + File out = LittleFS.open("/tmp.txt", "w"); REQUIRE(out); out.print(text); } { - File in = SPIFFS.open("/tmp.txt", "r"); + File in = LittleFS.open("/tmp.txt", "r"); REQUIRE(in); CHECK(in.size() == text.length()); in.setTimeout(0); @@ -39,14 +39,14 @@ TEST_CASE("read-write test","[fs]") TEST_CASE("A bunch of files show up in openDir, and can be removed", "[fs]") { - REQUIRE(SPIFFS.begin()); + REQUIRE(LittleFS.begin()); const int n = 10; int found[n] = {0}; { - Dir root = SPIFFS.openDir(""); + Dir root = LittleFS.openDir(""); while (root.next()) { - CHECK(SPIFFS.remove(root.fileName())); + CHECK(LittleFS.remove(root.fileName())); } } @@ -55,14 +55,14 @@ TEST_CASE("A bunch of files show up in openDir, and can be removed", "[fs]") name += i; name += ".txt"; - File out = SPIFFS.open(name, "w"); + File out = LittleFS.open(name, "w"); REQUIRE(out); out.println(i); } { - Dir root = SPIFFS.openDir("/"); + Dir root = LittleFS.openDir("/"); while (root.next()) { String fileName = root.fileName(); CHECK(fileName.indexOf("/seq_") == 0); @@ -77,35 +77,35 @@ TEST_CASE("A bunch of files show up in openDir, and can be removed", "[fs]") } { - Dir root = SPIFFS.openDir("/"); + Dir root = LittleFS.openDir("/"); while (root.next()) { String fileName = root.fileName(); - CHECK(SPIFFS.remove(fileName)); + CHECK(LittleFS.remove(fileName)); } } } TEST_CASE("files can be renamed", "[fs]") { - REQUIRE(SPIFFS.begin()); + REQUIRE(LittleFS.begin()); { - File tmp = SPIFFS.open("/tmp1.txt", "w"); + File tmp = LittleFS.open("/tmp1.txt", "w"); tmp.println("rename test"); } { - CHECK(SPIFFS.rename("/tmp1.txt", "/tmp2.txt")); + CHECK(LittleFS.rename("/tmp1.txt", "/tmp2.txt")); - File tmp2 = SPIFFS.open("/tmp2.txt", "r"); + File tmp2 = LittleFS.open("/tmp2.txt", "r"); CHECK(tmp2); } } TEST_CASE("FS::info works","[fs]") { - REQUIRE(SPIFFS.begin()); + REQUIRE(LittleFS.begin()); FSInfo info; - CHECK(SPIFFS.info(info)); + CHECK(LittleFS.info(info)); Serial.printf("Total: %u\nUsed: %u\nBlock: %u\nPage: %u\nMax open files: %u\nMax path len: %u\n", info.totalBytes, @@ -119,10 +119,10 @@ TEST_CASE("FS::info works","[fs]") TEST_CASE("FS is empty after format","[fs]") { - REQUIRE(SPIFFS.begin()); - REQUIRE(SPIFFS.format()); + REQUIRE(LittleFS.begin()); + REQUIRE(LittleFS.format()); - Dir root = SPIFFS.openDir("/"); + Dir root = LittleFS.openDir("/"); int count = 0; while (root.next()) { ++count; @@ -132,12 +132,12 @@ TEST_CASE("FS is empty after format","[fs]") TEST_CASE("Can reopen empty file","[fs]") { - REQUIRE(SPIFFS.begin()); + REQUIRE(LittleFS.begin()); { - File tmp = SPIFFS.open("/tmp.txt", "w"); + File tmp = LittleFS.open("/tmp.txt", "w"); } { - File tmp = SPIFFS.open("/tmp.txt", "w"); + File tmp = LittleFS.open("/tmp.txt", "w"); CHECK(tmp); } } diff --git a/tests/host/Makefile b/tests/host/Makefile index f819047891..97a4c671fb 100644 --- a/tests/host/Makefile +++ b/tests/host/Makefile @@ -30,6 +30,7 @@ GENHTML ?= genhtml CXXFLAGS += -std=gnu++17 CFLAGS += -std=gnu17 +# 32-bit mode is prefered, but not required ifeq ($(FORCE32),1) SIZEOFLONG = $(shell echo 'int main(){return sizeof(long);}'|$(CXX) -m32 -x c++ - -o sizeoflong 2>/dev/null && ./sizeoflong; echo $$?; rm -f sizeoflong;) ifneq ($(SIZEOFLONG),4) @@ -50,6 +51,7 @@ endif OUTPUT_BINARY := $(BINDIR)/host_tests LCOV_DIRECTORY := $(BINDIR)/../lcov +# Hide full build commands by default ifeq ($(V), 0) VERBC = @echo "C $@"; VERBCXX = @echo "C++ $@"; @@ -66,6 +68,30 @@ endif $(shell mkdir -p $(BINDIR)) +# Core files sometimes override libc functions, check when necessary to hide them +# TODO proper configure script / other build system? +ifeq (,$(wildcard $(BINDIR)/.have_strlcpy)) +$(shell printf '#include \nint main(){char a[4]{}; char b[4]{}; strlcpy(&a[0], &b[0], sizeof(a)); return 0;}\n' | \ + $(CXX) -x c++ - -o $(BINDIR)/.have_strlcpy 2>/dev/null || ( printf '#!/bin/sh\nexit 1\n' > $(BINDIR)/.have_strlcpy ; chmod +x $(BINDIR)/.have_strlcpy; )) +endif + +$(shell $(BINDIR)/.have_strlcpy) +ifneq ($(.SHELLSTATUS), 0) +FLAGS += -DSTRLCPY_MISSING +endif + +ifeq (,$(wildcard $(BINDIR)/.have_strlcat)) +$(shell printf '#include \nint main(){char a[4]{}; strlcat(&a[0], "test", sizeof(a)); return 0;}\n' | \ + $(CXX) -x c++ - -o $(BINDIR)/.have_strlcat 2>/dev/null || ( printf '#!/bin/sh\nexit 1\n' > $(BINDIR)/.have_strlcat ; chmod +x $(BINDIR)/.have_strlcat; )) +endif + +$(shell $(BINDIR)/.have_strlcat) +ifneq ($(.SHELLSTATUS), 0) +FLAGS += -DSTRLCAT_MISSING +endif + +# Actual build recipes + CORE_CPP_FILES := \ $(addprefix $(abspath $(CORE_PATH))/,\ debug.cpp \ diff --git a/tests/host/common/ArduinoCatch.hpp b/tests/host/common/ArduinoCatch.hpp index 8cb81afb7c..d30d30e4ec 100644 --- a/tests/host/common/ArduinoCatch.hpp +++ b/tests/host/common/ArduinoCatch.hpp @@ -24,13 +24,15 @@ std::ostream& operator<<(std::ostream&, const String&); -namespace Catch { +namespace Catch +{ std::string toString(const String&); template<> -struct StringMaker { +struct StringMaker +{ static std::string convert(const String&); }; -} // namespace Catch +} // namespace Catch diff --git a/tests/host/common/mock.h b/tests/host/common/mock.h index b3308282f0..344ce6b10c 100644 --- a/tests/host/common/mock.h +++ b/tests/host/common/mock.h @@ -56,18 +56,23 @@ #define D8 8 #include +#include +#include + +#include #ifdef __cplusplus extern "C" { #endif - // TODO: #include ? - char* itoa(int val, char* s, int radix); - char* ltoa(long val, char* s, int radix); - + char* utoa(unsigned value, char* result, int base); + char* itoa(int value, char* result, int base); +#ifdef STRLCAT_MISSING size_t strlcat(char* dst, const char* src, size_t size); +#endif +#ifdef STRLCPY_MISSING size_t strlcpy(char* dst, const char* src, size_t size); - +#endif #ifdef __cplusplus } #endif diff --git a/tests/host/common/noniso.c b/tests/host/common/noniso.c index 5c4e14b306..20fd3d1d5a 100644 --- a/tests/host/common/noniso.c +++ b/tests/host/common/noniso.c @@ -18,9 +18,10 @@ #include #include #include -#include "stdlib_noniso.h" -void reverse(char* begin, char* end) +#include + +static void reverse(char* begin, char* end) { char* is = begin; char* ie = end - 1; @@ -84,20 +85,3 @@ char* itoa(int value, char* result, int base) utoa(uvalue, result, base); return out; } - -int atoi(const char* s) -{ - return (int)atol(s); -} - -long atol(const char* s) -{ - char* tmp; - return strtol(s, &tmp, 10); -} - -double atof(const char* s) -{ - char* tmp; - return strtod(s, &tmp); -} diff --git a/tests/host/common/queue.h b/tests/host/common/queue.h index 0bc4ee7bd5..4919ae9fc4 100644 --- a/tests/host/common/queue.h +++ b/tests/host/common/queue.h @@ -184,7 +184,7 @@ struct name \ { \ struct type* stqh_first; /* first element */ \ - struct type** stqh_last; /* addr of last next element */ \ + struct type** stqh_last; /* addr of last next element */ \ } #define STAILQ_HEAD_INITIALIZER(head) \ @@ -371,7 +371,7 @@ struct name \ { \ struct type* tqh_first; /* first element */ \ - struct type** tqh_last; /* addr of last next element */ \ + struct type** tqh_last; /* addr of last next element */ \ } #define TAILQ_HEAD_INITIALIZER(head) \ diff --git a/tests/host/common/strl.cpp b/tests/host/common/strl.cpp index 0b0725b046..b01f6652a8 100644 --- a/tests/host/common/strl.cpp +++ b/tests/host/common/strl.cpp @@ -1,84 +1,78 @@ // https://gist.github.com/Fonger/98cc95ac39fbe1a7e4d9 -#ifndef HAVE_STRLCAT -/* - '_cups_strlcat()' - Safely concatenate two strings. -*/ - -size_t /* O - Length of string */ -strlcat(char* dst, /* O - Destination string */ - const char* src, /* I - Source string */ - size_t size) /* I - Size of destination string buffer */ +#include +#include +#include + +extern "C" { - size_t srclen; /* Length of source string */ - size_t dstlen; /* Length of destination string */ +#ifdef STRLCAT_MISSING + // '_cups_strlcat()' - Safely concatenate two strings. - /* - Figure out how much room is left... - */ + size_t /* O - Length of string */ + strlcat(char* dst, /* O - Destination string */ + const char* src, /* I - Source string */ + size_t size) /* I - Size of destination string buffer */ + { + size_t srclen; /* Length of source string */ + size_t dstlen; /* Length of destination string */ - dstlen = strlen(dst); - size -= dstlen + 1; + // Figure out how much room is left... - if (!size) - { - return (dstlen); /* No room, return immediately... */ - } + dstlen = strlen(dst); + size -= dstlen + 1; - /* - Figure out how much room is needed... - */ + if (!size) + { + return (dstlen); /* No room, return immediately... */ + } - srclen = strlen(src); + // Figure out how much room is needed... - /* - Copy the appropriate amount... - */ + srclen = strlen(src); - if (srclen > size) - { - srclen = size; + // Copy the appropriate amount... + + if (srclen > size) + { + srclen = size; + } + + memcpy(dst + dstlen, src, srclen); + dst[dstlen + srclen] = '\0'; + + return (dstlen + srclen); } +#endif /* STRLCAT_MISSING */ - memcpy(dst + dstlen, src, srclen); - dst[dstlen + srclen] = '\0'; +#ifdef STRLCPY_MISSING + // '_cups_strlcpy()' - Safely copy two strings. - return (dstlen + srclen); -} -#endif /* !HAVE_STRLCAT */ + size_t /* O - Length of string */ + strlcpy(char* dst, /* O - Destination string */ + const char* src, /* I - Source string */ + size_t size) /* I - Size of destination string buffer */ + { + size_t srclen; /* Length of source string */ -#ifndef HAVE_STRLCPY -/* - '_cups_strlcpy()' - Safely copy two strings. -*/ + // Figure out how much room is needed... -size_t /* O - Length of string */ -strlcpy(char* dst, /* O - Destination string */ - const char* src, /* I - Source string */ - size_t size) /* I - Size of destination string buffer */ -{ - size_t srclen; /* Length of source string */ + size--; - /* - Figure out how much room is needed... - */ + srclen = strlen(src); - size--; + // Copy the appropriate amount... - srclen = strlen(src); + if (srclen > size) + { + srclen = size; + } - /* - Copy the appropriate amount... - */ + memcpy(dst, src, srclen); + dst[srclen] = '\0'; - if (srclen > size) - { - srclen = size; + return (srclen); } +#endif /* STRLCPY_MISSING */ - memcpy(dst, src, srclen); - dst[srclen] = '\0'; - - return (srclen); -} -#endif /* !HAVE_STRLCPY */ +} // extern "C" diff --git a/tests/host/sys/pgmspace.h b/tests/host/sys/pgmspace.h index ef878eaaff..ac60cb15be 100644 --- a/tests/host/sys/pgmspace.h +++ b/tests/host/sys/pgmspace.h @@ -82,5 +82,6 @@ inline int vsnprintf_P(char* str, size_t size, const char* format, va_list ap) #define strncmp_P strncmp #define strncasecmp_P strncasecmp #define strcat_P strcat +#define memcmp_P memcmp #endif diff --git a/tests/lib-skip-ino.sh b/tests/lib-skip-ino.sh new file mode 100644 index 0000000000..6bcfe15a6c --- /dev/null +++ b/tests/lib-skip-ino.sh @@ -0,0 +1,53 @@ +# return 0 if this sketch should not be built in CI (for other archs, not needed, etc.) +function skip_ino() +{ + local ino=$1 + + case "$ino" in + *"/#attic/"* | \ + *"/AvrAdcLogger/"* | \ + *"/RtcTimestampTest/"* | \ + *"/SoftwareSpi/"* | \ + *"/TeensyDmaAdcLogger/"* | \ + *"/TeensyRtcTimestamp/"* | \ + *"/TeensySdioDemo/"* | \ + *"/TeensySdioLogger/"* | \ + *"/UnicodeFilenames/"* | \ + *"/UserChipSelectFunction/"* | \ + *"/UserSPIDriver/"* | \ + *"/debug/"* | \ + *"/examplesV1/"* | \ + *"/onewiretest/"*) + return 0 + ;; + *"Teensy"*) + return 0 + ;; + *) + ;; + esac + + return 1 +} + +# return reason if this sketch is not the main one or it is explicitly disabled with .test.skip in its directory +function skip_sketch() +{ + local sketch=$1 + + local sketchdir + sketchdir=$(dirname $sketch) + + local sketchdirname + sketchdirname=$(basename $sketchdir) + + local sketchname + sketchname=$(basename $sketch) + + if [[ "${sketchdirname}.ino" != "$sketchname" ]]; then + echo "Skipping $sketch (not the main sketch file)" + fi + if skip_ino "$sketch" || [[ -f "$sketchdir/.test.skip" ]]; then + echo "Skipping $sketch" + fi +} diff --git a/tests/restyle.py b/tests/restyle.py new file mode 100755 index 0000000000..4be9cab555 --- /dev/null +++ b/tests/restyle.py @@ -0,0 +1,291 @@ +#!/usr/bin/env python + +import argparse +import os +import sys +import pathlib +import subprocess +import contextlib + +from dataclasses import dataclass + + +GIT_ROOT = pathlib.Path( + subprocess.check_output( + ["git", "rev-parse", "--show-toplevel"], universal_newlines=True + ).strip() +) + + +def clang_format(clang_format, config, files): + if not files: + raise ValueError("Files list cannot be empty") + + cmd = [clang_format, "--verbose", f"--style=file:{config.as_posix()}", "-i"] + cmd.extend(files) + + subprocess.run(cmd, check=True) + + +def ls_files(patterns): + """Git-only search, but rather poor at matching complex patterns (at least w/ <=py3.12)""" + proc = subprocess.run( + ["git", "--no-pager", "ls-files"], + capture_output=True, + check=True, + universal_newlines=True, + ) + + out = [] + for line in proc.stdout.split("\n"): + path = pathlib.Path(line.strip()) + if any(path.match(pattern) for pattern in patterns): + out.append(path) + + return out + + +def diff_lines(): + proc = subprocess.run( + ["git", "--no-pager", "diff", "--ignore-submodules"], + capture_output=True, + check=True, + universal_newlines=True, + ) + + return proc.stdout.split("\n") + + +def find_files(patterns): + """Filesystem search, matches both git and non-git files""" + return [ + file + for pattern in patterns + for file in [found for found in GIT_ROOT.rglob(pattern)] + ] + + +def find_core_files(): + """Returns a subset of Core files that should be formatted""" + return [ + file + for file in find_files( + ( + "cores/esp8266/Lwip*", + "libraries/ESP8266mDNS/**/*", + "libraries/Wire/**/*", + "libraries/lwIP*/**/*", + "cores/esp8266/debug*", + "cores/esp8266/core_esp8266_si2c*", + "cores/esp8266/StreamString*", + "cores/esp8266/StreamSend*", + "libraries/Netdump/**/*", + "tests/**/*", + ) + ) + if file.is_file() + and file.suffix in (".c", ".cpp", ".h", ".hpp") + and not GIT_ROOT / "tests/device/test_libc" in file.parents + and not GIT_ROOT / "tests/host/bin" in file.parents + and not GIT_ROOT / "tests/host/common/catch.hpp" == file + ] + + +def find_arduino_files(): + """Returns every .ino file available in the repository, excluding submodule ones""" + return [ + ino + for library in find_files(("libraries/*",)) + if library.is_dir() and not (library / ".git").exists() + for ino in library.rglob("**/*.ino") + ] + + +FILES_PRESETS = { + "core": find_core_files, + "arduino": find_arduino_files, +} + + +@dataclass +class Changed: + file: str + hunk: str + lines: list[int] + + +class Context: + def __init__(self): + self.append_hunk = False + self.deleted = False + self.file = "" + self.hunk = [] + self.markers = [] + + def reset(self): + self.__init__() + + def reset_with_line(self, line): + self.reset() + self.hunk.append(line) + + def pop(self, out, line): + if self.file and self.hunk and self.markers: + out.append( + Changed(file=self.file, hunk="\n".join(self.hunk), lines=self.markers) + ) + + self.reset_with_line(line) + + +def changed_files_for_diff(lines: list[str] | str) -> list[Changed]: + """ + Naive git-diff output parser. Generates list of objects for every file changed after clang-format. + """ + match lines: + case str(): + lines = lines.split("\n") + case list(): + pass + case _: + raise ValueError("Unknown 'lines' type, can be either list[str] or str") + + ctx = Context() + out = [] + + # TODO: pygit2? + # ref. https://github.com/cpp-linter/cpp-linter/blob/main/cpp_linter/git/__init__.py ::parse_diff + # ref. https://github.com/libgit2/pygit2/blob/master/src/diff.c ::parse_diff + for line in lines: + # '--- a/path/to/changed/file' most likely + # '--- /dev/null' aka created file. should be ignored, same as removed ones + if line.startswith("---"): + ctx.pop(out, line) + + _, file = line.split(" ") + ctx.deleted = "/dev/null" in file + + # '+++ b/path/to/changed/file' most likely + # '+++ /dev/null' aka removed file + elif not ctx.deleted and line.startswith("+++"): + ctx.hunk.append(line) + + _, file = line.split(" ") + ctx.deleted = "/dev/null" in file + if not ctx.deleted: + ctx.file = file[2:] + + # @@ from-file-line-numbers to-file-line-numbers @@ + elif not ctx.deleted and line.startswith("@@"): + ctx.hunk.append(line) + + _, _, numbers, _ = line.split(" ", 3) + if "," in numbers: + numbers, _ = numbers.split(",") # drop count + + numbers = numbers.replace("+", "") + numbers = numbers.replace("-", "") + + ctx.markers.append(int(numbers)) + ctx.append_hunk = True + + # capture diff for the summary + elif ctx.append_hunk and line.startswith(("+", "-", " ")): + ctx.hunk.append(line) + + ctx.pop(out, line) + + return out + + +def changed_files() -> list[Changed]: + return changed_files_for_diff(diff_lines()) + + +def errors_changed(changed: Changed): + all_lines = ", ".join(str(x) for x in changed.lines) + for line in changed.lines: + print( + f"::error file={changed.file},title=Run tests/restyle.sh and re-commit {changed.file},line={line}::File {changed.file} failed clang-format style check. (lines {all_lines})" + ) + + +SUMMARY_PATH = pathlib.Path(os.environ.get("GITHUB_STEP_SUMMARY", os.devnull)) +SUMMARY_OUTPUT = SUMMARY_PATH.open("a") + + +def summary_diff(changed: Changed): + with contextlib.redirect_stdout(SUMMARY_OUTPUT): + print(f"# {changed.file} (suggested change)") + print("```diff") + print(changed.hunk) + print("```") + + +def stdout_diff(): + subprocess.run(["git", "--no-pager", "diff", "--ignore-submodules"]) + + +def assert_unchanged(): + subprocess.run( + ["git", "diff", "--ignore-submodules", "--exit-code"], + check=True, + stdout=subprocess.DEVNULL, + ) + + +def run_format(args): + targets = [] + + for include in args.include: + targets.append( + (GIT_ROOT / f"tests/clang-format-{include}.yaml", FILES_PRESETS[include]()) + ) + + if not targets: + targets.append((args.config, args.files)) + + for target in targets: + clang_format(args.clang_format, *target) + + +def run_assert(args): + for changed in changed_files(): + if args.with_errors: + errors_changed(changed) + if args.with_summary: + summary_diff(changed) + + if args.with_diff: + stdout_diff() + + assert_unchanged() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + + cmd = parser.add_subparsers(required=True) + format_ = cmd.add_parser("format") + format_.set_defaults(func=run_format) + format_.add_argument("--clang-format", default="clang-format") + + fmt = format_.add_subparsers(required=True) + + preset = fmt.add_parser("preset") + preset.add_argument( + "--include", action="append", required=True, choices=tuple(FILES_PRESETS.keys()) + ) + + files = fmt.add_parser("files") + files.add_argument("--config", type=pathlib.Path, required=True) + files.add_argument("files", type=pathlib.Path, nargs="+") + + assert_ = cmd.add_parser("assert") + assert_.set_defaults(func=run_assert) + assert_.add_argument("--with-diff", action="store_true") + assert_.add_argument("--with-errors", action="store_true") + assert_.add_argument("--with-summary", action="store_true") + + args = parser.parse_args() + args.func(args) diff --git a/tests/restyle.sh b/tests/restyle.sh index ce8e906a76..3bc90e52f0 100755 --- a/tests/restyle.sh +++ b/tests/restyle.sh @@ -1,5 +1,5 @@ #!/bin/sh -# requires clang-format, git, python3 with pyyaml +# requires python3, git, and runnable clang-format (specified below) set -e -x @@ -8,54 +8,13 @@ test -d ${root}/cores/esp8266 test -d ${root}/libraries # allow `env CLANG_FORMAT=clang-format-N`, or some other version -# default to v15, latest stable version from ubuntu-latest Github Actions image -CLANG_FORMAT=${CLANG_FORMAT:-clang-format-15} - -######################################### -# 'all' variable should be "cores/esp8266 libraries" - -all=${1:-" -cores/esp8266/Lwip* -libraries/ESP8266mDNS -libraries/Wire -libraries/lwIP* -cores/esp8266/debug* -cores/esp8266/core_esp8266_si2c.cpp -cores/esp8266/StreamString.* -cores/esp8266/StreamSend.* -libraries/Netdump -tests -"} - -######################################### -# restyling core & libraries +CLANG_FORMAT=${CLANG_FORMAT:-clang-format} cd $root +python $root/tests/restyle.py format --clang-format=$CLANG_FORMAT preset --include core --include arduino -style=${root}/tests/clang-format-core.yaml -for target in $all; do - if [ -d "$target" ]; then - find $target \ - '(' -name "*.cpp" -o -name "*.c" -o -name "*.h" ')' \ - -exec $CLANG_FORMAT --verbose --style="file:$style" -i {} \; - else - $CLANG_FORMAT --verbose --style="file:$style" -i $target - fi -done - -######################################### -# restyling arduino examples - -# TODO should not be matched, these are formatted externally -# exclude=$(git submodule --quiet foreach git rev-parse --show-toplevel | grep libraries) - -if [ -z $1 ] ; then - style=${root}/tests/clang-format-arduino.yaml - find libraries \ - -path libraries/ESP8266SdFat -prune -o \ - -path libraries/Ethernet -prune -o \ - -path libraries/SoftwareSerial -prune -o \ - -name '*.ino' -exec $CLANG_FORMAT --verbose --style="file:$style" -i {} \; +if [ "$CI" = "true" ] ; then + python $root/tests/restyle.py assert --with-summary --with-errors +else + python $root/tests/restyle.py assert --with-diff fi - -######################################### diff --git a/tests/sanity_check.sh b/tests/sanity_check.sh index a4754a0ed4..f5f2a6f2e1 100755 --- a/tests/sanity_check.sh +++ b/tests/sanity_check.sh @@ -5,8 +5,9 @@ source "$root/tests/common.sh" pushd "$root"/tools python3 get.py -q - +python3 makecorever.py --git-root "$root" "$root/cores/esp8266/core_version.h" popd + pushd "$cache_dir" gcc="$root/tools/xtensa-lx106-elf/bin/xtensa-lx106-elf-gcc"\ diff --git a/tests/test_restyle.py b/tests/test_restyle.py new file mode 100644 index 0000000000..7264f1c09e --- /dev/null +++ b/tests/test_restyle.py @@ -0,0 +1,182 @@ +import unittest + +from restyle import changed_files_for_diff + +# small git-diff samples from https://queirozf.com/entries/git-diff-reference-and-examples + + +class BaseTest(unittest.TestCase): + def testNewLine(self): + diff = """ +diff --git a/file.txt b/file.txt +index 257cc56..3bd1f0e 100644 +--- a/file.txt ++++ b/file.txt +@@ -1 +1,2 @@ + foo ++bar +""" + changed = changed_files_for_diff(diff) + self.assertEqual(1, len(changed)) + self.assertEqual("file.txt", changed[0].file) + self.assertEqual(1, len(changed[0].lines)) + self.assertEqual(1, changed[0].lines[0]) + + expected = """ +--- a/file.txt ++++ b/file.txt +@@ -1 +1,2 @@ + foo ++bar +""".strip() + self.assertEqual(expected, changed[0].hunk.strip()) + + def testNewLines(self): + diff = """ +diff --git a/file.txt b/file.txt +index 257cc56..3bd1f0e 100644 +--- a/file2.txt ++++ b/file2.txt +@@ -1 +1,2 @@ + foo ++bar + baz +@@ -1 +10,2 @@ + 222 +-222 + 333 +@@ -1 +100,3 @@ + aaa ++bbb ++ccc + ddd +""" + changed = changed_files_for_diff(diff) + self.assertEqual(1, len(changed)) + self.assertEqual("file2.txt", changed[0].file) + + lines = changed[0].lines + self.assertEqual(3, len(lines)) + + first, second, third = lines + self.assertEqual(1, first) + self.assertEqual(10, second) + self.assertEqual(100, third) + + expected = """ +--- a/file2.txt ++++ b/file2.txt +@@ -1 +1,2 @@ + foo ++bar + baz +@@ -1 +10,2 @@ + 222 +-222 + 333 +@@ -1 +100,3 @@ + aaa ++bbb ++ccc + ddd +""".strip() + self.assertEqual(expected, changed[0].hunk.strip()) + + def testRemovedLineAndDeletedFile(self): + diff = """ +diff --git a/file.txt b/file.txt +index 3bd1f0e..257cc56 100644 +--- a/file.txt ++++ b/file.txt +@@ -1,2 +1 @@ + foo +-bar +diff --git a/file2.txt b/file2.txt +deleted file mode 100644 +index 85553e8..0000000 +--- a/file2.txt ++++ /dev/null +@@ -1,2 +0,0 @@ +-aaaaaa +-bbbbbb +""" + changed = changed_files_for_diff(diff) + self.assertEqual(1, len(changed)) + self.assertEqual("file.txt", changed[0].file) + self.assertEqual(1, len(changed[0].lines)) + self.assertEqual(1, changed[0].lines[0]) + + expected = """ +--- a/file.txt ++++ b/file.txt +@@ -1,2 +1 @@ + foo +-bar +""".strip() + self.assertEqual(expected, changed[0].hunk.strip()) + + def testNewLineAndDeletedFile(self): + diff = """ +diff --git a/file.txt b/file.txt +index 3bd1f0e..86e041d 100644 +--- a/file.txt ++++ b/file.txt +@@ -1,2 +1,3 @@ + foo + bar ++baz +diff --git a/file2.txt b/file2.txt +deleted file mode 100644 +index 85553e8..0000000 +--- a/file2.txt ++++ /dev/null +@@ -1,2 +0,0 @@ +-aaaaaa +-bbbbbb +""" + changed = changed_files_for_diff(diff) + self.assertEqual(1, len(changed)) + self.assertEqual("file.txt", changed[0].file) + self.assertEqual(1, len(changed[0].lines)) + self.assertEqual(1, changed[0].lines[0]) + + expected = """ +--- a/file.txt ++++ b/file.txt +@@ -1,2 +1,3 @@ + foo + bar ++baz +""".strip() + self.assertEqual(expected, changed[0].hunk.strip()) + + def testDeletedFile(self): + diff = """ +diff --git a/file2.txt b/file2.txt +deleted file mode 100644 +index 85553e8..0000000 +--- a/file2.txt ++++ /dev/null +@@ -1,2 +0,0 @@ +-aaaaaa +-bbbbbb +""" + changed = changed_files_for_diff(diff) + self.assertEqual(0, len(changed)) + + def testNewFile(self): + diff = """ +diff --git a/file3.txt b/file3.txt +new file mode 100644 +index 0000000..a309e46 +--- /dev/null ++++ b/file3.txt +@@ -0,0 +1 @@ ++this is file3 +""" + changed = changed_files_for_diff(diff) + self.assertEqual(0, len(changed)) + + +if __name__ == '__main__': + unittest.main() diff --git a/tools/boards.txt.py b/tools/boards.txt.py index 681cc0b7b6..e94149a08b 100755 --- a/tools/boards.txt.py +++ b/tools/boards.txt.py @@ -112,25 +112,30 @@ '+-----------------+------------+------------------+', '| GND | | GND |', '+-----------------+------------+------------------+', - '| TX or GPIO2\* | | RX |', + '| TX or GPIO2 | | |', + '| [#tx_or_gpio2]_ | RX | |', '+-----------------+------------+------------------+', '| RX | | TX |', '+-----------------+------------+------------------+', '| GPIO0 | PullUp | DTR |', '+-----------------+------------+------------------+', - '| Reset\* | PullUp | RTS |', + '| Reset | | |', + '| [#reset]_ | PullUp | RTS |', '+-----------------+------------+------------------+', - '| GPIO15\* | PullDown | |', + '| GPIO15 | | |', + '| [#gpio15]_ | PullDown | |', '+-----------------+------------+------------------+', - '| CH\_PD | PullUp | |', + '| CH\\_PD | | |', + '| [#ch_pd]_ | PullUp | |', '+-----------------+------------+------------------+', '', - '- Note', - '- GPIO15 is also named MTDO', - '- Reset is also named RSBT or REST (adding PullUp improves the', + '.. rubric:: Notes', + '', + '.. [#tx_or_gpio2] GPIO15 is also named MTDO', + '.. [#reset] Reset is also named RSBT or REST (adding PullUp improves the', ' stability of the module)', - '- GPIO2 is alternative TX for the boot loader mode', - '- **Directly connecting a pin to VCC or GND is not a substitute for a', + '.. [#gpio15] GPIO2 is alternative TX for the boot loader mode', + '.. [#ch_pd] **Directly connecting a pin to VCC or GND is not a substitute for a', ' PullUp or PullDown resistor, doing this can break upload management', ' and the serial console, instability has also been noted in some', ' cases.**', @@ -161,15 +166,16 @@ '+---------------+------------+------------------+', '| GPIO0 | | GND |', '+---------------+------------+------------------+', - '| Reset | | RTS\* |', + '| Reset | | RTS [#rts]_ |', '+---------------+------------+------------------+', '| GPIO15 | PullDown | |', '+---------------+------------+------------------+', - '| CH\_PD | PullUp | |', + '| CH\\_PD | PullUp | |', '+---------------+------------+------------------+', '', - '- Note', - '- if no RTS is used a manual power toggle is needed', + '.. rubric:: Notes', + '', + '.. [#rts] if no RTS is used a manual power toggle is needed', '', 'Minimal Hardware Setup for Running only', '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', @@ -187,7 +193,7 @@ '+----------+------------+----------------+', '| GPIO15 | PullDown | |', '+----------+------------+----------------+', - '| CH\_PD | PullUp | |', + '| CH\\_PD | PullUp | |', '+----------+------------+----------------+', '', 'Minimal', @@ -249,7 +255,11 @@ 'boot mode', '~~~~~~~~~', '', - 'the first value respects the pin setup of the Pins 0, 2 and 15.', + 'the first value respects the pin setup of the Pins 0, 2 and 15', + '', + '.. code-block::', + '', + ' Number = (GPIO15 << 2) | (GPIO0 << 1) | GPIO2', '', '+----------+----------+---------+---------+-------------+', '| Number | GPIO15 | GPIO0 | GPIO2 | Mode |', @@ -271,7 +281,6 @@ '| 7 | 3.3V | 3.3V | 3.3V | SDIO |', '+----------+----------+---------+---------+-------------+', '', - 'note: - number = ((GPIO15 << 2) \| (GPIO0 << 1) \| GPIO2);', ], }), ( 'esp8285', { @@ -436,6 +445,23 @@ ], 'desc': [ 'ESPresso Lite 2.0 is an Arduino-compatible Wi-Fi development board based on an earlier V1 (beta version). Re-designed together with Cytron Technologies, the newly-revised ESPresso Lite V2.0 features the auto-load/auto-program function, eliminating the previous need to reset the board manually before flashing a new program. It also feature two user programmable side buttons and a reset button. The special distinctive features of on-board pads for I2C sensor and actuator is retained.', ] }), + ( 'mercury', { + 'name': 'Mercury', + 'opts': { + '.build.board': 'mercury', + '.build.variant': 'mercury', + }, + 'macro': [ + 'resetmethod_nodemcu', + 'flashmode_dio', + 'flashfreq_40', + '4M', + ], + 'desc': [ 'ESP8266 based development board supercharged with onboard motor driver, RGB LED, support for servo motors and etc.', + 'Git: https://github.com/raliotech/products/tree/master', + 'Product page: https://www.raliotech.com', + ], + }), ( 'phoenix_v1', { 'name': 'Phoenix 1.0', 'opts': { @@ -756,14 +782,10 @@ ], 'desc': [ 'ESPino by ThaiEasyElec using WROOM-02 module from Espressif Systems with 4 MB Flash.', '', - 'We will update an English description soon. - Product page:', - 'http://thaieasyelec.com/products/wireless-modules/wifi-modules/espino-wifi-development-board-detail.html', - '- Schematics:', - 'www.thaieasyelec.com/downloads/ETEE052/ETEE052\_ESPino\_Schematic.pdf -', - 'Dimensions:', - 'http://thaieasyelec.com/downloads/ETEE052/ETEE052\_ESPino\_Dimension.pdf', - '- Pinouts:', - 'http://thaieasyelec.com/downloads/ETEE052/ETEE052\_ESPino\_User\_Manual\_TH\_v1\_0\_20160204.pdf (Please see pg. 8)', + '* Product page (retired product): https://www.thaieasyelec.com/product/%E0%B8%A2%E0%B8%81%E0%B9%80%E0%B8%A5%E0%B8%B4%E0%B8%81%E0%B8%88%E0%B8%B3%E0%B8%AB%E0%B8%99%E0%B9%88%E0%B8%B2%E0%B8%A2-retired-espino-wifi-development-board/11000833173001086', + '* Schematics: https://downloads.thaieasyelec.com/ETEE052/ETEE052\\_ESPino\\_Schematic.pdf', + '* Dimensions: https://downloads.thaieasyelec.com/ETEE052/ETEE052\\_ESPino\\_Dimension.pdf', + '* Pinouts (Please see pg.8): https://downloads.thaieasyelec.com/ETEE052/ETEE052\\_ESPino\\_User\\_Manual\\_TH\\_v1\\_0\\_20160204.pdf', ], }), ( 'wifinfo', { @@ -1626,13 +1648,13 @@ def all_flash_map (): print("generated: flash map config file (in cores/esp8266/FlashMap.h)") return { - 'autoflash': { - '.menu.eesz.autoflash': 'Mapping defined by Hardware and Sketch', - '.menu.eesz.autoflash.build.flash_size': '16M', - '.menu.eesz.autoflash.build.flash_ld': 'eagle.flash.auto.ld', - '.menu.eesz.autoflash.build.extra_flags': '-DFLASH_MAP_SUPPORT=1', - '.menu.eesz.autoflash.upload.maximum_size': '1044464', - }, + 'autoflash': collections.OrderedDict([ + ('.menu.eesz.autoflash', 'Mapping defined by Hardware and Sketch'), + ('.menu.eesz.autoflash.build.flash_size', '16M'), + ('.menu.eesz.autoflash.build.flash_ld', 'eagle.flash.auto.ld'), + ('.menu.eesz.autoflash.build.extra_flags', '-DFLASH_MAP_SUPPORT=1'), + ('.menu.eesz.autoflash.upload.maximum_size', '1044464') + ]), '512K': f512, '1M': f1m, '2M': f2m, @@ -1855,7 +1877,7 @@ def package (): substitution += ',\n'.join(board_items) substitution += '\n ],' - newfilestr = re.sub(r'"boards":[^\]]*\],', substitution, filestr, re.MULTILINE) + newfilestr = re.sub(r'"boards":[^\]]*\],', substitution, filestr, flags=re.MULTILINE) # To get consistent indent/formatting read the JSON and write it out programmatically if packagegen: diff --git a/tools/cert.py b/tools/cert.py index 7498d5ee62..f319ef0f30 100755 --- a/tools/cert.py +++ b/tools/cert.py @@ -41,8 +41,8 @@ def printData(data, showPub = True): name = re.sub('[^a-zA-Z0-9_]', '_', cn) print('// CN: {} => name: {}'.format(cn, name)) - print('// not valid before:', xcert.not_valid_before) - print('// not valid after: ', xcert.not_valid_after) + print('// not valid before:', xcert.not_valid_before_utc) + print('// not valid after: ', xcert.not_valid_after_utc) if showPub: @@ -114,7 +114,7 @@ def main(): print() print('// this file is autogenerated - any modification will be overwritten') print('// unused symbols will not be linked in the final binary') - print('// generated on {}'.format(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))) + print('// generated on {}'.format(datetime.datetime.now(datetime.UTC).strftime("%Y-%m-%d %H:%M:%S"))) print('// by {}'.format(sys.argv)) print() print('#pragma once') diff --git a/tools/decoder.py b/tools/decoder.py index 1ed3dcf8ed..b1f1706767 100755 --- a/tools/decoder.py +++ b/tools/decoder.py @@ -10,6 +10,7 @@ import sys import re import subprocess +import shutil # https://github.com/me-no-dev/EspExceptionDecoder/blob/349d17e4c9896306e2c00b4932be3ba510cad208/src/EspExceptionDecoder.java#L59-L90 EXCEPTION_CODES = ( @@ -52,6 +53,7 @@ "permit stores", ) + # similar to java version, which used `list` and re-formatted it # instead, simply use an already short-format `info line` # TODO `info symbol`? revert to `list`? @@ -96,14 +98,17 @@ def addresses_addr2line(addr2line, elf, addresses): def decode_lines(format_addresses, elf, lines): - STACK_RE = re.compile(r"^[0-9a-f]{8}:\s+([0-9a-f]{8} ?)+ *$") + ANY_ADDR_RE = re.compile(r"0x[0-9a-fA-F]{8}|[0-9a-fA-F]{8}") + HEX_ADDR_RE = re.compile(r"0x[0-9a-f]{8}") - LAST_ALLOC_RE = re.compile( - r"last failed alloc call: ([0-9a-fA-F]{8})\(([0-9]+)\).*" - ) - LAST_ALLOC = "last failed alloc" + MEM_ERR_LINE_RE = re.compile(r"^(Stack|last failed alloc call)") + + STACK_LINE_RE = re.compile(r"^[0-9a-f]{8}:\s\s+") + + IGNORE_FIRMWARE_RE = re.compile(r"^(epc1=0x........, |Fatal exception )") CUT_HERE_STRING = "CUT HERE FOR EXCEPTION DECODER" + DECODE_IT = "DECODE IT" EXCEPTION_STRING = "Exception (" EPC_STRING = "epc1=" @@ -131,13 +136,13 @@ def format_address(address): stack_addresses = print_all_addresses(stack_addresses) last_stack = line.strip() # 3fffffb0: feefeffe feefeffe 3ffe85d8 401004ed - elif in_stack and STACK_RE.match(line): - stack, addrs = line.split(":") - addrs = addrs.strip() - addrs = addrs.split(" ") + elif IGNORE_FIRMWARE_RE.match(line): + continue + elif in_stack and STACK_LINE_RE.match(line): + _, addrs = line.split(":") + addrs = ANY_ADDR_RE.findall(addrs) stack_addresses.setdefault(last_stack, []) - for addr in addrs: - stack_addresses[last_stack].append(addr) + stack_addresses[last_stack].extend(addrs) # epc1=0xfffefefe epc2=0xfefefefe epc3=0xefefefef excvaddr=0xfefefefe depc=0xfefefefe elif EPC_STRING in line: pairs = line.split() @@ -152,20 +157,22 @@ def format_address(address): elif EXCEPTION_STRING in line: number = line.strip()[len(EXCEPTION_STRING) : -2] print(f"Exception ({number}) - {EXCEPTION_CODES[int(number)]}") + # stack smashing detected at # last failed alloc call: ()[@] - elif LAST_ALLOC in line: - values = LAST_ALLOC_RE.match(line) - if values: - addr, size = values.groups() - print() - print(f"Allocation of {size} bytes failed: {format_address(addr)}") + elif MEM_ERR_LINE_RE.match(line): + for addr in ANY_ADDR_RE.findall(line): + line = line.replace(addr, format_address(addr)) + print() + print(line.strip()) # postmortem guards our actual stack dump values with these elif ">>>stack>>>" in line: in_stack = True # ignore elif "<<= (3, 12): + TARFILE_EXTRACT_ARGS = {"filter": "data"} else: - # Not Python 3 - today, it is most likely to be Python 2 - from urllib import urlretrieve + TARFILE_EXTRACT_ARGS = {} + +PLATFORMS = { + "Darwin": {32: "i386-apple-darwin", 64: "x86_64-apple-darwin"}, + "DarwinARM": {32: "arm64-apple-darwin", 64: "arm64-apple-darwin"}, + "Linux": {32: "i686-pc-linux-gnu", 64: "x86_64-pc-linux-gnu"}, + "LinuxARM": {32: "arm-linux-gnueabihf", 64: "aarch64-linux-gnu"}, + "Windows": {32: "i686-mingw32", 64: "x86_64-mingw32"}, +} -dist_dir = 'dist/' -def sha256sum(filename, blocksize=65536): - hash = hashlib.sha256() - with open(filename, "rb") as f: +class HashMismatch(Exception): + pass + + +def sha256sum(p: pathlib.Path, blocksize=65536): + hasher = hashlib.sha256() + with p.open("rb") as f: for block in iter(lambda: f.read(blocksize), b""): - hash.update(block) - return hash.hexdigest() + hasher.update(block) + + return hasher.hexdigest() -def mkdir_p(path): - try: - os.makedirs(path) - except OSError as exc: - if exc.errno != errno.EEXIST or not os.path.isdir(path): - raise def report_progress(count, blockSize, totalSize): - global verbose - if verbose: - percent = int(count*blockSize*100/totalSize) - percent = min(100, percent) - sys.stdout.write("\r%d%%" % percent) - sys.stdout.flush() - -def unpack(filename, destination): - dirname = '' - print('Extracting {0}'.format(filename)) - if filename.endswith('tar.gz'): - tfile = tarfile.open(filename, 'r:gz') - tfile.extractall(destination) - dirname= tfile.getnames()[0] - elif filename.endswith('zip'): - zfile = zipfile.ZipFile(filename) + percent = int(count * blockSize * 100 / totalSize) + percent = min(100, percent) + print(f"\r{percent}%", end="", file=sys.stdout, flush=True) + + +def unpack(p: pathlib.Path, destination: pathlib.Path): + outdir = None # type: Optional[pathlib.Path] + + print(f"Extracting {p}") + if p.suffix == ".zip": + zfile = zipfile.ZipFile(p) zfile.extractall(destination) - dirname = zfile.namelist()[0] + outdir = destination / zfile.namelist()[0] else: - raise NotImplementedError('Unsupported archive type') + tfile = tarfile.open(p, "r:*") + tfile.extractall(destination, **TARFILE_EXTRACT_ARGS) # type: ignore + outdir = destination / tfile.getnames()[0] + + if not outdir: + raise NotImplementedError(f"Unsupported archive type {p.suffix}") # a little trick to rename tool directories so they don't contain version number - rename_to = re.match(r'^([a-zA-Z_][^\-]*\-*)+', dirname).group(0).strip('-') - if rename_to != dirname: - print('Renaming {0} to {1}'.format(dirname, rename_to)) - if os.path.isdir(rename_to): - shutil.rmtree(rename_to) - shutil.move(dirname, rename_to) - -def get_tool(tool): - archive_name = tool['archiveFileName'] - local_path = dist_dir + archive_name - url = tool['url'] - real_hash = tool['checksum'].split(':')[1] - if not os.path.isfile(local_path): - print('Downloading ' + archive_name); - urlretrieve(url, local_path, report_progress) - sys.stdout.write("\rDone\n") - sys.stdout.flush() + match = re.match(r"^([a-zA-Z_][^\-]*\-*)+", outdir.name) + if match: + rename_to = match.group(0).strip("-") else: - print('Tool {0} already downloaded'.format(archive_name)) - local_hash = sha256sum(local_path) - if local_hash != real_hash: - print('Hash mismatch for {0}, delete the file and try again'.format(local_path)) - raise RuntimeError() - unpack(local_path, '.') - -def load_tools_list(filename, platform): - tools_info = json.load(open(filename))['packages'][0]['tools'] - tools_to_download = [] - for t in tools_info: - tool_platform = [p for p in t['systems'] if p['host'] == platform] - if len(tool_platform) == 0: - continue - tools_to_download.append(tool_platform[0]) - return tools_to_download - -def identify_platform(): - arduino_platform_names = {'Darwin' : {32 : 'i386-apple-darwin', 64 : 'x86_64-apple-darwin'}, - 'Linux' : {32 : 'i686-pc-linux-gnu', 64 : 'x86_64-pc-linux-gnu'}, - 'LinuxARM': {32 : 'arm-linux-gnueabihf', 64 : 'aarch64-linux-gnu'}, - 'Windows' : {32 : 'i686-mingw32', 64 : 'x86_64-mingw32'}} - bits = 32 - if sys.maxsize > 2**32: - bits = 64 - sys_name = platform.system() - if 'Linux' in sys_name and (platform.platform().find('arm') > 0 or platform.platform().find('aarch64') > 0): - sys_name = 'LinuxARM' - if 'CYGWIN_NT' in sys_name: - sys_name = 'Windows' - if 'MSYS_NT' in sys_name: - sys_name = 'Windows' - if 'MINGW' in sys_name: - sys_name = 'Windows' - return arduino_platform_names[sys_name][bits] - -def main(): - global verbose - # Support optional "-q" quiet mode simply - if len(sys.argv) == 2: - if sys.argv[1] == "-q": - verbose = False - # Remove a symlink generated in 2.6.3 which causes later issues since the tarball can't properly overwrite it - if (os.path.exists('python3/python3')): - os.unlink('python3/python3') - print('Platform: {0}'.format(identify_platform())) - tools_to_download = load_tools_list('../package/package_esp8266com_index.template.json', identify_platform()) - mkdir_p(dist_dir) + rename_to = outdir.name + + if outdir.name != rename_to: + print(f"Renaming {outdir.name} to {rename_to}") + destdir = destination / rename_to + if destdir.is_dir(): + shutil.rmtree(destdir) + shutil.move(outdir, destdir) + + +# ref. https://docs.arduino.cc/arduino-cli/package_index_json-specification/ +def get_tool(tool: dict, *, dist_dir: pathlib.Path, quiet: bool, dry_run: bool): + if not dist_dir.exists(): + dist_dir.mkdir(parents=True, exist_ok=True) + + archive_name = tool["archiveFileName"] + local_path = dist_dir / archive_name + + url = tool["url"] + algorithm, real_hash = tool["checksum"].split(":", 1) + if algorithm != "SHA-256": + raise NotImplementedError(f"Unsupported hash algorithm {algorithm}") + + if dry_run: + print(f'{archive_name} ({tool.get("size")} bytes): {url}') + else: + if not quiet: + reporthook = report_progress + else: + reporthook = None + + if not local_path.is_file(): + print(f"Downloading {archive_name}") + urlretrieve(url, local_path, reporthook) + print("\rDone", file=sys.stdout, flush=True) + else: + print( + f"Tool {archive_name} ({local_path.stat().st_size} bytes) already downloaded" + ) + + if not dry_run or (dry_run and local_path.exists()): + local_hash = sha256sum(local_path) + if local_hash != real_hash: + raise HashMismatch( + f"Expected {local_hash}, got {real_hash}. Delete {local_path} and try again" + ) from None + + if not dry_run: + unpack(local_path, PWD / ".") + + +def load_tools_list(package_index_json: pathlib.Path, hosts: List[str]): + out = [] + + with package_index_json.open("r") as f: + root = json.load(f) + + package = root["packages"][0] + tools = package["tools"] + + for info in tools: + found = [p for p in info["systems"] for host in hosts if p["host"] == host] + found.sort(key=lambda p: hosts.index(p["host"])) + if found: + out.append(found[0]) + + return out + + +def select_host( + sys_name: Optional[str], + sys_platform: Optional[str], + bits: Optional[Literal[32, 64]], +) -> List[str]: + if not sys_name: + sys_name = platform.system() + + if not sys_platform: + sys_platform = platform.platform() + + if not bits: + bits = 32 + if sys.maxsize > 2**32: + bits = 64 + + def maybe_arm(s: str) -> bool: + return (s.find("arm") > 0) or (s.find("aarch64") > 0) + + if "Darwin" in sys_name and maybe_arm(sys_platform): + sys_name = "DarwinARM" + elif "Linux" in sys_name and maybe_arm(sys_platform): + sys_name = "LinuxARM" + elif "CYGWIN_NT" in sys_name or "MSYS_NT" in sys_name or "MINGW" in sys_name: + sys_name = "Windows" + + out = [ + PLATFORMS[sys_name][bits], + ] + + if sys_name == "DarwinARM": + out.append(PLATFORMS["Darwin"][bits]) + + return out + + +def main(args: argparse.Namespace): + # #6960 - Remove a symlink generated in 2.6.3 which causes later issues since the tarball can't properly overwrite it + py3symlink = PWD / "python3" / "python3" + if py3symlink.is_symlink(): + py3symlink.unlink() + + host = args.host + if not host: + host = select_host( + sys_name=args.system, + sys_platform=args.platform, + bits=args.bits, + ) + + print(f"Platform: {', '.join(host)}") + + tools_to_download = load_tools_list(args.package_index_json, host) + if args.tool: + tools_to_download = [ + tool + for tool in tools_to_download + for exclude in args.tool + if exclude in tool["archiveFileName"] + ] + for tool in tools_to_download: - get_tool(tool) + get_tool( + tool, + dist_dir=args.dist_dir, + quiet=args.quiet, + dry_run=args.dry_run, + ) + + +def parse_args(args: Optional[str] = None, namespace=argparse.Namespace): + parser = argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter + ) + + parser.add_argument("-q", "--quiet", action="store_true", default=False) + parser.add_argument("-d", "--dry-run", action="store_true", default=False) + parser.add_argument("-t", "--tool", action="append", type=str) + + parser.add_argument("--host", type=str, action="append") + parser.add_argument("--system", type=str) + parser.add_argument("--platform", type=str) + parser.add_argument("--bits", type=int, choices=PLATFORMS["Linux"].keys()) + + parser.add_argument( + "--no-progress", dest="quiet", action="store_true", default=False + ) + parser.add_argument("--dist-dir", type=pathlib.Path, default=PWD / "dist") + parser.add_argument( + "--package-index-json", + type=pathlib.Path, + default=PWD / ".." / "package/package_esp8266com_index.template.json", + ) + + return parser.parse_args(args, namespace) + -if __name__ == '__main__': - main() +if __name__ == "__main__": + main(parse_args()) diff --git a/tools/makecorever.py b/tools/makecorever.py index d3f44f1742..939120fff3 100755 --- a/tools/makecorever.py +++ b/tools/makecorever.py @@ -18,25 +18,58 @@ # along with this program. If not, see . import argparse -import os +import pathlib import subprocess +import sys +from typing import Optional, TextIO + + +PWD = pathlib.Path(__file__).parent.absolute() + +VERSION_UNSPECIFIED = "unspecified" +VERSION_DEFAULT = ("0", "0", "0") -def generate(path, platform_path, version="unspecified", release = False): - def git(*args): - cmd = ["git", "-C", platform_path] - cmd.extend(args) - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, universal_newlines=True, stderr=subprocess.DEVNULL) - return proc.stdout.readlines()[0].strip() - text = "" +def check_git(*args: str, cwd: Optional[str]): + cmd = ["git"] + if cwd: + cmd.extend(["-C", cwd]) + cmd.extend(args) - git_ver = "00000000" try: - git_ver = git("rev-parse", "--short=8", "HEAD") - except Exception: + with subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + universal_newlines=True, + stderr=subprocess.DEVNULL, + ) as proc: + if proc.stdout: + lines = proc.stdout.readlines() + return lines[0].strip() + except IndexError: pass - text = "#define ARDUINO_ESP8266_GIT_VER 0x{}\n".format(git_ver) + except FileNotFoundError: + pass + + return "" + + +def generate( + *, + git_root: pathlib.Path, + hash_length: int = 8, + release: bool, + version: str, +): + git_root = git_root.absolute() + git_cwd = git_root.as_posix() + + def git(*args): + return check_git(*args, cwd=git_cwd) + + git_ver = "0" * hash_length + git_ver = git("rev-parse", f"--short={hash_length}", "HEAD") or git_ver # version is # - using Arduino-CLI: @@ -45,75 +78,97 @@ def git(*args): # - using git: # - 5.6.7 (from release script, official release) # - 5.6.7-42-g00d1e5 (from release script, test release) - git_desc = version - try: - # in any case, get a better version when git is around - git_desc = git("describe", "--tags") - except Exception: - pass - text += "#define ARDUINO_ESP8266_GIT_DESC {}\n".format(git_desc) - text += "#define ARDUINO_ESP8266_VERSION {}\n".format(version) - text += "\n" + # in any case, get a better version when git is around + git_desc = git("describe", "--tags") or version + + if version == VERSION_UNSPECIFIED: + version = git_desc + + version_triple = list(VERSION_DEFAULT) + + if version != VERSION_UNSPECIFIED: + try: + version_triple = version.split(".", 2) + except ValueError: + pass - version_split = version.split(".") - # major: if present, skip "unix-" in "unix-3" - text += "#define ARDUINO_ESP8266_MAJOR {}\n".format(version_split[0].split("-")[-1]) - text += "#define ARDUINO_ESP8266_MINOR {}\n".format(version_split[1]) - # revision can be ".n" or ".n-dev" or ".n-42-g00d1e5" - revision = version_split[2].split("-") - text += "#define ARDUINO_ESP8266_REVISION {}\n".format(revision[0]) - text += "\n" + major, minor, patch = version_triple - # release or dev ? + major = major.split("-")[-1] + revision = patch.split("-")[0] + + text = rf"""// ! ! ! DO NOT EDIT, AUTOMATICALLY GENERATED ! ! ! +#define ARDUINO_ESP8266_GIT_VER 0x{git_ver} +#define ARDUINO_ESP8266_GIT_DESC {git_desc} +#define ARDUINO_ESP8266_VERSION {version} + +#define ARDUINO_ESP8266_MAJOR {major} +#define ARDUINO_ESP8266_MINOR {minor} +#define ARDUINO_ESP8266_REVISION {revision} +""" if release: - text += "#define ARDUINO_ESP8266_RELEASE \"{}\"\n".format(git_desc) - text += "#define ARDUINO_ESP8266_RELEASE_{}\n".format(git_desc.replace("-","_").replace(".","_")) + text += rf""" +#define ARDUINO_ESP8266_RELEASE \"{major}.{minor}.{revision}\" +#define ARDUINO_ESP8266_RELEASE_{major}_{minor}_{revision} +""" else: - text += "#define ARDUINO_ESP8266_DEV 1 // development version\n" - - try: - with open(path, "r") as inp: - old_text = inp.read() - if old_text == text: - return - except Exception: - pass + text += """ +#define ARDUINO_ESP8266_DEV 1 // development version +""" - with open(path, "w") as out: - out.write(text) + return text if __name__ == "__main__": parser = argparse.ArgumentParser(description="Generate core_version.h") parser.add_argument( - "-b", "--build_path", action="store", required=True, help="build.path variable" + "--git-root", + action="store", + help="ESP8266 Core Git root. In platform.txt, this is {platform.path}", + type=pathlib.Path, + default=PWD / "..", + ) + parser.add_argument( + "--hash-length", + default=8, + type=int, + help="Used in git rev-parse --short=...", ) parser.add_argument( - "-p", - "--platform_path", + "--version", action="store", - required=True, - help="platform.path variable", + default=VERSION_UNSPECIFIED, + help="ESP8266 Core version string. In platform.txt, this is {version}", ) parser.add_argument( - "-v", "--version", action="store", required=True, help="version variable" + "--release", + action="store_true", + default=False, + help="In addition to numeric version, also provide ARDUINO_ESP8266_RELEASE{,_...} definitions", + ) + parser.add_argument( + "output", + nargs="?", + type=str, + default="", ) - parser.add_argument("-i", "--include_dir", default="core") - parser.add_argument("-r", "--release", action="store_true", default=False) args = parser.parse_args() - include_dir = os.path.join(args.build_path, args.include_dir) - try: - os.makedirs(include_dir) - except Exception: - pass - - generate( - os.path.join(include_dir, "core_version.h"), - args.platform_path, + contents = generate( + git_root=args.git_root, + hash_length=args.hash_length, + release=args.release, version=args.version, - release=args.release ) + + if args.output: + out = pathlib.Path(args.output) + out.parent.mkdir(parents=True, exist_ok=True) + + if not out.exists() or contents != out.read_text(encoding="utf-8"): + out.write_text(contents, encoding="utf-8") + else: + print(contents, file=sys.stdout) diff --git a/tools/platformio-build.py b/tools/platformio-build.py index 20fe8c77fb..fafcac728d 100644 --- a/tools/platformio-build.py +++ b/tools/platformio-build.py @@ -409,18 +409,20 @@ def platform_txt_version(default): if isdir(join(FRAMEWORK_DIR, ".git")): - cmd = '"$PYTHONEXE" "{script}" -b "$BUILD_DIR" -p "{framework_dir}" -v {version}' + out = join("$BUILD_DIR", "core", "core_version.h") + + cmd = '"$PYTHONEXE" "{script}" --git-root "{framework_dir}" --version {version} "$TARGET"' fmt = { "script": join(FRAMEWORK_DIR, "tools", "makecorever.py"), "framework_dir": FRAMEWORK_DIR, - "version": platform_txt_version("unspecified") + "version": platform_txt_version("unspecified"), } env.Prepend(CPPPATH=[ join("$BUILD_DIR", "core") ]) core_version = env.Command( - join("$BUILD_DIR", "core", "core_version.h"), + out, join(FRAMEWORK_DIR, ".git"), env.VerboseAction(cmd.format(**fmt), "Generating $TARGET") ) diff --git a/tools/sdk/include/bearssl/bearssl_git.h b/tools/sdk/include/bearssl/bearssl_git.h index abdd61ac02..37fe2a0d36 100644 --- a/tools/sdk/include/bearssl/bearssl_git.h +++ b/tools/sdk/include/bearssl/bearssl_git.h @@ -1,2 +1,2 @@ // Do not edit -- Automatically generated by tools/sdk/ssl/bearssl/Makefile -#define BEARSSL_GIT b024386 +#define BEARSSL_GIT 5166f2b diff --git a/tools/sdk/include/ets_sys.h b/tools/sdk/include/ets_sys.h index 7908f127c3..a199469e0d 100644 --- a/tools/sdk/include/ets_sys.h +++ b/tools/sdk/include/ets_sys.h @@ -208,7 +208,6 @@ void *ets_memset(void *s, int c, size_t n); void ets_timer_arm_new(ETSTimer *a, int b, int c, int isMstimer); void ets_timer_setfn(ETSTimer *t, ETSTimerFunc *fn, void *parg); void ets_timer_disarm(ETSTimer *a); -int atoi(const char *nptr); int ets_strncmp(const char *s1, const char *s2, int len); int ets_strcmp(const char *s1, const char *s2); int ets_strlen(const char *s); diff --git a/tools/sdk/lib/libbearssl.a b/tools/sdk/lib/libbearssl.a index 70352ab2fc..6b9a77e3f6 100644 Binary files a/tools/sdk/lib/libbearssl.a and b/tools/sdk/lib/libbearssl.a differ diff --git a/tools/sdk/ssl/bearssl b/tools/sdk/ssl/bearssl index b024386d46..5166f2bb03 160000 --- a/tools/sdk/ssl/bearssl +++ b/tools/sdk/ssl/bearssl @@ -1 +1 @@ -Subproject commit b024386d461abd1b7b9be3117e2516b7541f1201 +Subproject commit 5166f2bb03fb03597b0f2c8c7fbcf01616df67c9 diff --git a/tools/upload.py b/tools/upload.py index 760d4dbbef..a7b8c4f530 100755 --- a/tools/upload.py +++ b/tools/upload.py @@ -1,74 +1,126 @@ #!/usr/bin/env python3 # Wrapper for Arduino core / others that can call esptool.py possibly multiple times -# Adds pyserial to sys.path automatically based on the path of the current file - -# First parameter is pyserial path, second is esptool path, then a series of command arguments -# i.e. upload.py tools/pyserial tools/esptool write_flash file 0x0 +# Adds pyserial & esptool that are in the same directory as the script to sys.path import os +import atexit +import pathlib import sys import tempfile +import traceback + +from typing import List + +# Add neighbouring pyserial & esptool to search path +MODULES = [ + "pyserial", + "esptool", +] -sys.argv.pop(0) # Remove executable name -toolspath = os.path.dirname(os.path.realpath(__file__)) +PWD = pathlib.Path(__file__).resolve().parent +for m in MODULES: + sys.path.insert(0, (PWD / m).as_posix()) + +# If this fails, we can't continue and will bomb below try: - sys.path.insert(0, os.path.join(toolspath, "pyserial")) # Add pyserial dir to search path - sys.path.insert(0, os.path.join(toolspath, "esptool")) # Add esptool dir to search path - import esptool # If this fails, we can't continue and will bomb below + import esptool except ImportError: - sys.stderr.write("pyserial or esptool directories not found next to this upload.py tool.\n") + sys.stderr.write( + "\n*** pyserial or esptool directories not found next to upload.py tool (this script) ***\n" + ) + traceback.print_exc(file=sys.stderr) + sys.stderr.flush() + sys.exit(1) -cmdline = [] -write_option = '' -write_addr = '0x0' -erase_addr = '' -erase_len = '' -while sys.argv: - thisarg = sys.argv.pop(0) +def make_erase_pair(addr: str, dest_size: int, block_size=2**16): + dest, path = tempfile.mkstemp() + + buffer = bytearray(b"\xff" * block_size) + while dest_size: + remainder = dest_size % block_size + + if remainder: + src = buffer[:remainder] + src_size = remainder + else: + src = buffer + src_size = block_size + + os.write(dest, src) + dest_size -= src_size + + os.close(dest) + + def maybe_remove(path): + try: + os.remove(path) + except Exception: + pass + + atexit.register(maybe_remove, path) + return [addr, path] + - # We silently replace the 921kbaud setting with 460k to enable backward +argv = sys.argv[1:] # Remove executable name + +cmdline: List[str] = [] +write_options: List[str] = ["--flash_size", "detect"] +erase_options: List[str] = [] + +thisarg = "" +lastarg = "" +while argv: + lastarg = thisarg + thisarg = argv.pop(0) + + # We silently replace the high-speed setting with 460k to enable backward # compatibility with the old esptool-ck.exe. Esptool.py doesn't seem - # work reliably at 921k, but is still significantly faster at 460kbaud. - if thisarg == "921600": + # work reliably, but 460kbaud is still plenty fast. + if lastarg == "--baud" and thisarg in ("921600", "3000000"): thisarg = "460800" # 'erase_flash' command is translated to the write_flash --erase-all option # https://github.com/esp8266/Arduino/issues/6755#issuecomment-553208688 if thisarg == "erase_flash": - write_option = '--erase-all' - elif thisarg == 'erase_region': - erase_addr = sys.argv.pop(0) - erase_len = sys.argv.pop(0) - elif thisarg == 'write_flash': - write_addr = sys.argv.pop(0) - binary = sys.argv.pop(0) + write_options.append("--erase-all") + + # instead of providing esptool with separate targets, + # everything below becomes 'write_flash' [ ] pairs + + # 'erase_region' becomes a temporary file filled with 0xff + # this pair is appended *after* 'write_flash' pairs + elif thisarg == "erase_region": + addr = argv.pop(0) + size = int(argv.pop(0), 0) + erase_options.extend(make_erase_pair(addr, size)) + + # 'write_flash' pair taken in order it was specified + elif thisarg == "write_flash": + addr = argv.pop(0) + path = argv.pop(0) + write_options.extend([addr, path]) + + # everything else is used as-is elif thisarg: - cmdline = cmdline + [thisarg] - -cmdline = cmdline + ['write_flash'] -if write_option: - cmdline = cmdline + [write_option] -cmdline = cmdline + ['--flash_size', 'detect'] -cmdline = cmdline + [write_addr, binary] - -erase_file = '' -if erase_addr: - # Generate temporary empty (0xff) file - eraser = tempfile.mkstemp() - erase_file = eraser[1] - os.write(eraser[0], bytearray([0xff] * int(erase_len, 0))) - os.close(eraser[0]) - cmdline = cmdline + [erase_addr, erase_file] + cmdline.append(thisarg) + + +cmdline.append("write_flash") +for opts in (write_options, erase_options): + if opts: + cmdline.extend(opts) try: esptool.main(cmdline) -except Exception as e: - sys.stderr.write('\nA fatal esptool.py error occurred: %s' % e) -finally: - if erase_file: - os.remove(erase_file) - if any(sys.exc_info()): - sys.exit(2) +except Exception: + etype, evalue, _ = sys.exc_info() + estring = "\n".join(traceback.format_exception_only(etype, value=evalue)) + + sys.stderr.write("\n*** upload.py fatal error ***\n") + sys.stderr.write(estring) + sys.stderr.flush() + + sys.exit(2) diff --git a/tools/warnings/README.md b/tools/warnings/README.md index 5fb33f2bd7..fa65199f11 100644 --- a/tools/warnings/README.md +++ b/tools/warnings/README.md @@ -1,12 +1,11 @@ These are the warning options for the compiler at different levels. -Because G++ 10 produces code which crashes when a function is declared +Because GCC produces code which crashes when a function is declared to return a value but doesn't (this is undefined per the C++ specs, but legal for C11 and above code as long as the [non]returned value is ignored), we cannot warn them if we use "-w" to disable all warnings, and instead have to delete every warning but "-Wreturn-type" -Generate the "none-g++" file with the following command: -```` -./tools/xtensa-lx106-elf/bin/xtensa-lx106-elf-gcc --help=warnings -Q | grep '\[enabled\]' | grep -v 'return-type' | awk '{print $1}' | sed 's/-W/-Wno-/' | grep -v = | grep -v -- -f | egrep -v '(c11-c2x-compat|c90-c99-compat|c99-c11-compat|declaration-after-statement|designated-init|discarded-array-qualifiers|discarded-qualifiers|implicit-int|incompatible-pointer-types|int-conversion|old-style-definition|override-init-side-effects|pointer-to-int-cast)' > tools/warnings/none-g++ -```` +Generate the C++ variant with the [`make_none-cxxflags.sh`](make_none-cxxflags.sh) script + +Modify [`patterns_none-cxxflags.txt`](patterns_none-cxxflags.txt) patterns to ignore incompatible warning types diff --git a/tools/warnings/default-cppflags b/tools/warnings/default-cxxflags similarity index 100% rename from tools/warnings/default-cppflags rename to tools/warnings/default-cxxflags diff --git a/tools/warnings/extra-cppflags b/tools/warnings/extra-cxxflags similarity index 100% rename from tools/warnings/extra-cppflags rename to tools/warnings/extra-cxxflags diff --git a/tools/warnings/make_none-cxxflags.sh b/tools/warnings/make_none-cxxflags.sh new file mode 100755 index 0000000000..0f0eb2ed4c --- /dev/null +++ b/tools/warnings/make_none-cxxflags.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env sh + +root=$(git rev-parse --show-toplevel) +${CC:-xtensa-lx106-elf-gcc} --help=warnings -Q |\ + grep '\[enabled\]' |\ + grep -v 'return-type' |\ + awk '{print $1}' |\ + sed 's/-W/-Wno-/' |\ + grep -v = |\ + grep -v -f ${root}/tools/warnings/patterns_none-cxxflags.txt |\ + sort -u > ${root}/tools/warnings/none-cxxflags diff --git a/tools/warnings/more-cppflags b/tools/warnings/more-cxxflags similarity index 100% rename from tools/warnings/more-cppflags rename to tools/warnings/more-cxxflags diff --git a/tools/warnings/none-cppflags b/tools/warnings/none-cxxflags similarity index 81% rename from tools/warnings/none-cppflags rename to tools/warnings/none-cxxflags index d72f50f2a8..5a16b0d417 100644 --- a/tools/warnings/none-cppflags +++ b/tools/warnings/none-cxxflags @@ -1,5 +1,10 @@ -Wno-address-of-packed-member -Wno-aggressive-loop-optimizations +-Wno-analyzer-double-fclose +-Wno-analyzer-double-free +-Wno-analyzer-exposure-through-output-file +-Wno-analyzer-file-leak +-Wno-analyzer-free-of-non-heap -Wno-analyzer-malloc-leak -Wno-analyzer-null-argument -Wno-analyzer-null-dereference @@ -8,18 +13,19 @@ -Wno-analyzer-stale-setjmp-buffer -Wno-analyzer-tainted-array-index -Wno-analyzer-unsafe-call-within-signal-handler --Wno-attribute-warning +-Wno-analyzer-use-after-free +-Wno-analyzer-use-of-pointer-in-stale-stack-frame -Wno-attributes +-Wno-attribute-warning -Wno-builtin-declaration-mismatch -Wno-builtin-macro-redefined -Wno-cannot-profile -Wno-coverage-mismatch -Wno-cpp --Wno-deprecated --Wno-deprecated-declarations -Wno-div-by-zero -Wno-endif-labels -Wno-enum-compare +-Wno-free-nonheap-object -Wno-hsa -Wno-if-not-aligned -Wno-ignored-attributes diff --git a/tools/warnings/patterns_none-cxxflags.txt b/tools/warnings/patterns_none-cxxflags.txt new file mode 100644 index 0000000000..b5f3d24981 --- /dev/null +++ b/tools/warnings/patterns_none-cxxflags.txt @@ -0,0 +1,22 @@ += +NSObject-attribute +c11-c2x-compat +c90-c99-compat +c99-c11-compat +compare-distinct-pointer-types +complain-wrong-lang +declaration-after-statement +declaration-missing-parameter-type +deprecated +deprecated-declarations +designated-init +discarded-array-qualifiers +discarded-qualifiers +implicit-function-declaration +implicit-int +incompatible-pointer-types +int-conversion +old-style-definition +override-init-side-effects +pointer-to-int-cast +return-mismatch diff --git a/variants/mercury/pins_arduino.h b/variants/mercury/pins_arduino.h new file mode 100644 index 0000000000..df9860aac8 --- /dev/null +++ b/variants/mercury/pins_arduino.h @@ -0,0 +1,74 @@ +/* + pins_arduino.h - Pin definition functions for Arduino + Part of Arduino - http://www.arduino.cc/ + + Copyright (c) 2007 David A. Mellis + Modified for ESP8266 platform by Ivan Grokhotkov, 2014-2015. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + $Id: wiring.h 249 2007-02-03 16:52:51Z mellis $ +*/ + +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include "../generic/common.h" + +#define LED_BUILTIN 2 +#define BUILTIN_LED LED_BUILTIN + +#define A0 (17) + +static const uint8_t D0 = 0; +static const uint8_t D1 = 12; +static const uint8_t D2 = 4; +static const uint8_t D3 = 16; +static const uint8_t D4 = 5; +static const uint8_t D5 = 13; +static const uint8_t D6 = 15; +static const uint8_t D7 = 2; +static const uint8_t D8 = 14; +static const uint8_t D9 = 9; +static const uint8_t D10 = 10; + +#define PIN_WIRE_SDA (2) +#define PIN_WIRE_SCL (14) + +// Brushed DC Motors +static const uint8_t MOTOR_1_DIR = D3; +static const uint8_t MOTOR_1_PWM = D1; +static const uint8_t MOTOR_2_DIR = D4; +static const uint8_t MOTOR_2_PWM = D2; + +//Servo +static const uint8_t SERVO1 = D4; +static const uint8_t SERVO2 = D6; +static const uint8_t SERVO3 = D3; +static const uint8_t SERVO4 = D5; + +//Ultrasonic Sensor +static const uint8_t USST = D7; +static const uint8_t USSE = D8; + +//IR +static const uint8_t IR1 = D9; +static const uint8_t IR2 = D10; + +//RGB LED +static const uint8_t RGB = D0; + +#endif /* Pins_Arduino_h */