diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..30dd030d31 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# see https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot +# make sure our actions stay up-to-date and we know about any updates. +# most of the time, this happens for major releases. +# (...unless we stop using version tags and switch to hashes...) + +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/build-host.yml b/.github/workflows/build-host.yml new file mode 100644 index 0000000000..ae0353068d --- /dev/null +++ b/.github/workflows/build-host.yml @@ -0,0 +1,40 @@ +# Run host test suite under valgrind for runtime checking of code. +# Also, a quick test that the mocking builds work at all + +name: Build on host OS + +on: + pull_request: + +permissions: + contents: read + +jobs: + host-tests: + name: Tests + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - run: | + sudo apt update + sudo apt install valgrind lcov + bash ./tests/ci/host_test.sh + + mock-check: + name: Mock + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - run: | + cd tests/host + make -j ../../libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser diff --git a/.github/workflows/build-ide.yml b/.github/workflows/build-ide.yml new file mode 100644 index 0000000000..565b08d392 --- /dev/null +++ b/.github/workflows/build-ide.yml @@ -0,0 +1,107 @@ +# Cross-platform builds to ensure our Core and toolchain works + +name: Build IDE examples + +on: + pull_request: + +permissions: + contents: read + +jobs: + + # Examples are built in parallel to avoid CI total job time limitation + sanity-check: + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + with: + submodules: false + - uses: actions/cache@v4 + with: + path: ./tools/dist + key: ${{ runner.os }}-${{ hashFiles('package/package_esp8266com_index.template.json', 'tests/common.sh', 'tests/build.sh') }} + - name: Toolchain sanity checks + run: | + bash ./tests/sanity_check.sh + + build-linux: + name: Linux - LwIP ${{ matrix.lwip }} (${{ matrix.chunk }}) + runs-on: ubuntu-latest + defaults: + run: + shell: bash + strategy: + matrix: + lwip: ["default", "IPv6"] + chunk: [0, 1, 2, 3, 4, 5, 6, 7] + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - uses: actions/setup-python@v5 + with: + python-version: '3.x' + - uses: actions/cache@v4 + with: + path: ./tools/dist + key: ${{ runner.os }}-${{ hashFiles('package/package_esp8266com_index.template.json', 'tests/common.sh', 'tests/build.sh') }} + - 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 }} + + # Just try to build at least one sketch, since we spend so much time with the matrix above + + build-windows: + name: Windows + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: false + - uses: actions/setup-python@v5 + with: + python-version: '3.x' + - uses: actions/cache@v4 + with: + path: ./tools/dist + 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 + + build-mac: + name: macOS + runs-on: macOS-latest + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + with: + submodules: false + - uses: actions/setup-python@v5 + with: + python-version: '3.x' + - uses: actions/cache@v4 + with: + path: ./tools/dist + 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/build-platformio.yml b/.github/workflows/build-platformio.yml new file mode 100644 index 0000000000..5150a4bd29 --- /dev/null +++ b/.github/workflows/build-platformio.yml @@ -0,0 +1,39 @@ +# We do not distribute any environment settings, so just try to build some sketches +# using the 'master' branch and using the uploaded toolchain version via get.py +# Also, limit the amount of sketches and simply + +name: Build examples with PlatformIO + +on: + pull_request: + +permissions: + contents: read + +jobs: + build-pio: + name: Linux (random sketches) + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - uses: actions/setup-python@v5 + with: + python-version: '3.x' + - uses: actions/cache@v4 + with: + path: | + tools/dist + ~/.cache/pip + ~/.platformio/.cache + key: ${{ runner.os }}-${{ hashFiles('package/package_esp8266com_index.template.json', 'tests/common.sh', 'tests/build.sh') }} + - name: Build + env: + ESP8266_ARDUINO_BUILDER: "platformio" + run: | + pip install -U platformio + env ESP8266_ARDUINO_SKETCHES="$(find libraries/ -name '*.ino' | shuf -n 10 -)" bash ./tests/build.sh diff --git a/.github/workflows/check-autogenerated.yml b/.github/workflows/check-autogenerated.yml new file mode 100644 index 0000000000..40f3f93e70 --- /dev/null +++ b/.github/workflows/check-autogenerated.yml @@ -0,0 +1,62 @@ +# Ensure no manual edits happen to our autogenerated files + +name: Check autogenerated files + +on: + pull_request: + +permissions: + contents: read + +jobs: + pkgrefs-check: + name: .json template + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + with: + submodules: false + - run: | + bash ./tests/ci/pkgrefs_test.sh + + eboot-check: + name: eboot .elf + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + with: + submodules: false + - uses: actions/setup-python@v5 + with: + python-version: '3.x' + - uses: actions/cache@v4 + with: + path: ./tools/dist + key: ${{ runner.os }}-${{ hashFiles('package/package_esp8266com_index.template.json', 'tests/common.sh') }} + - run: | + # ^ reuse toolchain cache from our linux build job + git submodule update --init --remote tools/sdk/uzlib + bash ./tests/ci/eboot_test.sh + + boards-txt-check: + name: boards.txt.py + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + with: + submodules: false + - uses: actions/setup-python@v5 + with: + python-version: '3.x' + - name: Check git-diff result + run: | + bash ./tests/ci/build_boards.sh diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml new file mode 100644 index 0000000000..977570aad8 --- /dev/null +++ b/.github/workflows/documentation.yml @@ -0,0 +1,31 @@ +# Ensure Sphinx can build the documentation properly. + +name: Documentation + +on: + pull_request: + +permissions: + contents: read + +jobs: + documentation: + name: Sphinx build + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - uses: actions/setup-python@v5 + with: + python-version: '3.x' + - name: Build documentation + run: | + 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/pull-request.yml b/.github/workflows/pull-request.yml deleted file mode 100644 index 1f3e12a3b7..0000000000 --- a/.github/workflows/pull-request.yml +++ /dev/null @@ -1,325 +0,0 @@ -# Run whenever a PR is generated or updated. - -# Most jobs check out the code, ensure Python3 is installed, and for build -# tests the ESP8266 toolchain is cached when possible to speed up execution. - -name: ESP8266 Arduino CI - -on: - pull_request: - - -jobs: - -# Run 8 parallel jobs for the default build of all examples. - build-linux: - name: Build ${{ matrix.chunk }} - runs-on: ubuntu-latest - defaults: - run: - shell: bash - strategy: - matrix: - chunk: [0, 1, 2, 3, 4, 5, 6, 7] - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Cache Linux toolchain - id: cache-linux - uses: actions/cache@v2 - with: - path: ./tools/dist - key: ${{ runner.os }}-${{ hashFiles('package/package_esp8266com_index.template.json', 'tests/common.sh') }} - - name: Build Sketches - env: - TRAVIS_BUILD_DIR: ${{ github.workspace }} - TRAVIS_TAG: ${{ github.ref }} - BUILD_PARITY: custom - mod: 8 - rem: ${{ matrix.chunk }} - run: | - bash ./tests/build.sh - - -# Cover the debug and IPv6 cases by enabling both and running 8 parallel jobs -# over all example code. - build-debug-ipv6: - name: Debug IPv6 ${{ matrix.chunk }} - runs-on: ubuntu-latest - defaults: - run: - shell: bash - strategy: - matrix: - chunk: [0, 1, 2, 3, 4, 5, 6, 7] - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Cache Linux toolchain - id: cache-linux - uses: actions/cache@v2 - with: - path: ./tools/dist - key: ${{ runner.os }}-${{ hashFiles('package/package_esp8266com_index.template.json', 'tests/common.sh') }} - - name: Build Sketches - env: - TRAVIS_BUILD_DIR: ${{ github.workspace }} - TRAVIS_TAG: ${{ github.ref }} - BUILD_PARITY: custom - mod: 8 - rem: ${{ matrix.chunk }} - run: | - bash ./tests/debug6.sh - - -# Single build under Windows to ensure the Win toolchain is good. - build-windows: - name: Windows - runs-on: windows-latest - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Cache Windows toolchain - id: cache-windows - uses: actions/cache@v2 - with: - path: ./tools/dist - key: ${{ runner.os }}-${{ hashFiles('package/package_esp8266com_index.template.json', 'tests/common.sh') }} - - name: Build Sketch - env: - TRAVIS_BUILD_DIR: ${{ github.workspace }} - TRAVIS_TAG: ${{ github.ref }} - WINDOWS: 1 - BUILD_PARITY: custom - mod: 500 - rem: 1 - run: | - # Windows has python3 already installed, but it's called "python". - # Copy python.exe to the proper name so scripts "just work". - try { Get-Command python3 } catch { copy (get-command python).source (get-command python).source.Replace("python.exe", "python3.exe") } - bash ./tests/build.sh - - -# Single build under macOS to ensure the Mac toolchain is good. - build-mac: - name: Mac - runs-on: macOS-latest - defaults: - run: - shell: bash - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Cache Mac toolchain - id: cache-mac - uses: actions/cache@v2 - with: - path: ./tools/dist - key: ${{ runner.os }}-${{ hashFiles('package/package_esp8266com_index.template.json', 'tests/common.sh') }} - - name: Build Sketch - env: - TRAVIS_BUILD_DIR: ${{ github.workspace }} - TRAVIS_TAG: ${{ github.ref }} - MACOSX: 1 - BUILD_PARITY: custom - mod: 500 - rem: 1 - run: | - bash ./tests/build.sh - - -# Run a few Platform.IO jobs (not full suite) to check PIO integration. - build-pio: - name: Build Platform.IO - runs-on: ubuntu-latest - defaults: - run: - shell: bash - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Build subset on Platform.IO - env: - TRAVIS_BUILD_DIR: ${{ github.workspace }} - TRAVIS_TAG: ${{ github.ref }} - BUILD_PARITY: custom - mod: 42 # Picked at random to give 4-5 builds and exit. - rem: 13 - run: | - sudo apt update - sudo apt install python3-pip python3-setuptools - PATH=/home/runner/.local/bin:$PATH bash ./tests/platformio.sh - - -# Run host test suite under valgrind for runtime checking of code. - host-tests: - name: Host tests - runs-on: ubuntu-latest - defaults: - run: - shell: bash - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Run host tests - env: - TRAVIS_BUILD_DIR: ${{ github.workspace }} - TRAVIS_TAG: ${{ github.ref }} - run: | - sudo apt update - sudo apt install valgrind lcov - bash ./tests/ci/host_test.sh - - -# Ensure Sphinx can build the documentation properly. - documentation: - name: Documentation - runs-on: ubuntu-latest - defaults: - run: - shell: bash - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Build documentation - env: - TRAVIS_BUILD_DIR: ${{ github.workspace }} - TRAVIS_TAG: ${{ github.ref }} - run: | - sudo apt update - sudo apt install python3-pip python3-setuptools - # GitHub CI installs pip3 and setuptools outside the path. - # Update the path to include them and run. - PATH=/home/runner/.local/bin:$PATH pip3 install --user -r doc/requirements.txt - PATH=/home/runner/.local/bin:$PATH bash ./tests/ci/build_docs.sh - - -# Standard Arduino formatting in all the examples - style-check: - name: Style and formatting - runs-on: ubuntu-latest - defaults: - run: - shell: bash - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Style check - env: - LLVM_SNAPSHOT_KEY: "6084F3CF814B57C1CF12EFD515CF4D18AF4F7421" - TRAVIS_BUILD_DIR: ${{ github.workspace }} - TRAVIS_TAG: ${{ github.ref }} - run: | - export GNUPGHOME=$(mktemp -d) - gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "$LLVM_SNAPSHOT_KEY" - gpg --batch --armor --export "$LLVM_SNAPSHOT_KEY" | \ - sudo tee /etc/apt/trusted.gpg.d/llvm-snapshot.gpg.asc - rm -r $GNUPGHOME - echo "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-13 main" | \ - sudo tee /etc/apt/sources.list.d/llvm.list - sudo apt update - sudo apt install clang-format-13 - pip3 install pyyaml - bash ./tests/ci/style_check.sh - - -# Quick test that the mocking builds succeed - mock-check: - name: Mock trivial test - runs-on: ubuntu-latest - defaults: - run: - shell: bash - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Mock build - env: - TRAVIS_BUILD_DIR: ${{ github.workspace }} - TRAVIS_TAG: ${{ github.ref }} - run: | - bash ./tests/buildm.sh - - -# Ensure no manual edits to boards.txt - boards-check: - name: Boards.txt check - runs-on: ubuntu-latest - defaults: - run: - shell: bash - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Cache Linux toolchain - id: cache-linux - uses: actions/cache@v2 - with: - path: ./tools/dist - key: ${{ runner.os }}-${{ hashFiles('package/package_esp8266com_index.template.json', 'tests/common.sh') }} - - name: Boards.txt diff - env: - TRAVIS_BUILD_DIR: ${{ github.workspace }} - TRAVIS_TAG: ${{ github.ref }} - run: | - bash ./tests/ci/build_boards.sh - bash ./tests/ci/eboot_test.sh - bash ./tests/ci/pkgrefs_test.sh - - -# Validate orthography - code-spell: - name: Check spelling - runs-on: ubuntu-latest - defaults: - run: - shell: bash - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - name: Run codespell - uses: codespell-project/actions-codespell@master - with: - skip: ./libraries/ESP8266SdFat,./libraries/LittleFS/lib,./tools/pyserial,./tools/sdk,./tools/esptool,./libraries/SoftwareSerial,./libraries/Ethernet,./github/workflows,./libraries/ESP8266HTTPUpdateServer/examples/SecureBearSSLUpdater/SecureBearSSLUpdater.ino,./libraries/esp8266/examples/RTCUserMemory/RTCUserMemory.ino,./libraries/esp8266/examples/StreamString/StreamString.ino,./libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino,./libraries/ESP8266WiFi/examples/BearSSL_Sessions/BearSSL_Sessions.ino,./libraries/ESP8266WebServer/examples/HelloServerBearSSL/HelloServerBearSSL.ino,./libraries/ESP8266WebServer/examples/HttpHashCredAuth/HttpHashCredAuth.ino,./cores/esp8266/spiffs,./tests/device/test_libc/libm_string.c, ./libraries/Netdump/examples/Netdump/Netdump.ino,./libraries/ESP8266WiFi/examples/BearSSL_Server,./cores/esp8266/LwipIntfDev.h - ignore_words_list: ESP8266,esp8266,esp,dout,DOUT,ser,ans diff --git a/.github/workflows/release-to-publish.yml b/.github/workflows/release-to-publish.yml index 8c815e4fd3..3a80412551 100644 --- a/.github/workflows/release-to-publish.yml +++ b/.github/workflows/release-to-publish.yml @@ -28,21 +28,20 @@ on: release: types: [published] +permissions: + contents: read + jobs: package: name: Update master JSON file runs-on: ubuntu-latest - defaults: - run: - shell: bash steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: false fetch-depth: 0 - name: Deploy updated JSON env: - TRAVIS_BUILD_DIR: ${{ github.workspace }} BUILD_TYPE: package CI_GITHUB_API_KEY: ${{ secrets.GITHUB_TOKEN }} GHCI_DEPLOY_KEY: ${{ secrets.GHCI_DEPLOY_KEY }} diff --git a/.github/workflows/style-check.yml b/.github/workflows/style-check.yml new file mode 100644 index 0000000000..c371c2f252 --- /dev/null +++ b/.github/workflows/style-check.yml @@ -0,0 +1,45 @@ +name: Style and syntax checks + +on: + pull_request: + +permissions: + contents: read + +jobs: + + # Generic formatting for Core and examples + + clang-format: + name: clang-format + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - uses: actions/setup-python@v5 + with: + python-version: '3.x' + - name: Style check + run: | + sudo apt update + python ./tests/test_restyle.py --quiet + env CLANG_FORMAT="clang-format-18" bash ./tests/ci/style_check.sh + + # Validate orthography + + code-spell: + name: codespell + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: false + - name: Run codespell + uses: codespell-project/actions-codespell@master + with: + skip: ./libraries/ESP8266SdFat,./libraries/LittleFS/lib,./tools/pyserial,./tools/sdk,./tools/esptool,./libraries/SoftwareSerial,./libraries/Ethernet,./github/workflows,./libraries/ESP8266HTTPUpdateServer/examples/SecureBearSSLUpdater/SecureBearSSLUpdater.ino,./libraries/esp8266/examples/RTCUserMemory/RTCUserMemory.ino,./libraries/esp8266/examples/StreamString/StreamString.ino,./libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino,./libraries/ESP8266WiFi/examples/BearSSL_Sessions/BearSSL_Sessions.ino,./libraries/ESP8266WebServer/examples/HelloServerBearSSL/HelloServerBearSSL.ino,./libraries/ESP8266WebServer/examples/HttpHashCredAuth/HttpHashCredAuth.ino,./cores/esp8266/spiffs,./tests/device/test_libc/libm_string.c, ./libraries/Netdump/examples/Netdump/Netdump.ino,./libraries/ESP8266WiFi/examples/BearSSL_Server,./cores/esp8266/LwipIntfDev.h + ignore_words_list: ESP8266,esp8266,esp,dout,DOUT,ser,ans diff --git a/.github/workflows/tag-to-draft-release.yml b/.github/workflows/tag-to-draft-release.yml index 51240262d0..e9311f9fce 100644 --- a/.github/workflows/tag-to-draft-release.yml +++ b/.github/workflows/tag-to-draft-release.yml @@ -18,28 +18,34 @@ jobs: run: shell: bash steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: true fetch-depth: 0 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v5 with: python-version: '3.x' - name: Set GIT tag name run: | # Sets an environment variable used in the next steps - TRAVIS_TAG="$(git describe --exact-match --tags)" - echo "TRAVIS_TAG=${TRAVIS_TAG}" >> $GITHUB_ENV + ESP8266_ARDUINO_RELEASE_TAG="$(git describe --exact-match --tags)" + echo "ESP8266_ARDUINO_RELEASE_TAG=${ESP8266_ARDUINO_RELEASE_TAG}" >> $GITHUB_ENV - name: Build package JSON env: - TRAVIS_BUILD_DIR: ${{ github.workspace }} - BUILD_TYPE: package CI_GITHUB_API_KEY: ${{ secrets.GITHUB_TOKEN }} + BUILD_TYPE: package run: | - bash ./tests/ci/build_package.sh - pip3 install PyGithub - # Create a draft release and upload the ZIP and JSON files. - # This draft is not visible to normal users and needs to be - # updated manually with release notes and published from the - # GitHub web interface. - python3 ./package/upload_release.py --user "$GITHUB_ACTOR" --repo "$GITHUB_REPOSITORY" --token "$CI_GITHUB_API_KEY" --tag "$TRAVIS_TAG" --name "Release $TRAVIS_TAG" --msg "Update the draft with release notes before publishing." package/versions/*/*.zip package/versions/*/package_esp8266com_index.json + bash ./tests/ci/build_package.sh + # Create a draft release and upload the ZIP and JSON files. + # This draft is not visible to normal users and needs to be + # updated manually with release notes and published from the + # GitHub web interface. + pip3 install PyGithub + python3 ./package/upload_release.py \ + --user "$GITHUB_ACTOR" \ + --repo "$GITHUB_REPOSITORY" \ + --token "$CI_GITHUB_API_KEY" \ + --tag "$ESP8266_ARDUINO_RELEASE_TAG" \ + --name "Release ${ESP8266_ARDUINO_RELEASE_TAG}" \ + --msg "Update the draft with release notes before publishing." \ + package/versions/*/*.zip package/versions/*/package_esp8266com_index.json 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/README.md b/README.md index eaaabb6a8b..21ec6ef397 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Arduino core for ESP8266 WiFi chip # Quick links -- [Latest release documentation](https://arduino-esp8266.readthedocs.io/en/3.0.2/) +- [Latest release documentation](https://arduino-esp8266.readthedocs.io/en/3.1.2/) - [Current "git version" documentation](https://arduino-esp8266.readthedocs.io/en/latest/) - [Install git version](https://arduino-esp8266.readthedocs.io/en/latest/installing.html#using-git-version) ([sources](doc/installing.rst#using-git-version)) @@ -28,23 +28,22 @@ ESP8266 Arduino core comes with libraries to communicate over WiFi using TCP and Starting with 1.6.4, Arduino allows installation of third-party platform packages using Boards Manager. We have packages available for Windows, Mac OS, and Linux (32 and 64 bit). -- Install the current upstream Arduino IDE at the 1.8.9 level or later. The current version is on the [Arduino website](https://www.arduino.cc/en/main/software). -- Start Arduino and open the Preferences window. -- Enter ```https://arduino.esp8266.com/stable/package_esp8266com_index.json``` into the *File>Preferences>Additional Boards Manager URLs* field of the Arduino IDE. You can add multiple URLs, separating them with commas. +- [Download and install Arduino IDE 1.x or 2.x](https://www.arduino.cc/en/software) +- Start Arduino and open the Preferences window +- Enter `https://arduino.esp8266.com/stable/package_esp8266com_index.json` into the *File>Preferences>Additional Boards Manager URLs* field of the Arduino IDE. You can add multiple URLs, separating them with commas. - Open Boards Manager from Tools > Board menu and install *esp8266* platform (and don't forget to select your ESP8266 board from Tools > Board menu after installation). #### Latest release [![Latest release](https://img.shields.io/github/release/esp8266/Arduino.svg)](https://github.com/esp8266/Arduino/releases/latest/) Boards manager link: `https://arduino.esp8266.com/stable/package_esp8266com_index.json` -Documentation: [https://arduino-esp8266.readthedocs.io/en/3.0.2/](https://arduino-esp8266.readthedocs.io/en/3.0.2/) +Documentation: [https://arduino-esp8266.readthedocs.io/en/3.1.2/](https://arduino-esp8266.readthedocs.io/en/3.1.2/) ### Using git version -[![Linux build status](https://travis-ci.org/esp8266/Arduino.svg)](https://travis-ci.org/esp8266/Arduino) Also known as latest git or master branch. -- Install the current upstream Arduino IDE at the 1.8 level or later. The current version is on the [Arduino website](https://www.arduino.cc/en/main/software). -- Follow the [instructions in the documentation](https://arduino-esp8266.readthedocs.io/en/latest/installing.html#using-git-version). +- When using [Arduino IDE](https://www.arduino.cc/en/software), follow [our instructions here](https://arduino-esp8266.readthedocs.io/en/latest/installing.html#using-git-version). +- When using [PlatformIO](https://platformio.org/install), refer to [platformio/espressif8266 platform documentation](https://docs.platformio.org/en/stable/platforms/espressif8266.html#using-arduino-framework-with-staging-version). ### Using PlatformIO diff --git a/boards.txt b/boards.txt index 7204ffdca5..1b44fe3310 100644 --- a/boards.txt +++ b/boards.txt @@ -17,12 +17,14 @@ menu.FlashFreq=Flash Frequency menu.ResetMethod=Reset Method menu.dbg=Debug port menu.lvl=Debug Level +menu.optim=Debug Optimization menu.ip=lwIP Variant menu.vt=VTables menu.exception=C++ Exceptions menu.stacksmash=Stack Protection menu.wipe=Erase Flash -menu.sdk=Espressif FW +menu.sdk=NONOS SDK Version +menu.iramfloat=Floating Point operations menu.ssl=SSL Support menu.mmu=MMU menu.non32xfer=Non-32-Bit Access @@ -40,6 +42,7 @@ generic.build.mcu=esp8266 generic.build.core=esp8266 generic.build.variant=generic generic.build.spiffs_pagesize=256 +generic.build.debug_optim= generic.build.debug_port= generic.build.debug_level= generic.menu.xtal.80=80 MHz @@ -363,8 +366,8 @@ generic.menu.sdk.nonosdk_190313=nonos-sdk 2.2.1+61 (190313) generic.menu.sdk.nonosdk_190313.build.sdk=NONOSDK22x_190313 generic.menu.sdk.nonosdk221=nonos-sdk 2.2.1 (legacy) generic.menu.sdk.nonosdk221.build.sdk=NONOSDK221 -generic.menu.sdk.nonosdk3v0=nonos-sdk pre-3 (180626 known issues) -generic.menu.sdk.nonosdk3v0.build.sdk=NONOSDK3V0 +generic.menu.sdk.nonosdk305=nonos-sdk 3.0.5 (experimental) +generic.menu.sdk.nonosdk305.build.sdk=NONOSDK305 generic.menu.ip.lm2f=v2 Lower Memory generic.menu.ip.lm2f.build.lwip_include=lwip2/include generic.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat @@ -397,6 +400,12 @@ generic.menu.dbg.Serial1=Serial1 generic.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 generic.menu.lvl.None____=None generic.menu.lvl.None____.build.debug_level= +generic.menu.optim.Smallest=None +generic.menu.optim.Smallest.build.debug_optim=-Os +generic.menu.optim.Lite=Lite +generic.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +generic.menu.optim.Full=Optimum +generic.menu.optim.Full.build.debug_optim=-Og generic.menu.lvl.SSL=SSL generic.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL generic.menu.lvl.TLS_MEM=TLS_MEM @@ -488,6 +497,10 @@ generic.menu.eesz.autoflash.build.flash_size=16M generic.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld generic.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 generic.menu.eesz.autoflash.upload.maximum_size=1044464 +generic.menu.iramfloat.no=in IROM +generic.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +generic.menu.iramfloat.yes=allowed in ISR +generic.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## esp8285.name=Generic ESP8285 Module @@ -502,6 +515,7 @@ esp8285.serial.disableRTS=true esp8285.build.mcu=esp8266 esp8285.build.core=esp8266 esp8285.build.spiffs_pagesize=256 +esp8285.build.debug_optim= esp8285.build.debug_port= esp8285.build.debug_level= esp8285.menu.xtal.80=80 MHz @@ -708,8 +722,8 @@ esp8285.menu.sdk.nonosdk_190313=nonos-sdk 2.2.1+61 (190313) esp8285.menu.sdk.nonosdk_190313.build.sdk=NONOSDK22x_190313 esp8285.menu.sdk.nonosdk221=nonos-sdk 2.2.1 (legacy) esp8285.menu.sdk.nonosdk221.build.sdk=NONOSDK221 -esp8285.menu.sdk.nonosdk3v0=nonos-sdk pre-3 (180626 known issues) -esp8285.menu.sdk.nonosdk3v0.build.sdk=NONOSDK3V0 +esp8285.menu.sdk.nonosdk305=nonos-sdk 3.0.5 (experimental) +esp8285.menu.sdk.nonosdk305.build.sdk=NONOSDK305 esp8285.menu.ip.lm2f=v2 Lower Memory esp8285.menu.ip.lm2f.build.lwip_include=lwip2/include esp8285.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat @@ -742,6 +756,12 @@ esp8285.menu.dbg.Serial1=Serial1 esp8285.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 esp8285.menu.lvl.None____=None esp8285.menu.lvl.None____.build.debug_level= +esp8285.menu.optim.Smallest=None +esp8285.menu.optim.Smallest.build.debug_optim=-Os +esp8285.menu.optim.Lite=Lite +esp8285.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +esp8285.menu.optim.Full=Optimum +esp8285.menu.optim.Full.build.debug_optim=-Og esp8285.menu.lvl.SSL=SSL esp8285.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL esp8285.menu.lvl.TLS_MEM=TLS_MEM @@ -833,6 +853,10 @@ esp8285.menu.eesz.autoflash.build.flash_size=16M esp8285.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld esp8285.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 esp8285.menu.eesz.autoflash.upload.maximum_size=1044464 +esp8285.menu.iramfloat.no=in IROM +esp8285.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +esp8285.menu.iramfloat.yes=allowed in ISR +esp8285.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## gen4iod.name=4D Systems gen4 IoD Range @@ -848,6 +872,7 @@ gen4iod.serial.disableRTS=true gen4iod.build.mcu=esp8266 gen4iod.build.core=esp8266 gen4iod.build.spiffs_pagesize=256 +gen4iod.build.debug_optim= gen4iod.build.debug_port= gen4iod.build.debug_level= gen4iod.menu.xtal.80=80 MHz @@ -1010,6 +1035,12 @@ gen4iod.menu.dbg.Serial1=Serial1 gen4iod.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 gen4iod.menu.lvl.None____=None gen4iod.menu.lvl.None____.build.debug_level= +gen4iod.menu.optim.Smallest=None +gen4iod.menu.optim.Smallest.build.debug_optim=-Os +gen4iod.menu.optim.Lite=Lite +gen4iod.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +gen4iod.menu.optim.Full=Optimum +gen4iod.menu.optim.Full.build.debug_optim=-Og gen4iod.menu.lvl.SSL=SSL gen4iod.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL gen4iod.menu.lvl.TLS_MEM=TLS_MEM @@ -1101,6 +1132,10 @@ gen4iod.menu.eesz.autoflash.build.flash_size=16M gen4iod.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld gen4iod.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 gen4iod.menu.eesz.autoflash.upload.maximum_size=1044464 +gen4iod.menu.iramfloat.no=in IROM +gen4iod.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +gen4iod.menu.iramfloat.yes=allowed in ISR +gen4iod.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## huzzah.name=Adafruit Feather HUZZAH ESP8266 @@ -1115,6 +1150,7 @@ huzzah.serial.disableRTS=true huzzah.build.mcu=esp8266 huzzah.build.core=esp8266 huzzah.build.spiffs_pagesize=256 +huzzah.build.debug_optim= huzzah.build.debug_port= huzzah.build.debug_level= huzzah.menu.xtal.80=80 MHz @@ -1222,6 +1258,12 @@ huzzah.menu.dbg.Serial1=Serial1 huzzah.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 huzzah.menu.lvl.None____=None huzzah.menu.lvl.None____.build.debug_level= +huzzah.menu.optim.Smallest=None +huzzah.menu.optim.Smallest.build.debug_optim=-Os +huzzah.menu.optim.Lite=Lite +huzzah.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +huzzah.menu.optim.Full=Optimum +huzzah.menu.optim.Full.build.debug_optim=-Og huzzah.menu.lvl.SSL=SSL huzzah.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL huzzah.menu.lvl.TLS_MEM=TLS_MEM @@ -1313,6 +1355,10 @@ huzzah.menu.eesz.autoflash.build.flash_size=16M huzzah.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld huzzah.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 huzzah.menu.eesz.autoflash.upload.maximum_size=1044464 +huzzah.menu.iramfloat.no=in IROM +huzzah.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +huzzah.menu.iramfloat.yes=allowed in ISR +huzzah.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## wifi_slot.name=Amperka WiFi Slot @@ -1327,6 +1373,7 @@ wifi_slot.serial.disableRTS=true wifi_slot.build.mcu=esp8266 wifi_slot.build.core=esp8266 wifi_slot.build.spiffs_pagesize=256 +wifi_slot.build.debug_optim= wifi_slot.build.debug_port= wifi_slot.build.debug_level= wifi_slot.menu.xtal.80=80 MHz @@ -1528,6 +1575,12 @@ wifi_slot.menu.dbg.Serial1=Serial1 wifi_slot.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 wifi_slot.menu.lvl.None____=None wifi_slot.menu.lvl.None____.build.debug_level= +wifi_slot.menu.optim.Smallest=None +wifi_slot.menu.optim.Smallest.build.debug_optim=-Os +wifi_slot.menu.optim.Lite=Lite +wifi_slot.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +wifi_slot.menu.optim.Full=Optimum +wifi_slot.menu.optim.Full.build.debug_optim=-Og wifi_slot.menu.lvl.SSL=SSL wifi_slot.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL wifi_slot.menu.lvl.TLS_MEM=TLS_MEM @@ -1619,6 +1672,10 @@ wifi_slot.menu.eesz.autoflash.build.flash_size=16M wifi_slot.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld wifi_slot.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 wifi_slot.menu.eesz.autoflash.upload.maximum_size=1044464 +wifi_slot.menu.iramfloat.no=in IROM +wifi_slot.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +wifi_slot.menu.iramfloat.yes=allowed in ISR +wifi_slot.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## arduino-esp8266.name=Arduino @@ -1645,6 +1702,7 @@ arduino-esp8266.build.mcu=esp8266 arduino-esp8266.build.core=esp8266 arduino-esp8266.build.variant=generic arduino-esp8266.build.spiffs_pagesize=256 +arduino-esp8266.build.debug_optim= arduino-esp8266.build.debug_port= arduino-esp8266.build.debug_level= arduino-esp8266.menu.xtal.80=80 MHz @@ -1752,6 +1810,12 @@ arduino-esp8266.menu.dbg.Serial1=Serial1 arduino-esp8266.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 arduino-esp8266.menu.lvl.None____=None arduino-esp8266.menu.lvl.None____.build.debug_level= +arduino-esp8266.menu.optim.Smallest=None +arduino-esp8266.menu.optim.Smallest.build.debug_optim=-Os +arduino-esp8266.menu.optim.Lite=Lite +arduino-esp8266.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +arduino-esp8266.menu.optim.Full=Optimum +arduino-esp8266.menu.optim.Full.build.debug_optim=-Og arduino-esp8266.menu.lvl.SSL=SSL arduino-esp8266.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL arduino-esp8266.menu.lvl.TLS_MEM=TLS_MEM @@ -1843,6 +1907,10 @@ arduino-esp8266.menu.eesz.autoflash.build.flash_size=16M arduino-esp8266.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld arduino-esp8266.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 arduino-esp8266.menu.eesz.autoflash.upload.maximum_size=1044464 +arduino-esp8266.menu.iramfloat.no=in IROM +arduino-esp8266.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +arduino-esp8266.menu.iramfloat.yes=allowed in ISR +arduino-esp8266.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## espmxdevkit.name=DOIT ESP-Mx DevKit (ESP8285) @@ -1858,6 +1926,7 @@ espmxdevkit.serial.disableRTS=true espmxdevkit.build.mcu=esp8266 espmxdevkit.build.core=esp8266 espmxdevkit.build.spiffs_pagesize=256 +espmxdevkit.build.debug_optim= espmxdevkit.build.debug_port= espmxdevkit.build.debug_level= espmxdevkit.menu.xtal.80=80 MHz @@ -1997,6 +2066,12 @@ espmxdevkit.menu.dbg.Serial1=Serial1 espmxdevkit.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 espmxdevkit.menu.lvl.None____=None espmxdevkit.menu.lvl.None____.build.debug_level= +espmxdevkit.menu.optim.Smallest=None +espmxdevkit.menu.optim.Smallest.build.debug_optim=-Os +espmxdevkit.menu.optim.Lite=Lite +espmxdevkit.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +espmxdevkit.menu.optim.Full=Optimum +espmxdevkit.menu.optim.Full.build.debug_optim=-Og espmxdevkit.menu.lvl.SSL=SSL espmxdevkit.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL espmxdevkit.menu.lvl.TLS_MEM=TLS_MEM @@ -2088,6 +2163,10 @@ espmxdevkit.menu.eesz.autoflash.build.flash_size=16M espmxdevkit.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld espmxdevkit.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 espmxdevkit.menu.eesz.autoflash.upload.maximum_size=1044464 +espmxdevkit.menu.iramfloat.no=in IROM +espmxdevkit.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +espmxdevkit.menu.iramfloat.yes=allowed in ISR +espmxdevkit.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## oak.name=Digistump Oak @@ -2103,6 +2182,7 @@ oak.serial.disableRTS=true oak.build.mcu=esp8266 oak.build.core=esp8266 oak.build.spiffs_pagesize=256 +oak.build.debug_optim= oak.build.debug_port= oak.build.debug_level= oak.menu.xtal.80=80 MHz @@ -2210,6 +2290,12 @@ oak.menu.dbg.Serial1=Serial1 oak.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 oak.menu.lvl.None____=None oak.menu.lvl.None____.build.debug_level= +oak.menu.optim.Smallest=None +oak.menu.optim.Smallest.build.debug_optim=-Os +oak.menu.optim.Lite=Lite +oak.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +oak.menu.optim.Full=Optimum +oak.menu.optim.Full.build.debug_optim=-Og oak.menu.lvl.SSL=SSL oak.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL oak.menu.lvl.TLS_MEM=TLS_MEM @@ -2301,6 +2387,10 @@ oak.menu.eesz.autoflash.build.flash_size=16M oak.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld oak.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 oak.menu.eesz.autoflash.upload.maximum_size=1044464 +oak.menu.iramfloat.no=in IROM +oak.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +oak.menu.iramfloat.yes=allowed in ISR +oak.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## espduino.name=ESPDuino (ESP-13 Module) @@ -2324,6 +2414,7 @@ espduino.serial.disableRTS=true espduino.build.mcu=esp8266 espduino.build.core=esp8266 espduino.build.spiffs_pagesize=256 +espduino.build.debug_optim= espduino.build.debug_port= espduino.build.debug_level= espduino.menu.xtal.80=80 MHz @@ -2430,6 +2521,12 @@ espduino.menu.dbg.Serial1=Serial1 espduino.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 espduino.menu.lvl.None____=None espduino.menu.lvl.None____.build.debug_level= +espduino.menu.optim.Smallest=None +espduino.menu.optim.Smallest.build.debug_optim=-Os +espduino.menu.optim.Lite=Lite +espduino.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +espduino.menu.optim.Full=Optimum +espduino.menu.optim.Full.build.debug_optim=-Og espduino.menu.lvl.SSL=SSL espduino.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL espduino.menu.lvl.TLS_MEM=TLS_MEM @@ -2521,6 +2618,10 @@ espduino.menu.eesz.autoflash.build.flash_size=16M espduino.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld espduino.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 espduino.menu.eesz.autoflash.upload.maximum_size=1044464 +espduino.menu.iramfloat.no=in IROM +espduino.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +espduino.menu.iramfloat.yes=allowed in ISR +espduino.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## espectro.name=ESPectro Core @@ -2535,6 +2636,7 @@ espectro.serial.disableRTS=true espectro.build.mcu=esp8266 espectro.build.core=esp8266 espectro.build.spiffs_pagesize=256 +espectro.build.debug_optim= espectro.build.debug_port= espectro.build.debug_level= espectro.menu.xtal.80=80 MHz @@ -2642,6 +2744,12 @@ espectro.menu.dbg.Serial1=Serial1 espectro.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 espectro.menu.lvl.None____=None espectro.menu.lvl.None____.build.debug_level= +espectro.menu.optim.Smallest=None +espectro.menu.optim.Smallest.build.debug_optim=-Os +espectro.menu.optim.Lite=Lite +espectro.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +espectro.menu.optim.Full=Optimum +espectro.menu.optim.Full.build.debug_optim=-Og espectro.menu.lvl.SSL=SSL espectro.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL espectro.menu.lvl.TLS_MEM=TLS_MEM @@ -2733,6 +2841,10 @@ espectro.menu.eesz.autoflash.build.flash_size=16M espectro.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld espectro.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 espectro.menu.eesz.autoflash.upload.maximum_size=1044464 +espectro.menu.iramfloat.no=in IROM +espectro.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +espectro.menu.iramfloat.yes=allowed in ISR +espectro.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## espino.name=ESPino (ESP-12 Module) @@ -2747,6 +2859,7 @@ espino.serial.disableRTS=true espino.build.mcu=esp8266 espino.build.core=esp8266 espino.build.spiffs_pagesize=256 +espino.build.debug_optim= espino.build.debug_port= espino.build.debug_level= espino.menu.xtal.80=80 MHz @@ -2857,6 +2970,12 @@ espino.menu.dbg.Serial1=Serial1 espino.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 espino.menu.lvl.None____=None espino.menu.lvl.None____.build.debug_level= +espino.menu.optim.Smallest=None +espino.menu.optim.Smallest.build.debug_optim=-Os +espino.menu.optim.Lite=Lite +espino.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +espino.menu.optim.Full=Optimum +espino.menu.optim.Full.build.debug_optim=-Og espino.menu.lvl.SSL=SSL espino.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL espino.menu.lvl.TLS_MEM=TLS_MEM @@ -2948,6 +3067,10 @@ espino.menu.eesz.autoflash.build.flash_size=16M espino.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld espino.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 espino.menu.eesz.autoflash.upload.maximum_size=1044464 +espino.menu.iramfloat.no=in IROM +espino.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +espino.menu.iramfloat.yes=allowed in ISR +espino.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## espresso_lite_v1.name=ESPresso Lite 1.0 @@ -2962,6 +3085,7 @@ espresso_lite_v1.serial.disableRTS=true espresso_lite_v1.build.mcu=esp8266 espresso_lite_v1.build.core=esp8266 espresso_lite_v1.build.spiffs_pagesize=256 +espresso_lite_v1.build.debug_optim= espresso_lite_v1.build.debug_port= espresso_lite_v1.build.debug_level= espresso_lite_v1.menu.xtal.80=80 MHz @@ -3072,6 +3196,12 @@ espresso_lite_v1.menu.dbg.Serial1=Serial1 espresso_lite_v1.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 espresso_lite_v1.menu.lvl.None____=None espresso_lite_v1.menu.lvl.None____.build.debug_level= +espresso_lite_v1.menu.optim.Smallest=None +espresso_lite_v1.menu.optim.Smallest.build.debug_optim=-Os +espresso_lite_v1.menu.optim.Lite=Lite +espresso_lite_v1.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +espresso_lite_v1.menu.optim.Full=Optimum +espresso_lite_v1.menu.optim.Full.build.debug_optim=-Og espresso_lite_v1.menu.lvl.SSL=SSL espresso_lite_v1.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL espresso_lite_v1.menu.lvl.TLS_MEM=TLS_MEM @@ -3163,6 +3293,10 @@ espresso_lite_v1.menu.eesz.autoflash.build.flash_size=16M espresso_lite_v1.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld espresso_lite_v1.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 espresso_lite_v1.menu.eesz.autoflash.upload.maximum_size=1044464 +espresso_lite_v1.menu.iramfloat.no=in IROM +espresso_lite_v1.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +espresso_lite_v1.menu.iramfloat.yes=allowed in ISR +espresso_lite_v1.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## espresso_lite_v2.name=ESPresso Lite 2.0 @@ -3177,6 +3311,7 @@ espresso_lite_v2.serial.disableRTS=true espresso_lite_v2.build.mcu=esp8266 espresso_lite_v2.build.core=esp8266 espresso_lite_v2.build.spiffs_pagesize=256 +espresso_lite_v2.build.debug_optim= espresso_lite_v2.build.debug_port= espresso_lite_v2.build.debug_level= espresso_lite_v2.menu.xtal.80=80 MHz @@ -3287,6 +3422,12 @@ espresso_lite_v2.menu.dbg.Serial1=Serial1 espresso_lite_v2.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 espresso_lite_v2.menu.lvl.None____=None espresso_lite_v2.menu.lvl.None____.build.debug_level= +espresso_lite_v2.menu.optim.Smallest=None +espresso_lite_v2.menu.optim.Smallest.build.debug_optim=-Os +espresso_lite_v2.menu.optim.Lite=Lite +espresso_lite_v2.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +espresso_lite_v2.menu.optim.Full=Optimum +espresso_lite_v2.menu.optim.Full.build.debug_optim=-Og espresso_lite_v2.menu.lvl.SSL=SSL espresso_lite_v2.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL espresso_lite_v2.menu.lvl.TLS_MEM=TLS_MEM @@ -3378,6 +3519,10 @@ espresso_lite_v2.menu.eesz.autoflash.build.flash_size=16M espresso_lite_v2.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld espresso_lite_v2.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 espresso_lite_v2.menu.eesz.autoflash.upload.maximum_size=1044464 +espresso_lite_v2.menu.iramfloat.no=in IROM +espresso_lite_v2.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +espresso_lite_v2.menu.iramfloat.yes=allowed in ISR +espresso_lite_v2.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## sonoff.name=ITEAD Sonoff @@ -3401,6 +3546,7 @@ sonoff.serial.disableRTS=true sonoff.build.mcu=esp8266 sonoff.build.core=esp8266 sonoff.build.spiffs_pagesize=256 +sonoff.build.debug_optim= sonoff.build.debug_port= sonoff.build.debug_level= sonoff.menu.xtal.80=80 MHz @@ -3540,6 +3686,12 @@ sonoff.menu.dbg.Serial1=Serial1 sonoff.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 sonoff.menu.lvl.None____=None sonoff.menu.lvl.None____.build.debug_level= +sonoff.menu.optim.Smallest=None +sonoff.menu.optim.Smallest.build.debug_optim=-Os +sonoff.menu.optim.Lite=Lite +sonoff.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +sonoff.menu.optim.Full=Optimum +sonoff.menu.optim.Full.build.debug_optim=-Og sonoff.menu.lvl.SSL=SSL sonoff.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL sonoff.menu.lvl.TLS_MEM=TLS_MEM @@ -3631,6 +3783,10 @@ sonoff.menu.eesz.autoflash.build.flash_size=16M sonoff.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld sonoff.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 sonoff.menu.eesz.autoflash.upload.maximum_size=1044464 +sonoff.menu.iramfloat.no=in IROM +sonoff.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +sonoff.menu.iramfloat.yes=allowed in ISR +sonoff.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## inventone.name=Invent One @@ -3645,6 +3801,7 @@ inventone.serial.disableRTS=true inventone.build.mcu=esp8266 inventone.build.core=esp8266 inventone.build.spiffs_pagesize=256 +inventone.build.debug_optim= inventone.build.debug_port= inventone.build.debug_level= inventone.menu.xtal.80=80 MHz @@ -3752,6 +3909,12 @@ inventone.menu.dbg.Serial1=Serial1 inventone.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 inventone.menu.lvl.None____=None inventone.menu.lvl.None____.build.debug_level= +inventone.menu.optim.Smallest=None +inventone.menu.optim.Smallest.build.debug_optim=-Os +inventone.menu.optim.Lite=Lite +inventone.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +inventone.menu.optim.Full=Optimum +inventone.menu.optim.Full.build.debug_optim=-Og inventone.menu.lvl.SSL=SSL inventone.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL inventone.menu.lvl.TLS_MEM=TLS_MEM @@ -3843,6 +4006,249 @@ inventone.menu.eesz.autoflash.build.flash_size=16M inventone.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld inventone.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 inventone.menu.eesz.autoflash.upload.maximum_size=1044464 +inventone.menu.iramfloat.no=in IROM +inventone.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +inventone.menu.iramfloat.yes=allowed in ISR +inventone.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM + +############################################################## +d1_wroom_02.name=LOLIN(WEMOS) D1 ESP-WROOM-02 +d1_wroom_02.build.board=ESP8266_WEMOS_D1WROOM02 +d1_wroom_02.build.variant=d1_mini +d1_wroom_02.upload.tool=esptool +d1_wroom_02.upload.maximum_data_size=81920 +d1_wroom_02.upload.wait_for_upload_port=true +d1_wroom_02.upload.erase_cmd= +d1_wroom_02.serial.disableDTR=true +d1_wroom_02.serial.disableRTS=true +d1_wroom_02.build.mcu=esp8266 +d1_wroom_02.build.core=esp8266 +d1_wroom_02.build.spiffs_pagesize=256 +d1_wroom_02.build.debug_optim= +d1_wroom_02.build.debug_port= +d1_wroom_02.build.debug_level= +d1_wroom_02.menu.xtal.80=80 MHz +d1_wroom_02.menu.xtal.80.build.f_cpu=80000000L +d1_wroom_02.menu.xtal.160=160 MHz +d1_wroom_02.menu.xtal.160.build.f_cpu=160000000L +d1_wroom_02.menu.vt.flash=Flash +d1_wroom_02.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +d1_wroom_02.menu.vt.heap=Heap +d1_wroom_02.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +d1_wroom_02.menu.vt.iram=IRAM +d1_wroom_02.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +d1_wroom_02.menu.exception.disabled=Disabled (new aborts on oom) +d1_wroom_02.menu.exception.disabled.build.exception_flags=-fno-exceptions +d1_wroom_02.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +d1_wroom_02.menu.exception.enabled=Enabled +d1_wroom_02.menu.exception.enabled.build.exception_flags=-fexceptions +d1_wroom_02.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +d1_wroom_02.menu.stacksmash.disabled=Disabled +d1_wroom_02.menu.stacksmash.disabled.build.stacksmash_flags= +d1_wroom_02.menu.stacksmash.enabled=Enabled +d1_wroom_02.menu.stacksmash.enabled.build.stacksmash_flags=-fstack-protector +d1_wroom_02.menu.ssl.all=All SSL ciphers (most compatible) +d1_wroom_02.menu.ssl.all.build.sslflags= +d1_wroom_02.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +d1_wroom_02.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +d1_wroom_02.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +d1_wroom_02.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +d1_wroom_02.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +d1_wroom_02.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +d1_wroom_02.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +d1_wroom_02.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +d1_wroom_02.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +d1_wroom_02.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +d1_wroom_02.menu.mmu.ext128k=128K Heap External 23LC1024 +d1_wroom_02.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +d1_wroom_02.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +d1_wroom_02.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +d1_wroom_02.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +d1_wroom_02.menu.non32xfer.fast.build.non32xferflags= +d1_wroom_02.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +d1_wroom_02.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER +d1_wroom_02.upload.resetmethod=--before default_reset --after hard_reset +d1_wroom_02.build.flash_mode=dio +d1_wroom_02.build.flash_flags=-DFLASHMODE_DIO +d1_wroom_02.build.flash_freq=26 +d1_wroom_02.menu.eesz.2M64=2MB (FS:64KB OTA:~992KB) +d1_wroom_02.menu.eesz.2M64.build.flash_size=2M +d1_wroom_02.menu.eesz.2M64.build.flash_ld=eagle.flash.2m64.ld +d1_wroom_02.menu.eesz.2M64.build.spiffs_pagesize=256 +d1_wroom_02.menu.eesz.2M64.build.rfcal_addr=0x1FC000 +d1_wroom_02.menu.eesz.2M64.build.spiffs_start=0x1F0000 +d1_wroom_02.menu.eesz.2M64.build.spiffs_end=0x1FB000 +d1_wroom_02.menu.eesz.2M64.build.spiffs_blocksize=4096 +d1_wroom_02.menu.eesz.2M128=2MB (FS:128KB OTA:~960KB) +d1_wroom_02.menu.eesz.2M128.build.flash_size=2M +d1_wroom_02.menu.eesz.2M128.build.flash_ld=eagle.flash.2m128.ld +d1_wroom_02.menu.eesz.2M128.build.spiffs_pagesize=256 +d1_wroom_02.menu.eesz.2M128.build.rfcal_addr=0x1FC000 +d1_wroom_02.menu.eesz.2M128.build.spiffs_start=0x1E0000 +d1_wroom_02.menu.eesz.2M128.build.spiffs_end=0x1FB000 +d1_wroom_02.menu.eesz.2M128.build.spiffs_blocksize=4096 +d1_wroom_02.menu.eesz.2M256=2MB (FS:256KB OTA:~896KB) +d1_wroom_02.menu.eesz.2M256.build.flash_size=2M +d1_wroom_02.menu.eesz.2M256.build.flash_ld=eagle.flash.2m256.ld +d1_wroom_02.menu.eesz.2M256.build.spiffs_pagesize=256 +d1_wroom_02.menu.eesz.2M256.build.rfcal_addr=0x1FC000 +d1_wroom_02.menu.eesz.2M256.build.spiffs_start=0x1C0000 +d1_wroom_02.menu.eesz.2M256.build.spiffs_end=0x1FB000 +d1_wroom_02.menu.eesz.2M256.build.spiffs_blocksize=4096 +d1_wroom_02.menu.eesz.2M512=2MB (FS:512KB OTA:~768KB) +d1_wroom_02.menu.eesz.2M512.build.flash_size=2M +d1_wroom_02.menu.eesz.2M512.build.flash_ld=eagle.flash.2m512.ld +d1_wroom_02.menu.eesz.2M512.build.spiffs_pagesize=256 +d1_wroom_02.menu.eesz.2M512.build.rfcal_addr=0x1FC000 +d1_wroom_02.menu.eesz.2M512.build.spiffs_start=0x180000 +d1_wroom_02.menu.eesz.2M512.build.spiffs_end=0x1FA000 +d1_wroom_02.menu.eesz.2M512.build.spiffs_blocksize=8192 +d1_wroom_02.menu.eesz.2M1M=2MB (FS:1MB OTA:~512KB) +d1_wroom_02.menu.eesz.2M1M.build.flash_size=2M +d1_wroom_02.menu.eesz.2M1M.build.flash_ld=eagle.flash.2m1m.ld +d1_wroom_02.menu.eesz.2M1M.build.spiffs_pagesize=256 +d1_wroom_02.menu.eesz.2M1M.build.rfcal_addr=0x1FC000 +d1_wroom_02.menu.eesz.2M1M.build.spiffs_start=0x100000 +d1_wroom_02.menu.eesz.2M1M.build.spiffs_end=0x1FA000 +d1_wroom_02.menu.eesz.2M1M.build.spiffs_blocksize=8192 +d1_wroom_02.menu.eesz.2M=2MB (FS:none OTA:~1019KB) +d1_wroom_02.menu.eesz.2M.build.flash_size=2M +d1_wroom_02.menu.eesz.2M.build.flash_ld=eagle.flash.2m.ld +d1_wroom_02.menu.eesz.2M.build.spiffs_pagesize=256 +d1_wroom_02.menu.eesz.2M.build.rfcal_addr=0x1FC000 +d1_wroom_02.menu.ip.lm2f=v2 Lower Memory +d1_wroom_02.menu.ip.lm2f.build.lwip_include=lwip2/include +d1_wroom_02.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +d1_wroom_02.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +d1_wroom_02.menu.ip.hb2f=v2 Higher Bandwidth +d1_wroom_02.menu.ip.hb2f.build.lwip_include=lwip2/include +d1_wroom_02.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +d1_wroom_02.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +d1_wroom_02.menu.ip.lm2n=v2 Lower Memory (no features) +d1_wroom_02.menu.ip.lm2n.build.lwip_include=lwip2/include +d1_wroom_02.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +d1_wroom_02.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +d1_wroom_02.menu.ip.hb2n=v2 Higher Bandwidth (no features) +d1_wroom_02.menu.ip.hb2n.build.lwip_include=lwip2/include +d1_wroom_02.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +d1_wroom_02.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +d1_wroom_02.menu.ip.lm6f=v2 IPv6 Lower Memory +d1_wroom_02.menu.ip.lm6f.build.lwip_include=lwip2/include +d1_wroom_02.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +d1_wroom_02.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +d1_wroom_02.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +d1_wroom_02.menu.ip.hb6f.build.lwip_include=lwip2/include +d1_wroom_02.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +d1_wroom_02.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +d1_wroom_02.menu.dbg.Disabled=Disabled +d1_wroom_02.menu.dbg.Disabled.build.debug_port= +d1_wroom_02.menu.dbg.Serial=Serial +d1_wroom_02.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +d1_wroom_02.menu.dbg.Serial1=Serial1 +d1_wroom_02.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +d1_wroom_02.menu.lvl.None____=None +d1_wroom_02.menu.lvl.None____.build.debug_level= +d1_wroom_02.menu.optim.Smallest=None +d1_wroom_02.menu.optim.Smallest.build.debug_optim=-Os +d1_wroom_02.menu.optim.Lite=Lite +d1_wroom_02.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +d1_wroom_02.menu.optim.Full=Optimum +d1_wroom_02.menu.optim.Full.build.debug_optim=-Og +d1_wroom_02.menu.lvl.SSL=SSL +d1_wroom_02.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +d1_wroom_02.menu.lvl.TLS_MEM=TLS_MEM +d1_wroom_02.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +d1_wroom_02.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +d1_wroom_02.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +d1_wroom_02.menu.lvl.HTTP_SERVER=HTTP_SERVER +d1_wroom_02.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +d1_wroom_02.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +d1_wroom_02.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +d1_wroom_02.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +d1_wroom_02.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +d1_wroom_02.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +d1_wroom_02.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +d1_wroom_02.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +d1_wroom_02.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +d1_wroom_02.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +d1_wroom_02.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +d1_wroom_02.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +d1_wroom_02.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_wroom_02.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +d1_wroom_02.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +d1_wroom_02.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +d1_wroom_02.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +d1_wroom_02.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +d1_wroom_02.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_wroom_02.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +d1_wroom_02.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_wroom_02.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +d1_wroom_02.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_wroom_02.menu.lvl.CORE=CORE +d1_wroom_02.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +d1_wroom_02.menu.lvl.WIFI=WIFI +d1_wroom_02.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +d1_wroom_02.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +d1_wroom_02.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +d1_wroom_02.menu.lvl.UPDATER=UPDATER +d1_wroom_02.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +d1_wroom_02.menu.lvl.OTA=OTA +d1_wroom_02.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +d1_wroom_02.menu.lvl.OOM=OOM +d1_wroom_02.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +d1_wroom_02.menu.lvl.MDNS=MDNS +d1_wroom_02.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +d1_wroom_02.menu.lvl.HWDT=HWDT +d1_wroom_02.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +d1_wroom_02.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +d1_wroom_02.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K +d1_wroom_02.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +d1_wroom_02.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 +d1_wroom_02.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +d1_wroom_02.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 +d1_wroom_02.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +d1_wroom_02.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 +d1_wroom_02.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +d1_wroom_02.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 +d1_wroom_02.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +d1_wroom_02.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 +d1_wroom_02.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 +d1_wroom_02.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 +d1_wroom_02.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +d1_wroom_02.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +d1_wroom_02.menu.wipe.none=Only Sketch +d1_wroom_02.menu.wipe.none.upload.erase_cmd= +d1_wroom_02.menu.wipe.sdk=Sketch + WiFi Settings +d1_wroom_02.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +d1_wroom_02.menu.wipe.all=All Flash Contents +d1_wroom_02.menu.wipe.all.upload.erase_cmd=erase_flash +d1_wroom_02.menu.baud.921600=921600 +d1_wroom_02.menu.baud.921600.upload.speed=921600 +d1_wroom_02.menu.baud.57600=57600 +d1_wroom_02.menu.baud.57600.upload.speed=57600 +d1_wroom_02.menu.baud.115200=115200 +d1_wroom_02.menu.baud.115200.upload.speed=115200 +d1_wroom_02.menu.baud.230400.linux=230400 +d1_wroom_02.menu.baud.230400.macosx=230400 +d1_wroom_02.menu.baud.230400.upload.speed=230400 +d1_wroom_02.menu.baud.256000.windows=256000 +d1_wroom_02.menu.baud.256000.upload.speed=256000 +d1_wroom_02.menu.baud.460800.linux=460800 +d1_wroom_02.menu.baud.460800.macosx=460800 +d1_wroom_02.menu.baud.460800.upload.speed=460800 +d1_wroom_02.menu.baud.512000.windows=512000 +d1_wroom_02.menu.baud.512000.upload.speed=512000 +d1_wroom_02.menu.baud.3000000=3000000 +d1_wroom_02.menu.baud.3000000.upload.speed=3000000 +d1_wroom_02.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +d1_wroom_02.menu.eesz.autoflash.build.flash_size=16M +d1_wroom_02.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld +d1_wroom_02.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 +d1_wroom_02.menu.eesz.autoflash.upload.maximum_size=1044464 +d1_wroom_02.menu.iramfloat.no=in IROM +d1_wroom_02.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +d1_wroom_02.menu.iramfloat.yes=allowed in ISR +d1_wroom_02.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## d1_mini.name=LOLIN(WEMOS) D1 R2 & mini @@ -3857,6 +4263,7 @@ d1_mini.serial.disableRTS=true d1_mini.build.mcu=esp8266 d1_mini.build.core=esp8266 d1_mini.build.spiffs_pagesize=256 +d1_mini.build.debug_optim= d1_mini.build.debug_port= d1_mini.build.debug_level= d1_mini.menu.xtal.80=80 MHz @@ -3964,6 +4371,12 @@ d1_mini.menu.dbg.Serial1=Serial1 d1_mini.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 d1_mini.menu.lvl.None____=None d1_mini.menu.lvl.None____.build.debug_level= +d1_mini.menu.optim.Smallest=None +d1_mini.menu.optim.Smallest.build.debug_optim=-Os +d1_mini.menu.optim.Lite=Lite +d1_mini.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +d1_mini.menu.optim.Full=Optimum +d1_mini.menu.optim.Full.build.debug_optim=-Og d1_mini.menu.lvl.SSL=SSL d1_mini.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL d1_mini.menu.lvl.TLS_MEM=TLS_MEM @@ -4055,6 +4468,10 @@ d1_mini.menu.eesz.autoflash.build.flash_size=16M d1_mini.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld d1_mini.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 d1_mini.menu.eesz.autoflash.upload.maximum_size=1044464 +d1_mini.menu.iramfloat.no=in IROM +d1_mini.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +d1_mini.menu.iramfloat.yes=allowed in ISR +d1_mini.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## d1_mini_clone.name=LOLIN(WEMOS) D1 mini (clone) @@ -4069,6 +4486,7 @@ d1_mini_clone.serial.disableRTS=true d1_mini_clone.build.mcu=esp8266 d1_mini_clone.build.core=esp8266 d1_mini_clone.build.spiffs_pagesize=256 +d1_mini_clone.build.debug_optim= d1_mini_clone.build.debug_port= d1_mini_clone.build.debug_level= d1_mini_clone.menu.xtal.80=80 MHz @@ -4193,6 +4611,12 @@ d1_mini_clone.menu.dbg.Serial1=Serial1 d1_mini_clone.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 d1_mini_clone.menu.lvl.None____=None d1_mini_clone.menu.lvl.None____.build.debug_level= +d1_mini_clone.menu.optim.Smallest=None +d1_mini_clone.menu.optim.Smallest.build.debug_optim=-Os +d1_mini_clone.menu.optim.Lite=Lite +d1_mini_clone.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +d1_mini_clone.menu.optim.Full=Optimum +d1_mini_clone.menu.optim.Full.build.debug_optim=-Og d1_mini_clone.menu.lvl.SSL=SSL d1_mini_clone.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL d1_mini_clone.menu.lvl.TLS_MEM=TLS_MEM @@ -4284,6 +4708,10 @@ d1_mini_clone.menu.eesz.autoflash.build.flash_size=16M d1_mini_clone.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld d1_mini_clone.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 d1_mini_clone.menu.eesz.autoflash.upload.maximum_size=1044464 +d1_mini_clone.menu.iramfloat.no=in IROM +d1_mini_clone.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +d1_mini_clone.menu.iramfloat.yes=allowed in ISR +d1_mini_clone.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## d1_mini_lite.name=LOLIN(WEMOS) D1 mini Lite @@ -4298,6 +4726,7 @@ d1_mini_lite.serial.disableRTS=true d1_mini_lite.build.mcu=esp8266 d1_mini_lite.build.core=esp8266 d1_mini_lite.build.spiffs_pagesize=256 +d1_mini_lite.build.debug_optim= d1_mini_lite.build.debug_port= d1_mini_lite.build.debug_level= d1_mini_lite.menu.xtal.80=80 MHz @@ -4437,6 +4866,12 @@ d1_mini_lite.menu.dbg.Serial1=Serial1 d1_mini_lite.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 d1_mini_lite.menu.lvl.None____=None d1_mini_lite.menu.lvl.None____.build.debug_level= +d1_mini_lite.menu.optim.Smallest=None +d1_mini_lite.menu.optim.Smallest.build.debug_optim=-Os +d1_mini_lite.menu.optim.Lite=Lite +d1_mini_lite.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +d1_mini_lite.menu.optim.Full=Optimum +d1_mini_lite.menu.optim.Full.build.debug_optim=-Og d1_mini_lite.menu.lvl.SSL=SSL d1_mini_lite.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL d1_mini_lite.menu.lvl.TLS_MEM=TLS_MEM @@ -4528,6 +4963,10 @@ d1_mini_lite.menu.eesz.autoflash.build.flash_size=16M d1_mini_lite.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld d1_mini_lite.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 d1_mini_lite.menu.eesz.autoflash.upload.maximum_size=1044464 +d1_mini_lite.menu.iramfloat.no=in IROM +d1_mini_lite.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +d1_mini_lite.menu.iramfloat.yes=allowed in ISR +d1_mini_lite.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## d1_mini_pro.name=LOLIN(WEMOS) D1 mini Pro @@ -4542,6 +4981,7 @@ d1_mini_pro.serial.disableRTS=true d1_mini_pro.build.mcu=esp8266 d1_mini_pro.build.core=esp8266 d1_mini_pro.build.spiffs_pagesize=256 +d1_mini_pro.build.debug_optim= d1_mini_pro.build.debug_port= d1_mini_pro.build.debug_level= d1_mini_pro.menu.xtal.80=80 MHz @@ -4641,6 +5081,12 @@ d1_mini_pro.menu.dbg.Serial1=Serial1 d1_mini_pro.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 d1_mini_pro.menu.lvl.None____=None d1_mini_pro.menu.lvl.None____.build.debug_level= +d1_mini_pro.menu.optim.Smallest=None +d1_mini_pro.menu.optim.Smallest.build.debug_optim=-Os +d1_mini_pro.menu.optim.Lite=Lite +d1_mini_pro.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +d1_mini_pro.menu.optim.Full=Optimum +d1_mini_pro.menu.optim.Full.build.debug_optim=-Og d1_mini_pro.menu.lvl.SSL=SSL d1_mini_pro.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL d1_mini_pro.menu.lvl.TLS_MEM=TLS_MEM @@ -4732,6 +5178,10 @@ d1_mini_pro.menu.eesz.autoflash.build.flash_size=16M d1_mini_pro.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld d1_mini_pro.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 d1_mini_pro.menu.eesz.autoflash.upload.maximum_size=1044464 +d1_mini_pro.menu.iramfloat.no=in IROM +d1_mini_pro.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +d1_mini_pro.menu.iramfloat.yes=allowed in ISR +d1_mini_pro.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## d1.name=LOLIN(WeMos) D1 R1 @@ -4746,6 +5196,7 @@ d1.serial.disableRTS=true d1.build.mcu=esp8266 d1.build.core=esp8266 d1.build.spiffs_pagesize=256 +d1.build.debug_optim= d1.build.debug_port= d1.build.debug_level= d1.menu.xtal.80=80 MHz @@ -4853,6 +5304,12 @@ d1.menu.dbg.Serial1=Serial1 d1.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 d1.menu.lvl.None____=None d1.menu.lvl.None____.build.debug_level= +d1.menu.optim.Smallest=None +d1.menu.optim.Smallest.build.debug_optim=-Os +d1.menu.optim.Lite=Lite +d1.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +d1.menu.optim.Full=Optimum +d1.menu.optim.Full.build.debug_optim=-Og d1.menu.lvl.SSL=SSL d1.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL d1.menu.lvl.TLS_MEM=TLS_MEM @@ -4944,6 +5401,10 @@ d1.menu.eesz.autoflash.build.flash_size=16M d1.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld d1.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 d1.menu.eesz.autoflash.upload.maximum_size=1044464 +d1.menu.iramfloat.no=in IROM +d1.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +d1.menu.iramfloat.yes=allowed in ISR +d1.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## agruminolemon.name=Lifely Agrumino Lemon v4 @@ -4958,6 +5419,7 @@ agruminolemon.serial.disableRTS=true agruminolemon.build.mcu=esp8266 agruminolemon.build.core=esp8266 agruminolemon.build.spiffs_pagesize=256 +agruminolemon.build.debug_optim= agruminolemon.build.debug_port= agruminolemon.build.debug_level= agruminolemon.menu.xtal.80=80 MHz @@ -5081,6 +5543,12 @@ agruminolemon.menu.dbg.Serial1=Serial1 agruminolemon.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 agruminolemon.menu.lvl.None____=None agruminolemon.menu.lvl.None____.build.debug_level= +agruminolemon.menu.optim.Smallest=None +agruminolemon.menu.optim.Smallest.build.debug_optim=-Os +agruminolemon.menu.optim.Lite=Lite +agruminolemon.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +agruminolemon.menu.optim.Full=Optimum +agruminolemon.menu.optim.Full.build.debug_optim=-Og agruminolemon.menu.lvl.SSL=SSL agruminolemon.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL agruminolemon.menu.lvl.TLS_MEM=TLS_MEM @@ -5172,6 +5640,233 @@ agruminolemon.menu.eesz.autoflash.build.flash_size=16M agruminolemon.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld agruminolemon.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 agruminolemon.menu.eesz.autoflash.upload.maximum_size=1044464 +agruminolemon.menu.iramfloat.no=in IROM +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 + +############################################################## +mercury1.name=Mercury 1.0 +mercury1.build.board=mercury +mercury1.build.variant=mercury_v1 +mercury1.upload.tool=esptool +mercury1.upload.maximum_data_size=81920 +mercury1.upload.wait_for_upload_port=true +mercury1.upload.erase_cmd= +mercury1.serial.disableDTR=true +mercury1.serial.disableRTS=true +mercury1.build.mcu=esp8266 +mercury1.build.core=esp8266 +mercury1.build.spiffs_pagesize=256 +mercury1.build.debug_optim= +mercury1.build.debug_port= +mercury1.build.debug_level= +mercury1.menu.xtal.80=80 MHz +mercury1.menu.xtal.80.build.f_cpu=80000000L +mercury1.menu.xtal.160=160 MHz +mercury1.menu.xtal.160.build.f_cpu=160000000L +mercury1.menu.vt.flash=Flash +mercury1.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +mercury1.menu.vt.heap=Heap +mercury1.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +mercury1.menu.vt.iram=IRAM +mercury1.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +mercury1.menu.exception.disabled=Disabled (new aborts on oom) +mercury1.menu.exception.disabled.build.exception_flags=-fno-exceptions +mercury1.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +mercury1.menu.exception.enabled=Enabled +mercury1.menu.exception.enabled.build.exception_flags=-fexceptions +mercury1.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +mercury1.menu.stacksmash.disabled=Disabled +mercury1.menu.stacksmash.disabled.build.stacksmash_flags= +mercury1.menu.stacksmash.enabled=Enabled +mercury1.menu.stacksmash.enabled.build.stacksmash_flags=-fstack-protector +mercury1.menu.ssl.all=All SSL ciphers (most compatible) +mercury1.menu.ssl.all.build.sslflags= +mercury1.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +mercury1.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +mercury1.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +mercury1.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +mercury1.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +mercury1.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +mercury1.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +mercury1.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +mercury1.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +mercury1.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +mercury1.menu.mmu.ext128k=128K Heap External 23LC1024 +mercury1.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +mercury1.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +mercury1.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +mercury1.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +mercury1.menu.non32xfer.fast.build.non32xferflags= +mercury1.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +mercury1.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER +mercury1.upload.resetmethod=--before default_reset --after hard_reset +mercury1.build.flash_mode=dio +mercury1.build.flash_flags=-DFLASHMODE_DIO +mercury1.build.flash_freq=40 +mercury1.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) +mercury1.menu.eesz.4M2M.build.flash_size=4M +mercury1.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +mercury1.menu.eesz.4M2M.build.spiffs_pagesize=256 +mercury1.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +mercury1.menu.eesz.4M2M.build.spiffs_start=0x200000 +mercury1.menu.eesz.4M2M.build.spiffs_end=0x3FA000 +mercury1.menu.eesz.4M2M.build.spiffs_blocksize=8192 +mercury1.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) +mercury1.menu.eesz.4M3M.build.flash_size=4M +mercury1.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +mercury1.menu.eesz.4M3M.build.spiffs_pagesize=256 +mercury1.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +mercury1.menu.eesz.4M3M.build.spiffs_start=0x100000 +mercury1.menu.eesz.4M3M.build.spiffs_end=0x3FA000 +mercury1.menu.eesz.4M3M.build.spiffs_blocksize=8192 +mercury1.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) +mercury1.menu.eesz.4M1M.build.flash_size=4M +mercury1.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +mercury1.menu.eesz.4M1M.build.spiffs_pagesize=256 +mercury1.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +mercury1.menu.eesz.4M1M.build.spiffs_start=0x300000 +mercury1.menu.eesz.4M1M.build.spiffs_end=0x3FA000 +mercury1.menu.eesz.4M1M.build.spiffs_blocksize=8192 +mercury1.menu.eesz.4M=4MB (FS:none OTA:~1019KB) +mercury1.menu.eesz.4M.build.flash_size=4M +mercury1.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +mercury1.menu.eesz.4M.build.spiffs_pagesize=256 +mercury1.menu.eesz.4M.build.rfcal_addr=0x3FC000 +mercury1.menu.ip.lm2f=v2 Lower Memory +mercury1.menu.ip.lm2f.build.lwip_include=lwip2/include +mercury1.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +mercury1.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +mercury1.menu.ip.hb2f=v2 Higher Bandwidth +mercury1.menu.ip.hb2f.build.lwip_include=lwip2/include +mercury1.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +mercury1.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +mercury1.menu.ip.lm2n=v2 Lower Memory (no features) +mercury1.menu.ip.lm2n.build.lwip_include=lwip2/include +mercury1.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +mercury1.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +mercury1.menu.ip.hb2n=v2 Higher Bandwidth (no features) +mercury1.menu.ip.hb2n.build.lwip_include=lwip2/include +mercury1.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +mercury1.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +mercury1.menu.ip.lm6f=v2 IPv6 Lower Memory +mercury1.menu.ip.lm6f.build.lwip_include=lwip2/include +mercury1.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +mercury1.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +mercury1.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +mercury1.menu.ip.hb6f.build.lwip_include=lwip2/include +mercury1.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +mercury1.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +mercury1.menu.dbg.Disabled=Disabled +mercury1.menu.dbg.Disabled.build.debug_port= +mercury1.menu.dbg.Serial=Serial +mercury1.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +mercury1.menu.dbg.Serial1=Serial1 +mercury1.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 +mercury1.menu.lvl.None____=None +mercury1.menu.lvl.None____.build.debug_level= +mercury1.menu.optim.Smallest=None +mercury1.menu.optim.Smallest.build.debug_optim=-Os +mercury1.menu.optim.Lite=Lite +mercury1.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +mercury1.menu.optim.Full=Optimum +mercury1.menu.optim.Full.build.debug_optim=-Og +mercury1.menu.lvl.SSL=SSL +mercury1.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL +mercury1.menu.lvl.TLS_MEM=TLS_MEM +mercury1.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +mercury1.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +mercury1.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +mercury1.menu.lvl.HTTP_SERVER=HTTP_SERVER +mercury1.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +mercury1.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +mercury1.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +mercury1.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +mercury1.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +mercury1.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +mercury1.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +mercury1.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +mercury1.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +mercury1.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +mercury1.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +mercury1.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +mercury1.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +mercury1.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +mercury1.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +mercury1.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +mercury1.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +mercury1.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +mercury1.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +mercury1.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +mercury1.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +mercury1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +mercury1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +mercury1.menu.lvl.CORE=CORE +mercury1.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +mercury1.menu.lvl.WIFI=WIFI +mercury1.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +mercury1.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +mercury1.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +mercury1.menu.lvl.UPDATER=UPDATER +mercury1.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +mercury1.menu.lvl.OTA=OTA +mercury1.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +mercury1.menu.lvl.OOM=OOM +mercury1.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +mercury1.menu.lvl.MDNS=MDNS +mercury1.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +mercury1.menu.lvl.HWDT=HWDT +mercury1.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +mercury1.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +mercury1.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K +mercury1.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +mercury1.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 +mercury1.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +mercury1.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 +mercury1.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +mercury1.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 +mercury1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +mercury1.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 +mercury1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +mercury1.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 +mercury1.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 +mercury1.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 +mercury1.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +mercury1.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +mercury1.menu.wipe.none=Only Sketch +mercury1.menu.wipe.none.upload.erase_cmd= +mercury1.menu.wipe.sdk=Sketch + WiFi Settings +mercury1.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +mercury1.menu.wipe.all=All Flash Contents +mercury1.menu.wipe.all.upload.erase_cmd=erase_flash +mercury1.menu.baud.115200=115200 +mercury1.menu.baud.115200.upload.speed=115200 +mercury1.menu.baud.57600=57600 +mercury1.menu.baud.57600.upload.speed=57600 +mercury1.menu.baud.230400.linux=230400 +mercury1.menu.baud.230400.macosx=230400 +mercury1.menu.baud.230400.upload.speed=230400 +mercury1.menu.baud.256000.windows=256000 +mercury1.menu.baud.256000.upload.speed=256000 +mercury1.menu.baud.460800.linux=460800 +mercury1.menu.baud.460800.macosx=460800 +mercury1.menu.baud.460800.upload.speed=460800 +mercury1.menu.baud.512000.windows=512000 +mercury1.menu.baud.512000.upload.speed=512000 +mercury1.menu.baud.921600=921600 +mercury1.menu.baud.921600.upload.speed=921600 +mercury1.menu.baud.3000000=3000000 +mercury1.menu.baud.3000000.upload.speed=3000000 +mercury1.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +mercury1.menu.eesz.autoflash.build.flash_size=16M +mercury1.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld +mercury1.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 +mercury1.menu.eesz.autoflash.upload.maximum_size=1044464 +mercury1.menu.iramfloat.no=in IROM +mercury1.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +mercury1.menu.iramfloat.yes=allowed in ISR +mercury1.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## nodemcu.name=NodeMCU 0.9 (ESP-12 Module) @@ -5186,6 +5881,7 @@ nodemcu.serial.disableRTS=true nodemcu.build.mcu=esp8266 nodemcu.build.core=esp8266 nodemcu.build.spiffs_pagesize=256 +nodemcu.build.debug_optim= nodemcu.build.debug_port= nodemcu.build.debug_level= nodemcu.menu.xtal.80=80 MHz @@ -5293,6 +5989,12 @@ nodemcu.menu.dbg.Serial1=Serial1 nodemcu.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 nodemcu.menu.lvl.None____=None nodemcu.menu.lvl.None____.build.debug_level= +nodemcu.menu.optim.Smallest=None +nodemcu.menu.optim.Smallest.build.debug_optim=-Os +nodemcu.menu.optim.Lite=Lite +nodemcu.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +nodemcu.menu.optim.Full=Optimum +nodemcu.menu.optim.Full.build.debug_optim=-Og nodemcu.menu.lvl.SSL=SSL nodemcu.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL nodemcu.menu.lvl.TLS_MEM=TLS_MEM @@ -5384,6 +6086,10 @@ nodemcu.menu.eesz.autoflash.build.flash_size=16M nodemcu.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld nodemcu.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 nodemcu.menu.eesz.autoflash.upload.maximum_size=1044464 +nodemcu.menu.iramfloat.no=in IROM +nodemcu.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +nodemcu.menu.iramfloat.yes=allowed in ISR +nodemcu.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## nodemcuv2.name=NodeMCU 1.0 (ESP-12E Module) @@ -5398,6 +6104,7 @@ nodemcuv2.serial.disableRTS=true nodemcuv2.build.mcu=esp8266 nodemcuv2.build.core=esp8266 nodemcuv2.build.spiffs_pagesize=256 +nodemcuv2.build.debug_optim= nodemcuv2.build.debug_port= nodemcuv2.build.debug_level= nodemcuv2.menu.xtal.80=80 MHz @@ -5509,6 +6216,12 @@ nodemcuv2.menu.dbg.Serial1=Serial1 nodemcuv2.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 nodemcuv2.menu.lvl.None____=None nodemcuv2.menu.lvl.None____.build.debug_level= +nodemcuv2.menu.optim.Smallest=None +nodemcuv2.menu.optim.Smallest.build.debug_optim=-Os +nodemcuv2.menu.optim.Lite=Lite +nodemcuv2.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +nodemcuv2.menu.optim.Full=Optimum +nodemcuv2.menu.optim.Full.build.debug_optim=-Og nodemcuv2.menu.lvl.SSL=SSL nodemcuv2.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL nodemcuv2.menu.lvl.TLS_MEM=TLS_MEM @@ -5600,6 +6313,10 @@ nodemcuv2.menu.eesz.autoflash.build.flash_size=16M nodemcuv2.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld nodemcuv2.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 nodemcuv2.menu.eesz.autoflash.upload.maximum_size=1044464 +nodemcuv2.menu.iramfloat.no=in IROM +nodemcuv2.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +nodemcuv2.menu.iramfloat.yes=allowed in ISR +nodemcuv2.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## modwifi.name=Olimex MOD-WIFI-ESP8266(-DEV) @@ -5614,6 +6331,7 @@ modwifi.serial.disableRTS=true modwifi.build.mcu=esp8266 modwifi.build.core=esp8266 modwifi.build.spiffs_pagesize=256 +modwifi.build.debug_optim= modwifi.build.debug_port= modwifi.build.debug_level= modwifi.menu.xtal.80=80 MHz @@ -5752,6 +6470,12 @@ modwifi.menu.dbg.Serial1=Serial1 modwifi.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 modwifi.menu.lvl.None____=None modwifi.menu.lvl.None____.build.debug_level= +modwifi.menu.optim.Smallest=None +modwifi.menu.optim.Smallest.build.debug_optim=-Os +modwifi.menu.optim.Lite=Lite +modwifi.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +modwifi.menu.optim.Full=Optimum +modwifi.menu.optim.Full.build.debug_optim=-Og modwifi.menu.lvl.SSL=SSL modwifi.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL modwifi.menu.lvl.TLS_MEM=TLS_MEM @@ -5843,6 +6567,10 @@ modwifi.menu.eesz.autoflash.build.flash_size=16M modwifi.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld modwifi.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 modwifi.menu.eesz.autoflash.upload.maximum_size=1044464 +modwifi.menu.iramfloat.no=in IROM +modwifi.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +modwifi.menu.iramfloat.yes=allowed in ISR +modwifi.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## phoenix_v1.name=Phoenix 1.0 @@ -5857,6 +6585,7 @@ phoenix_v1.serial.disableRTS=true phoenix_v1.build.mcu=esp8266 phoenix_v1.build.core=esp8266 phoenix_v1.build.spiffs_pagesize=256 +phoenix_v1.build.debug_optim= phoenix_v1.build.debug_port= phoenix_v1.build.debug_level= phoenix_v1.menu.xtal.80=80 MHz @@ -5967,6 +6696,12 @@ phoenix_v1.menu.dbg.Serial1=Serial1 phoenix_v1.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 phoenix_v1.menu.lvl.None____=None phoenix_v1.menu.lvl.None____.build.debug_level= +phoenix_v1.menu.optim.Smallest=None +phoenix_v1.menu.optim.Smallest.build.debug_optim=-Os +phoenix_v1.menu.optim.Lite=Lite +phoenix_v1.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +phoenix_v1.menu.optim.Full=Optimum +phoenix_v1.menu.optim.Full.build.debug_optim=-Og phoenix_v1.menu.lvl.SSL=SSL phoenix_v1.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL phoenix_v1.menu.lvl.TLS_MEM=TLS_MEM @@ -6058,6 +6793,10 @@ phoenix_v1.menu.eesz.autoflash.build.flash_size=16M phoenix_v1.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld phoenix_v1.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 phoenix_v1.menu.eesz.autoflash.upload.maximum_size=1044464 +phoenix_v1.menu.iramfloat.no=in IROM +phoenix_v1.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +phoenix_v1.menu.iramfloat.yes=allowed in ISR +phoenix_v1.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## phoenix_v2.name=Phoenix 2.0 @@ -6072,6 +6811,7 @@ phoenix_v2.serial.disableRTS=true phoenix_v2.build.mcu=esp8266 phoenix_v2.build.core=esp8266 phoenix_v2.build.spiffs_pagesize=256 +phoenix_v2.build.debug_optim= phoenix_v2.build.debug_port= phoenix_v2.build.debug_level= phoenix_v2.menu.xtal.80=80 MHz @@ -6182,6 +6922,12 @@ phoenix_v2.menu.dbg.Serial1=Serial1 phoenix_v2.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 phoenix_v2.menu.lvl.None____=None phoenix_v2.menu.lvl.None____.build.debug_level= +phoenix_v2.menu.optim.Smallest=None +phoenix_v2.menu.optim.Smallest.build.debug_optim=-Os +phoenix_v2.menu.optim.Lite=Lite +phoenix_v2.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +phoenix_v2.menu.optim.Full=Optimum +phoenix_v2.menu.optim.Full.build.debug_optim=-Og phoenix_v2.menu.lvl.SSL=SSL phoenix_v2.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL phoenix_v2.menu.lvl.TLS_MEM=TLS_MEM @@ -6273,6 +7019,10 @@ phoenix_v2.menu.eesz.autoflash.build.flash_size=16M phoenix_v2.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld phoenix_v2.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 phoenix_v2.menu.eesz.autoflash.upload.maximum_size=1044464 +phoenix_v2.menu.iramfloat.no=in IROM +phoenix_v2.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +phoenix_v2.menu.iramfloat.yes=allowed in ISR +phoenix_v2.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## eduinowifi.name=Schirmilabs Eduino WiFi @@ -6287,6 +7037,7 @@ eduinowifi.serial.disableRTS=true eduinowifi.build.mcu=esp8266 eduinowifi.build.core=esp8266 eduinowifi.build.spiffs_pagesize=256 +eduinowifi.build.debug_optim= eduinowifi.build.debug_port= eduinowifi.build.debug_level= eduinowifi.menu.xtal.80=80 MHz @@ -6394,6 +7145,12 @@ eduinowifi.menu.dbg.Serial1=Serial1 eduinowifi.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 eduinowifi.menu.lvl.None____=None eduinowifi.menu.lvl.None____.build.debug_level= +eduinowifi.menu.optim.Smallest=None +eduinowifi.menu.optim.Smallest.build.debug_optim=-Os +eduinowifi.menu.optim.Lite=Lite +eduinowifi.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +eduinowifi.menu.optim.Full=Optimum +eduinowifi.menu.optim.Full.build.debug_optim=-Og eduinowifi.menu.lvl.SSL=SSL eduinowifi.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL eduinowifi.menu.lvl.TLS_MEM=TLS_MEM @@ -6485,6 +7242,10 @@ eduinowifi.menu.eesz.autoflash.build.flash_size=16M eduinowifi.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld eduinowifi.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 eduinowifi.menu.eesz.autoflash.upload.maximum_size=1044464 +eduinowifi.menu.iramfloat.no=in IROM +eduinowifi.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +eduinowifi.menu.iramfloat.yes=allowed in ISR +eduinowifi.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## wiolink.name=Seeed Wio Link @@ -6499,6 +7260,7 @@ wiolink.serial.disableRTS=true wiolink.build.mcu=esp8266 wiolink.build.core=esp8266 wiolink.build.spiffs_pagesize=256 +wiolink.build.debug_optim= wiolink.build.debug_port= wiolink.build.debug_level= wiolink.menu.xtal.80=80 MHz @@ -6606,6 +7368,12 @@ wiolink.menu.dbg.Serial1=Serial1 wiolink.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 wiolink.menu.lvl.None____=None wiolink.menu.lvl.None____.build.debug_level= +wiolink.menu.optim.Smallest=None +wiolink.menu.optim.Smallest.build.debug_optim=-Os +wiolink.menu.optim.Lite=Lite +wiolink.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +wiolink.menu.optim.Full=Optimum +wiolink.menu.optim.Full.build.debug_optim=-Og wiolink.menu.lvl.SSL=SSL wiolink.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL wiolink.menu.lvl.TLS_MEM=TLS_MEM @@ -6697,6 +7465,10 @@ wiolink.menu.eesz.autoflash.build.flash_size=16M wiolink.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld wiolink.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 wiolink.menu.eesz.autoflash.upload.maximum_size=1044464 +wiolink.menu.iramfloat.no=in IROM +wiolink.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +wiolink.menu.iramfloat.yes=allowed in ISR +wiolink.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## blynk.name=SparkFun Blynk Board @@ -6711,6 +7483,7 @@ blynk.serial.disableRTS=true blynk.build.mcu=esp8266 blynk.build.core=esp8266 blynk.build.spiffs_pagesize=256 +blynk.build.debug_optim= blynk.build.debug_port= blynk.build.debug_level= blynk.menu.xtal.80=80 MHz @@ -6818,6 +7591,12 @@ blynk.menu.dbg.Serial1=Serial1 blynk.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 blynk.menu.lvl.None____=None blynk.menu.lvl.None____.build.debug_level= +blynk.menu.optim.Smallest=None +blynk.menu.optim.Smallest.build.debug_optim=-Os +blynk.menu.optim.Lite=Lite +blynk.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +blynk.menu.optim.Full=Optimum +blynk.menu.optim.Full.build.debug_optim=-Og blynk.menu.lvl.SSL=SSL blynk.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL blynk.menu.lvl.TLS_MEM=TLS_MEM @@ -6909,6 +7688,10 @@ blynk.menu.eesz.autoflash.build.flash_size=16M blynk.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld blynk.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 blynk.menu.eesz.autoflash.upload.maximum_size=1044464 +blynk.menu.iramfloat.no=in IROM +blynk.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +blynk.menu.iramfloat.yes=allowed in ISR +blynk.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## thing.name=SparkFun ESP8266 Thing @@ -6923,6 +7706,7 @@ thing.serial.disableRTS=true thing.build.mcu=esp8266 thing.build.core=esp8266 thing.build.spiffs_pagesize=256 +thing.build.debug_optim= thing.build.debug_port= thing.build.debug_level= thing.menu.xtal.80=80 MHz @@ -7030,6 +7814,12 @@ thing.menu.dbg.Serial1=Serial1 thing.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 thing.menu.lvl.None____=None thing.menu.lvl.None____.build.debug_level= +thing.menu.optim.Smallest=None +thing.menu.optim.Smallest.build.debug_optim=-Os +thing.menu.optim.Lite=Lite +thing.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +thing.menu.optim.Full=Optimum +thing.menu.optim.Full.build.debug_optim=-Og thing.menu.lvl.SSL=SSL thing.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL thing.menu.lvl.TLS_MEM=TLS_MEM @@ -7121,6 +7911,10 @@ thing.menu.eesz.autoflash.build.flash_size=16M thing.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld thing.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 thing.menu.eesz.autoflash.upload.maximum_size=1044464 +thing.menu.iramfloat.no=in IROM +thing.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +thing.menu.iramfloat.yes=allowed in ISR +thing.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## thingdev.name=SparkFun ESP8266 Thing Dev @@ -7135,6 +7929,7 @@ thingdev.serial.disableRTS=true thingdev.build.mcu=esp8266 thingdev.build.core=esp8266 thingdev.build.spiffs_pagesize=256 +thingdev.build.debug_optim= thingdev.build.debug_port= thingdev.build.debug_level= thingdev.menu.xtal.80=80 MHz @@ -7242,6 +8037,12 @@ thingdev.menu.dbg.Serial1=Serial1 thingdev.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 thingdev.menu.lvl.None____=None thingdev.menu.lvl.None____.build.debug_level= +thingdev.menu.optim.Smallest=None +thingdev.menu.optim.Smallest.build.debug_optim=-Os +thingdev.menu.optim.Lite=Lite +thingdev.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +thingdev.menu.optim.Full=Optimum +thingdev.menu.optim.Full.build.debug_optim=-Og thingdev.menu.lvl.SSL=SSL thingdev.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL thingdev.menu.lvl.TLS_MEM=TLS_MEM @@ -7333,6 +8134,10 @@ thingdev.menu.eesz.autoflash.build.flash_size=16M thingdev.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld thingdev.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 thingdev.menu.eesz.autoflash.upload.maximum_size=1044464 +thingdev.menu.iramfloat.no=in IROM +thingdev.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +thingdev.menu.iramfloat.yes=allowed in ISR +thingdev.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## esp210.name=SweetPea ESP-210 @@ -7347,6 +8152,7 @@ esp210.build.mcu=esp8266 esp210.build.core=esp8266 esp210.build.variant=generic esp210.build.spiffs_pagesize=256 +esp210.build.debug_optim= esp210.build.debug_port= esp210.build.debug_level= esp210.menu.xtal.80=80 MHz @@ -7454,6 +8260,12 @@ esp210.menu.dbg.Serial1=Serial1 esp210.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 esp210.menu.lvl.None____=None esp210.menu.lvl.None____.build.debug_level= +esp210.menu.optim.Smallest=None +esp210.menu.optim.Smallest.build.debug_optim=-Os +esp210.menu.optim.Lite=Lite +esp210.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +esp210.menu.optim.Full=Optimum +esp210.menu.optim.Full.build.debug_optim=-Og esp210.menu.lvl.SSL=SSL esp210.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL esp210.menu.lvl.TLS_MEM=TLS_MEM @@ -7545,6 +8357,10 @@ esp210.menu.eesz.autoflash.build.flash_size=16M esp210.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld esp210.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 esp210.menu.eesz.autoflash.upload.maximum_size=1044464 +esp210.menu.iramfloat.no=in IROM +esp210.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +esp210.menu.iramfloat.yes=allowed in ISR +esp210.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## espinotee.name=ThaiEasyElec's ESPino @@ -7559,6 +8375,7 @@ espinotee.serial.disableRTS=true espinotee.build.mcu=esp8266 espinotee.build.core=esp8266 espinotee.build.spiffs_pagesize=256 +espinotee.build.debug_optim= espinotee.build.debug_port= espinotee.build.debug_level= espinotee.menu.xtal.80=80 MHz @@ -7666,6 +8483,12 @@ espinotee.menu.dbg.Serial1=Serial1 espinotee.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 espinotee.menu.lvl.None____=None espinotee.menu.lvl.None____.build.debug_level= +espinotee.menu.optim.Smallest=None +espinotee.menu.optim.Smallest.build.debug_optim=-Os +espinotee.menu.optim.Lite=Lite +espinotee.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +espinotee.menu.optim.Full=Optimum +espinotee.menu.optim.Full.build.debug_optim=-Og espinotee.menu.lvl.SSL=SSL espinotee.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL espinotee.menu.lvl.TLS_MEM=TLS_MEM @@ -7757,6 +8580,10 @@ espinotee.menu.eesz.autoflash.build.flash_size=16M espinotee.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld espinotee.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 espinotee.menu.eesz.autoflash.upload.maximum_size=1044464 +espinotee.menu.iramfloat.no=in IROM +espinotee.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +espinotee.menu.iramfloat.yes=allowed in ISR +espinotee.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## wifi_kit_8.name=WiFi Kit 8 @@ -7771,6 +8598,7 @@ wifi_kit_8.serial.disableRTS=true wifi_kit_8.build.mcu=esp8266 wifi_kit_8.build.core=esp8266 wifi_kit_8.build.spiffs_pagesize=256 +wifi_kit_8.build.debug_optim= wifi_kit_8.build.debug_port= wifi_kit_8.build.debug_level= wifi_kit_8.menu.xtal.80=80 MHz @@ -7878,6 +8706,12 @@ wifi_kit_8.menu.dbg.Serial1=Serial1 wifi_kit_8.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 wifi_kit_8.menu.lvl.None____=None wifi_kit_8.menu.lvl.None____.build.debug_level= +wifi_kit_8.menu.optim.Smallest=None +wifi_kit_8.menu.optim.Smallest.build.debug_optim=-Os +wifi_kit_8.menu.optim.Lite=Lite +wifi_kit_8.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +wifi_kit_8.menu.optim.Full=Optimum +wifi_kit_8.menu.optim.Full.build.debug_optim=-Og wifi_kit_8.menu.lvl.SSL=SSL wifi_kit_8.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL wifi_kit_8.menu.lvl.TLS_MEM=TLS_MEM @@ -7969,6 +8803,10 @@ wifi_kit_8.menu.eesz.autoflash.build.flash_size=16M wifi_kit_8.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld wifi_kit_8.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 wifi_kit_8.menu.eesz.autoflash.upload.maximum_size=1044464 +wifi_kit_8.menu.iramfloat.no=in IROM +wifi_kit_8.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +wifi_kit_8.menu.iramfloat.yes=allowed in ISR +wifi_kit_8.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## wifiduino.name=WiFiduino @@ -7983,6 +8821,7 @@ wifiduino.serial.disableRTS=true wifiduino.build.mcu=esp8266 wifiduino.build.core=esp8266 wifiduino.build.spiffs_pagesize=256 +wifiduino.build.debug_optim= wifiduino.build.debug_port= wifiduino.build.debug_level= wifiduino.menu.xtal.80=80 MHz @@ -8090,6 +8929,12 @@ wifiduino.menu.dbg.Serial1=Serial1 wifiduino.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 wifiduino.menu.lvl.None____=None wifiduino.menu.lvl.None____.build.debug_level= +wifiduino.menu.optim.Smallest=None +wifiduino.menu.optim.Smallest.build.debug_optim=-Os +wifiduino.menu.optim.Lite=Lite +wifiduino.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +wifiduino.menu.optim.Full=Optimum +wifiduino.menu.optim.Full.build.debug_optim=-Og wifiduino.menu.lvl.SSL=SSL wifiduino.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL wifiduino.menu.lvl.TLS_MEM=TLS_MEM @@ -8181,6 +9026,10 @@ wifiduino.menu.eesz.autoflash.build.flash_size=16M wifiduino.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld wifiduino.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 wifiduino.menu.eesz.autoflash.upload.maximum_size=1044464 +wifiduino.menu.iramfloat.no=in IROM +wifiduino.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +wifiduino.menu.iramfloat.yes=allowed in ISR +wifiduino.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## wifinfo.name=WifInfo @@ -8212,6 +9061,7 @@ wifinfo.serial.disableRTS=true wifinfo.build.mcu=esp8266 wifinfo.build.core=esp8266 wifinfo.build.spiffs_pagesize=256 +wifinfo.build.debug_optim= wifinfo.build.debug_port= wifinfo.build.debug_level= wifinfo.menu.xtal.80=80 MHz @@ -8358,6 +9208,12 @@ wifinfo.menu.dbg.Serial1=Serial1 wifinfo.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 wifinfo.menu.lvl.None____=None wifinfo.menu.lvl.None____.build.debug_level= +wifinfo.menu.optim.Smallest=None +wifinfo.menu.optim.Smallest.build.debug_optim=-Os +wifinfo.menu.optim.Lite=Lite +wifinfo.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +wifinfo.menu.optim.Full=Optimum +wifinfo.menu.optim.Full.build.debug_optim=-Og wifinfo.menu.lvl.SSL=SSL wifinfo.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL wifinfo.menu.lvl.TLS_MEM=TLS_MEM @@ -8449,6 +9305,10 @@ wifinfo.menu.eesz.autoflash.build.flash_size=16M wifinfo.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld wifinfo.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 wifinfo.menu.eesz.autoflash.upload.maximum_size=1044464 +wifinfo.menu.iramfloat.no=in IROM +wifinfo.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +wifinfo.menu.iramfloat.yes=allowed in ISR +wifinfo.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM ############################################################## cw01.name=XinaBox CW01 @@ -8463,6 +9323,7 @@ cw01.serial.disableRTS=true cw01.build.mcu=esp8266 cw01.build.core=esp8266 cw01.build.spiffs_pagesize=256 +cw01.build.debug_optim= cw01.build.debug_port= cw01.build.debug_level= cw01.menu.xtal.80=80 MHz @@ -8573,6 +9434,12 @@ cw01.menu.dbg.Serial1=Serial1 cw01.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 cw01.menu.lvl.None____=None cw01.menu.lvl.None____.build.debug_level= +cw01.menu.optim.Smallest=None +cw01.menu.optim.Smallest.build.debug_optim=-Os +cw01.menu.optim.Lite=Lite +cw01.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +cw01.menu.optim.Full=Optimum +cw01.menu.optim.Full.build.debug_optim=-Og cw01.menu.lvl.SSL=SSL cw01.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL cw01.menu.lvl.TLS_MEM=TLS_MEM @@ -8664,4 +9531,8 @@ cw01.menu.eesz.autoflash.build.flash_size=16M cw01.menu.eesz.autoflash.build.flash_ld=eagle.flash.auto.ld cw01.menu.eesz.autoflash.build.extra_flags=-DFLASH_MAP_SUPPORT=1 cw01.menu.eesz.autoflash.upload.maximum_size=1044464 +cw01.menu.iramfloat.no=in IROM +cw01.menu.iramfloat.no.build.iramfloat=-DFP_IN_IROM +cw01.menu.iramfloat.yes=allowed in ISR +cw01.menu.iramfloat.yes.build.iramfloat=-DFP_IN_IRAM diff --git a/bootloaders/eboot/eboot.c b/bootloaders/eboot/eboot.c index 6e15d137b2..c3d0c278f7 100644 --- a/bootloaders/eboot/eboot.c +++ b/bootloaders/eboot/eboot.c @@ -17,8 +17,39 @@ #define SWRST do { (*((volatile uint32_t*) 0x60000700)) |= 0x80000000; } while(0); -extern void ets_wdt_enable(void); -extern void ets_wdt_disable(void); +/* + After Power Enable Pin, EXT_RST, or HWDT event, at "main()" in eboot, WDT is + disabled. Key WDT hardware registers are zero. + + After "ESP.restart()" and other soft restarts, at "main()" in eboot, WDT is enabled. + + References for the under-documented ets_wdt_* API + https://mongoose-os.com/blog/esp8266-watchdog-timer/ + http://cholla.mmto.org/esp8266/bootrom/boot.txt + + After looking at esp8266-watchdog-timer some more, `ets_wdt_enable(4, 12, 12)` + is good for eboot's needs. From a ".map" the NON-OS SDK does not use the + ets_wdt_* APIs, so our choices are not too critical. + The SDK will set up the WDT as it wants it. + + A rationale for keeping the "ets_wdt_enable()" line, if the system is not + stable during a "soft restart," the HWDT would provide a recovery reboot. +*/ +extern void ets_wdt_enable(uint32_t mode, uint32_t arg1, uint32_t arg2); +/* + "ets_wdt_disable" + + Diables WDT, then feeds the dog. + For current modes other than 1 or 2, returns the current mode. + For current mode 1, calls ets_timer_disarm, then return the current mode. + For current mode 2, calls ets_isr_mask, then return the current mode. + + I always see a return value of 0xFFFFFFFF. + + The return value would normally be used with ets_wdt_restore; however, that is + not an option since a valid prior call to ets_wdt_enable() may not have been done. +*/ +extern uint32_t ets_wdt_disable(void); int print_version(const uint32_t flash_addr) { @@ -241,12 +272,12 @@ int main() ets_wdt_disable(); res = copy_raw(cmd.args[0], cmd.args[1], cmd.args[2], false); - ets_wdt_enable(); + ets_wdt_enable(4, 12, 12); // WDT about 13 secs. ets_printf("%d\n", res); #if 0 - //devyte: this verify step below (cmp:) only works when the end of copy operation above does not overwrite the - //beginning of the image in the empty area, see #7458. Disabling for now. + //devyte: this verify step below (cmp:) only works when the end of copy operation above does not overwrite the + //beginning of the image in the empty area, see #7458. Disabling for now. //TODO: replace the below verify with hash type, crc, or similar. // Verify the copy ets_printf("cmp:"); @@ -257,7 +288,7 @@ int main() } ets_printf("%d\n", res); -#endif +#endif if (res == 0) { cmd.action = ACTION_LOAD_APP; cmd.args[0] = cmd.args[1]; diff --git a/bootloaders/eboot/eboot.elf b/bootloaders/eboot/eboot.elf index 83384d8109..0e679ff46c 100755 Binary files a/bootloaders/eboot/eboot.elf and b/bootloaders/eboot/eboot.elf differ diff --git a/cores/esp8266/Arduino.h b/cores/esp8266/Arduino.h index ee9a7964f0..60737e0195 100644 --- a/cores/esp8266/Arduino.h +++ b/cores/esp8266/Arduino.h @@ -33,6 +33,7 @@ extern "C" { #include #include +#include "umm_malloc/umm_malloc_cfgport.h" #include "stdlib_noniso.h" #include "binary.h" #include "esp8266_peri.h" @@ -269,10 +270,13 @@ long map(long, long, long, long, long); void setTZ(const char* tz); -void configTime(int timezone, int daylightOffset_sec, const char* server1, +// configure time using POSIX TZ string +// server pointers *must remain valid* for the duration of the program +void configTime(const char* tz, const char* server1, const char* server2 = nullptr, const char* server3 = nullptr); -void configTime(const char* tz, const char* server1, +// configures with approximated TZ value. part of the old api, prefer configTime with TZ variable +void configTime(int timezone, int daylightOffset_sec, const char* server1, const char* server2 = nullptr, const char* server3 = nullptr); // esp32 api compatibility @@ -290,6 +294,12 @@ bool getLocalTime(struct tm * info, uint32_t ms = 5000); #include "WCharacter.h" #include "WString.h" +// configTime wrappers for temporary server{1,2,3} strings +void configTime(int timezone, int daylightOffset_sec, String server1, + String server2 = String(), String server3 = String()); +void configTime(const char* tz, String server1, + String server2 = String(), String server3 = String()); + #include "HardwareSerial.h" #include "Esp.h" #include "Updater.h" @@ -302,7 +312,8 @@ bool getLocalTime(struct tm * info, uint32_t ms = 5000); #endif #ifdef DEBUG_ESP_OOM -// reinclude *alloc redefinition because of undefining them -// this is mandatory for allowing OOM *alloc definitions in .ino files -#include "umm_malloc/umm_malloc_cfg.h" +// Position *alloc redefinition at the end of Arduino.h because would +// have undefined them. Mandatory for supporting OOM and other debug alloc +// definitions in .ino files +#include "heap_api_debug.h" #endif diff --git a/cores/esp8266/Esp-frag.cpp b/cores/esp8266/Esp-frag.cpp index 6913179974..9e4e9af6f1 100644 --- a/cores/esp8266/Esp-frag.cpp +++ b/cores/esp8266/Esp-frag.cpp @@ -19,10 +19,10 @@ */ #include "umm_malloc/umm_malloc.h" -#include "umm_malloc/umm_malloc_cfg.h" #include "coredecls.h" #include "Esp.h" +#if defined(UMM_INFO) void EspClass::getHeapStats(uint32_t* hfree, uint32_t* hmax, uint8_t* hfrag) { // L2 / Euclidean norm of free block sizes. @@ -60,3 +60,4 @@ uint8_t EspClass::getHeapFragmentation() { return (uint8_t)umm_fragmentation_metric(); } +#endif diff --git a/cores/esp8266/Esp.cpp b/cores/esp8266/Esp.cpp index d517f6a496..67719dcfe7 100644 --- a/cores/esp8266/Esp.cpp +++ b/cores/esp8266/Esp.cpp @@ -31,6 +31,7 @@ #include "umm_malloc/umm_malloc.h" #include #include "reboot_uart_dwnld.h" +#include "hardware_reset.h" extern "C" { #include "user_interface.h" @@ -219,13 +220,15 @@ uint16_t EspClass::getVcc(void) uint32_t EspClass::getFreeHeap(void) { - return system_get_free_heap_size(); + return umm_free_heap_size_lw(); } +#if defined(UMM_INFO) uint32_t EspClass::getMaxFreeBlockSize(void) { return umm_max_block_size(); } +#endif uint32_t EspClass::getFreeContStack() { @@ -469,7 +472,7 @@ bool EspClass::checkFlashCRC() { uint32_t firstPart = (uintptr_t)&__crc_len - 0x40200000; // How many bytes to check before the 1st CRC val // Start the checksum - uint32_t crc = crc32((const void*)0x40200000, firstPart, 0xffffffff); + uint32_t crc = crc32((const void*)0x40200000, firstPart); // Pretend the 2 words of crc/len are zero to be idempotent crc = crc32(z, 8, crc); // Finish the CRC calculation over the rest of flash @@ -517,7 +520,7 @@ struct rst_info * EspClass::getResetInfoPtr(void) { } bool EspClass::eraseConfig(void) { - const size_t cfgSize = 0x4000; + const size_t cfgSize = 0x4000; // Sectors: RF_CAL + SYSTEMPARAM[3] size_t cfgAddr = ESP.getFlashChipSize() - cfgSize; for (size_t offset = 0; offset < cfgSize; offset += SPI_FLASH_SEC_SIZE) { @@ -529,6 +532,17 @@ bool EspClass::eraseConfig(void) { return true; } +bool EspClass::eraseConfigAndReset(void) { + // Before calling, ensure the WiFi state is equivalent to + // "WiFi.mode(WIFI_OFF)." This will reduce the likelihood of the SDK + // performing WiFi data writes to Flash between erasing and resetting. + bool reset = eraseConfig(); + if (reset) { + hardware_reset(); + } + return reset; +} + uint8_t *EspClass::random(uint8_t *resultArray, const size_t outputSizeBytes) { /** @@ -673,7 +687,46 @@ bool EspClass::flashEraseSector(uint32_t sector) { return rc == 0; } +// Adapted from the old version of `flash_hal_write()` (before 3.0.0), which was used for SPIFFS to allow +// writing from both unaligned u8 buffers and to an unaligned offset on flash. +// Updated version re-uses some of the code from RTOS, replacing individual methods for block & page +// writes with just a single one +// https://github.com/espressif/ESP8266_RTOS_SDK/blob/master/components/spi_flash/src/spi_flash.c +// (if necessary, we could follow the esp-idf code and introduce flash chip drivers controling more than just writing methods?) + +// This is a generic writer that does not cross page boundaries. +// Offset, data address and size *must* be 4byte aligned. +static SpiFlashOpResult spi_flash_write_page_break(uint32_t offset, uint32_t *data, size_t size) { + static constexpr uint32_t PageSize { FLASH_PAGE_SIZE }; + size_t size_page_aligned = PageSize - (offset % PageSize); + + // most common case, we don't cross a page and simply write the data + if (size < size_page_aligned) { + return spi_flash_write(offset, data, size); + } + + // otherwise, write the initial part and continue writing breaking each page interval + SpiFlashOpResult result = SPI_FLASH_RESULT_ERR; + if ((result = spi_flash_write(offset, data, size_page_aligned)) != SPI_FLASH_RESULT_OK) { + return result; + } + + const auto last_page = (size - size_page_aligned) / PageSize; + for (uint32_t page = 0; page < last_page; ++page) { + if ((result = spi_flash_write(offset + size_page_aligned, data + (size_page_aligned >> 2), PageSize)) != SPI_FLASH_RESULT_OK) { + return result; + } + + size_page_aligned += PageSize; + } + + // finally, the remaining data + return spi_flash_write(offset + size_page_aligned, data + (size_page_aligned >> 2), size - size_page_aligned); +} + #if PUYA_SUPPORT +// Special wrapper for spi_flash_write *only for PUYA flash chips* +// Already handles paging, could be used as a `spi_flash_write_page_break` replacement static SpiFlashOpResult spi_flash_write_puya(uint32_t offset, uint32_t *data, size_t size) { if (data == nullptr) { return SPI_FLASH_RESULT_ERR; @@ -720,195 +773,129 @@ static SpiFlashOpResult spi_flash_write_puya(uint32_t offset, uint32_t *data, si } #endif -bool EspClass::flashReplaceBlock(uint32_t address, const uint8_t *value, uint32_t byteCount) { - uint32_t alignedAddress = (address & ~3); - uint32_t alignmentOffset = address - alignedAddress; +static constexpr size_t Alignment { 4 }; - if (alignedAddress != ((address + byteCount - 1) & ~3)) { - // Only one 4 byte block is supported - return false; - } -#if PUYA_SUPPORT - if (getFlashChipVendorId() == SPI_FLASH_VENDOR_PUYA) { - uint8_t tempData[4] __attribute__((aligned(4))); - if (spi_flash_read(alignedAddress, (uint32_t *)tempData, 4) != SPI_FLASH_RESULT_OK) { - return false; - } - for (size_t i = 0; i < byteCount; i++) { - tempData[i + alignmentOffset] &= value[i]; - } - if (spi_flash_write(alignedAddress, (uint32_t *)tempData, 4) != SPI_FLASH_RESULT_OK) { - return false; - } - } - else -#endif // PUYA_SUPPORT - { - uint32_t tempData; - if (spi_flash_read(alignedAddress, &tempData, 4) != SPI_FLASH_RESULT_OK) { - return false; - } - memcpy((uint8_t *)&tempData + alignmentOffset, value, byteCount); - if (spi_flash_write(alignedAddress, &tempData, 4) != SPI_FLASH_RESULT_OK) { - return false; - } - } - return true; +template +static T aligned(T value) { + static constexpr auto Mask = Alignment - 1; + return (value + Mask) & ~Mask; } +template +static T alignBefore(T value) { + return aligned(value) - Alignment; +} + +static bool isAlignedAddress(uint32_t address) { + return (address & (Alignment - 1)) == 0; +} + +static bool isAlignedSize(size_t size) { + return (size & (Alignment - 1)) == 0; +} + +static bool isAlignedPointer(const uint8_t *ptr) { + return isAlignedAddress(reinterpret_cast(ptr)); +} + + size_t EspClass::flashWriteUnalignedMemory(uint32_t address, const uint8_t *data, size_t size) { - size_t sizeLeft = (size & ~3); - size_t currentOffset = 0; - // Memory is unaligned, so we need to copy it to an aligned buffer - uint32_t alignedData[FLASH_PAGE_SIZE / sizeof(uint32_t)] __attribute__((aligned(4))); - // Handle page boundary - bool pageBreak = ((address % 4) != 0) && ((address / FLASH_PAGE_SIZE) != ((address + sizeLeft - 1) / FLASH_PAGE_SIZE)); + auto flash_write = [](uint32_t address, uint8_t *data, size_t size) { + return spi_flash_write(address, reinterpret_cast(data), size) == SPI_FLASH_RESULT_OK; + }; + + auto flash_read = [](uint32_t address, uint8_t *data, size_t size) { + return spi_flash_read(address, reinterpret_cast(data), size) == SPI_FLASH_RESULT_OK; + }; + + constexpr size_t BufferSize { FLASH_PAGE_SIZE }; + alignas(alignof(uint32_t)) uint8_t buf[BufferSize]; - if (pageBreak) { - size_t byteCount = 4 - (address % 4); + size_t written = 0; - if (!flashReplaceBlock(address, data, byteCount)) { + if (!isAlignedAddress(address)) { + auto before_address = alignBefore(address); + auto offset = address - before_address; + auto wlen = std::min(Alignment - offset, size); + + if (!flash_read(before_address, &buf[0], Alignment)) { return 0; } - // We will now have aligned address, so we can cross page boundaries - currentOffset += byteCount; - // Realign size to 4 - sizeLeft = (size - byteCount) & ~3; - } - while (sizeLeft) { - size_t willCopy = std::min(sizeLeft, sizeof(alignedData)); - memcpy(alignedData, data + currentOffset, willCopy); - // We now have address, data and size aligned to 4 bytes, so we can use aligned write - if (!flashWrite(address + currentOffset, alignedData, willCopy)) - { +#if PUYA_SUPPORT + if (getFlashChipVendorId() == SPI_FLASH_VENDOR_PUYA) { + for (size_t i = 0; i < wlen ; ++i) { + buf[offset + i] &= data[i]; + } + } else { +#endif + memcpy(&buf[offset], data, wlen); +#if PUYA_SUPPORT + } +#endif + + if (!flash_write(before_address, &buf[0], Alignment)) { return 0; } - sizeLeft -= willCopy; - currentOffset += willCopy; + + address += wlen; + data += wlen; + written += wlen; + size -= wlen; } - return currentOffset; -} + while (size > 0) { + auto len = std::min(size, BufferSize); + auto wlen = aligned(len); -bool EspClass::flashWritePageBreak(uint32_t address, const uint8_t *data, size_t size) { - if (size > 4) { - return false; - } - size_t pageLeft = FLASH_PAGE_SIZE - (address % FLASH_PAGE_SIZE); - size_t offset = 0; - size_t sizeLeft = size; - if (pageLeft > 3) { - return false; - } + if (wlen != len) { + auto partial = wlen - Alignment; + if (!flash_read(address + partial, &buf[partial], Alignment)) { + return written; + } + } - if (!flashReplaceBlock(address, data, pageLeft)) { - return false; - } - offset += pageLeft; - sizeLeft -= pageLeft; - // We replaced last 4-byte block of the page, now we write the remainder in next page - if (!flashReplaceBlock(address + offset, data + offset, sizeLeft)) { - return false; + memcpy(&buf[0], data, len); + if (!flashWrite(address, reinterpret_cast(&buf[0]), wlen)) { + return written; + } + + address += len; + data += len; + written += len; + size -= len; } - return true; + + return written; } bool EspClass::flashWrite(uint32_t address, const uint32_t *data, size_t size) { - SpiFlashOpResult rc = SPI_FLASH_RESULT_OK; - bool pageBreak = ((address % 4) != 0 && (address / FLASH_PAGE_SIZE) != ((address + size - 1) / FLASH_PAGE_SIZE)); - - if ((uintptr_t)data % 4 != 0 || size % 4 != 0 || pageBreak) { - return false; - } + SpiFlashOpResult result; #if PUYA_SUPPORT if (getFlashChipVendorId() == SPI_FLASH_VENDOR_PUYA) { - rc = spi_flash_write_puya(address, const_cast(data), size); + result = spi_flash_write_puya(address, const_cast(data), size); } else -#endif // PUYA_SUPPORT +#endif { - rc = spi_flash_write(address, const_cast(data), size); + result = spi_flash_write_page_break(address, const_cast(data), size); } - return rc == SPI_FLASH_RESULT_OK; + return result == SPI_FLASH_RESULT_OK; } bool EspClass::flashWrite(uint32_t address, const uint8_t *data, size_t size) { - if (size == 0) { - return true; - } - - size_t sizeLeft = size & ~3; - size_t currentOffset = 0; - - if (sizeLeft) { - if ((uintptr_t)data % 4 != 0) { - size_t written = flashWriteUnalignedMemory(address, data, size); - if (!written) { - return false; - } - currentOffset += written; - sizeLeft -= written; - } else { - bool pageBreak = ((address % 4) != 0 && (address / FLASH_PAGE_SIZE) != ((address + sizeLeft - 1) / FLASH_PAGE_SIZE)); - - if (pageBreak) { - while (sizeLeft) { - // We cannot cross page boundary, but the write must be 4 byte aligned, - // so this is the maximum amount we can write - size_t pageBoundary = (FLASH_PAGE_SIZE - ((address + currentOffset) % FLASH_PAGE_SIZE)) & ~3; - - if (sizeLeft > pageBoundary) { - // Aligned write up to page boundary - if (!flashWrite(address + currentOffset, (uint32_t *)(data + currentOffset), pageBoundary)) { - return false; - } - currentOffset += pageBoundary; - sizeLeft -= pageBoundary; - // Cross the page boundary - if (!flashWritePageBreak(address + currentOffset, data + currentOffset, 4)) { - return false; - } - currentOffset += 4; - sizeLeft -= 4; - } else { - // We do not cross page boundary - if (!flashWrite(address + currentOffset, (uint32_t *)(data + currentOffset), sizeLeft)) { - return false; - } - currentOffset += sizeLeft; - sizeLeft = 0; - } - } - } else { - // Pointer is properly aligned and write does not cross page boundary, - // so use aligned write - if (!flashWrite(address, (uint32_t *)data, sizeLeft)) { - return false; - } - currentOffset = sizeLeft; - sizeLeft = 0; - } - } - } - sizeLeft = size - currentOffset; - if (sizeLeft > 0) { - // Size was not aligned, so we have some bytes left to write, we also need to recheck for - // page boundary crossing - bool pageBreak = ((address % 4) != 0 && (address / FLASH_PAGE_SIZE) != ((address + sizeLeft - 1) / FLASH_PAGE_SIZE)); - - if (pageBreak) { - // Cross the page boundary - if (!flashWritePageBreak(address + currentOffset, data + currentOffset, sizeLeft)) { - return false; - } - } else { - // Just write partial block - flashReplaceBlock(address + currentOffset, data + currentOffset, sizeLeft); + if (data && size) { + if (!isAlignedAddress(address) + || !isAlignedPointer(data) + || !isAlignedSize(size)) + { + return flashWriteUnalignedMemory(address, data, size) == size; } + + return flashWrite(address, reinterpret_cast(data), size); } - return true; + return false; } bool EspClass::flashRead(uint32_t address, uint8_t *data, size_t size) { @@ -916,23 +903,24 @@ bool EspClass::flashRead(uint32_t address, uint8_t *data, size_t size) { size_t currentOffset = 0; if ((uintptr_t)data % 4 != 0) { - uint32_t alignedData[FLASH_PAGE_SIZE / sizeof(uint32_t)] __attribute__((aligned(4))); + constexpr size_t BufferSize { FLASH_PAGE_SIZE / sizeof(uint32_t) }; + alignas(alignof(uint32_t)) uint32_t buf[BufferSize]; size_t sizeLeft = sizeAligned; while (sizeLeft) { - size_t willCopy = std::min(sizeLeft, sizeof(alignedData)); + size_t willCopy = std::min(sizeLeft, BufferSize); // We read to our aligned buffer and then copy to data - if (!flashRead(address + currentOffset, alignedData, willCopy)) + if (!flashRead(address + currentOffset, &buf[0], willCopy)) { return false; } - memcpy(data + currentOffset, alignedData, willCopy); + memcpy(data + currentOffset, &buf[0], willCopy); sizeLeft -= willCopy; currentOffset += willCopy; } } else { // Pointer is properly aligned, so use aligned read - if (!flashRead(address, (uint32_t *)data, sizeAligned)) { + if (!flashRead(address, reinterpret_cast(data), sizeAligned)) { return false; } currentOffset = sizeAligned; @@ -940,10 +928,10 @@ bool EspClass::flashRead(uint32_t address, uint8_t *data, size_t size) { if (currentOffset < size) { uint32_t tempData; - if (spi_flash_read(address + currentOffset, &tempData, 4) != SPI_FLASH_RESULT_OK) { + if (spi_flash_read(address + currentOffset, &tempData, sizeof(tempData)) != SPI_FLASH_RESULT_OK) { return false; } - memcpy((uint8_t *)data + currentOffset, &tempData, size - currentOffset); + memcpy(data + currentOffset, &tempData, size - currentOffset); } return true; @@ -973,7 +961,7 @@ String EspClass::getSketchMD5() md5.begin(); while( lengthLeft > 0) { size_t readBytes = (lengthLeft < bufSize) ? lengthLeft : bufSize; - if (!flashRead(offset, reinterpret_cast(buf.get()), (readBytes + 3) & ~3)) { + if (!flashRead(offset, reinterpret_cast(buf.get()), (readBytes + 3) & ~3)) { return emptyString; } md5.add(buf.get(), readBytes); diff --git a/cores/esp8266/Esp.h b/cores/esp8266/Esp.h index 773d3cd192..9cb4141292 100644 --- a/cores/esp8266/Esp.h +++ b/cores/esp8266/Esp.h @@ -103,22 +103,23 @@ class EspClass { static void reset(); static void restart(); - /** - * @brief When calling this method the ESP8266 reboots into the UART download mode without - * the need of any external wiring. This is the same mode which can also be entered by - * pulling GPIO0=low, GPIO2=high, GPIO15=low and resetting the ESP8266. - */ + /** + * @brief When calling this method the ESP8266 reboots into the UART download mode without + * the need of any external wiring. This is the same mode which can also be entered by + * pulling GPIO0=low, GPIO2=high, GPIO15=low and resetting the ESP8266. + */ [[noreturn]] static void rebootIntoUartDownloadMode(); static uint16_t getVcc(); static uint32_t getChipId(); static uint32_t getFreeHeap(); +#if defined(UMM_INFO) static uint32_t getMaxFreeBlockSize(); static uint8_t getHeapFragmentation(); // in % static void getHeapStats(uint32_t* free = nullptr, uint16_t* max = nullptr, uint8_t* frag = nullptr) __attribute__((deprecated("Use 'uint32_t*' on max, 2nd argument"))); static void getHeapStats(uint32_t* free = nullptr, uint32_t* max = nullptr, uint8_t* frag = nullptr); - +#endif static uint32_t getFreeContStack(); static void resetFreeContStack(); @@ -211,6 +212,22 @@ class EspClass { static bool eraseConfig(); + /** + * @brief Erases 4 sectors at the end of flash, 1 - RF_CAL and 3 - SYSTEMPARM. + * These are the same additional sectors that are erase when you select + * Erase Flash: "Sketch + WiFi Settings" from the Arduino IDE Tools menu. + * + * This operation erases the running SDK's flash configuration space. + * As a precaution before calling, first call "WiFi.mode(WIFI_OFF)." + * + * If you need to erase "WiFi Settings" and reboot consider using + * "ArduinoOTA.eraseConfigAndReset()" it handles shutting down WiFi + * before the erase. + * @return bool result of operation. Always False on return. + * Function does not return on success. + */ + static bool eraseConfigAndReset(); + static uint8_t *random(uint8_t *resultArray, const size_t outputSizeBytes); static uint32_t random(); @@ -252,39 +269,18 @@ class EspClass { */ static void resetHeap(); private: - /** - * @brief Replaces @a byteCount bytes of a 4 byte block on flash - * - * @param address flash address - * @param value buffer with data - * @param byteCount number of bytes to replace - * @return bool result of operation - * @retval true success - * @retval false failed to read/write or invalid args - */ - static bool flashReplaceBlock(uint32_t address, const uint8_t *value, uint32_t byteCount); /** * @brief Write up to @a size bytes from @a data to flash at @a address - * This function takes case of unaligned memory access by copying @a data to a temporary buffer, - * it also takes care of page boundary crossing see @a flashWritePageBreak as to why it's done. - * Less than @a size bytes may be written, due to 4 byte alignment requirement of spi_flash_write + * This function handles all cases of unaligned memory acccess; when either + * address is not aligned, data pointer is not aligned or size is not a multiple of 4. + * User of this function should note that @a data will be copied into a buffer allocated on stack. + * * @param address address on flash where write should start * @param data input buffer * @param size amount of data * @return size_t amount of data written, 0 on failure */ static size_t flashWriteUnalignedMemory(uint32_t address, const uint8_t *data, size_t size); - /** - * @brief Splits up to 4 bytes into 4 byte blocks and writes them to flash - * We need this since spi_flash_write cannot handle writing over a page boundary with unaligned offset - * i.e. spi_flash_write(254, data, 4) will fail, also we cannot write less bytes as in - * spi_flash_write(254, data, 2) since it will be extended internally to 4 bytes and fail - * @param address start of write - * @param data data to be written - * @param size amount of data, must be < 4 - * @return bool result of operation - */ - static bool flashWritePageBreak(uint32_t address, const uint8_t *data, size_t size); }; extern EspClass ESP; diff --git a/cores/esp8266/FS.cpp b/cores/esp8266/FS.cpp index d9e4209c0e..edfc1f8b49 100644 --- a/cores/esp8266/FS.cpp +++ b/cores/esp8266/FS.cpp @@ -173,18 +173,15 @@ File File::openNextFile() { return _fakeDir->openFile("r"); } -String File::readString() -{ +String File::readString() { String ret; ret.reserve(size() - position()); - char temp[256+1]; - int countRead = readBytes(temp, sizeof(temp)-1); - while (countRead > 0) - { - temp[countRead] = 0; - ret += temp; - countRead = readBytes(temp, sizeof(temp)-1); - } + uint8_t temp[256]; + int countRead; + do { + countRead = read(temp, sizeof(temp)); + ret.concat((const char*)temp, countRead); + } while (countRead > 0); return ret; } diff --git a/cores/esp8266/FS.h b/cores/esp8266/FS.h index 7099321077..55305e9688 100644 --- a/cores/esp8266/FS.h +++ b/cores/esp8266/FS.h @@ -90,9 +90,8 @@ class File : public Stream uint8_t obuf[256]; size_t doneLen = 0; size_t sentLen; - int i; - while (src.available() > sizeof(obuf)){ + while (src.available() > (int)sizeof(obuf)){ src.read(obuf, sizeof(obuf)); sentLen = write(obuf, sizeof(obuf)); doneLen = doneLen + sentLen; diff --git a/cores/esp8266/IPAddress.cpp b/cores/esp8266/IPAddress.cpp index c269d00506..6f68bc56e9 100644 --- a/cores/esp8266/IPAddress.cpp +++ b/cores/esp8266/IPAddress.cpp @@ -27,6 +27,11 @@ IPAddress::IPAddress(const IPAddress& from) ip_addr_copy(_ip, from._ip); } +IPAddress::IPAddress(IPAddress&& from) +{ + ip_addr_copy(_ip, from._ip); +} + IPAddress::IPAddress() { _ip = *IP_ANY_TYPE; // lwIP's v4-or-v6 generic address } @@ -36,24 +41,14 @@ bool IPAddress::isSet () const { } IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet) { - setV4(); - (*this)[0] = first_octet; - (*this)[1] = second_octet; - (*this)[2] = third_octet; - (*this)[3] = fourth_octet; -} + uint8_t addr[] { + first_octet, + second_octet, + third_octet, + fourth_octet, + }; -void IPAddress::ctor32(uint32_t address) { - setV4(); - v4() = address; -} - -IPAddress::IPAddress(const uint8_t *address) { - setV4(); - (*this)[0] = address[0]; - (*this)[1] = address[1]; - (*this)[2] = address[2]; - (*this)[3] = address[3]; + *this = &addr[0]; } bool IPAddress::fromString(const char *address) { @@ -111,8 +106,10 @@ bool IPAddress::fromString4(const char *address) { } IPAddress& IPAddress::operator=(const uint8_t *address) { - setV4(); - v4() = *reinterpret_cast(address); + uint32_t value; + memcpy_P(&value, address, sizeof(value)); + + *this = value; return *this; } @@ -123,7 +120,14 @@ IPAddress& IPAddress::operator=(uint32_t address) { } bool IPAddress::operator==(const uint8_t* addr) const { - return isV4() && v4() == *reinterpret_cast(addr); + if (!isV4()) { + return false; + } + + uint32_t value; + memcpy_P(&value, addr, sizeof(value)); + + return v4() == value; } size_t IPAddress::printTo(Print& p) const { diff --git a/cores/esp8266/IPAddress.h b/cores/esp8266/IPAddress.h index c070118889..99419b92a9 100644 --- a/cores/esp8266/IPAddress.h +++ b/cores/esp8266/IPAddress.h @@ -61,17 +61,16 @@ class IPAddress: public Printable { return reinterpret_cast(&v4()); } - void ctor32 (uint32_t); - public: - // Constructors IPAddress(); - IPAddress(const IPAddress& from); + IPAddress(const IPAddress&); + IPAddress(IPAddress&&); + IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet); - IPAddress(uint32_t address) { ctor32(address); } - IPAddress(u32_t address) { ctor32(address); } - IPAddress(int address) { ctor32(address); } - IPAddress(const uint8_t *address); + IPAddress(uint32_t address) { *this = address; } + IPAddress(unsigned long address) { *this = address; } + IPAddress(int address) { *this = address; } + IPAddress(const uint8_t *address) { *this = address; } bool fromString(const char *address); bool fromString(const String &address) { return fromString(address.c_str()); } @@ -80,16 +79,14 @@ class IPAddress: public Printable { // to a four-byte uint8_t array is expected operator uint32_t() const { return isV4()? v4(): (uint32_t)0; } operator uint32_t() { return isV4()? v4(): (uint32_t)0; } - operator u32_t() const { return isV4()? v4(): (u32_t)0; } - operator u32_t() { return isV4()? v4(): (u32_t)0; } bool isSet () const; operator bool () const { return isSet(); } // <- operator bool () { return isSet(); } // <- both are needed // generic IPv4 wrapper to uint32-view like arduino loves to see it - const u32_t& v4() const { return ip_2_ip4(&_ip)->addr; } // for raw_address(const) - u32_t& v4() { return ip_2_ip4(&_ip)->addr; } + const uint32_t& v4() const { return ip_2_ip4(&_ip)->addr; } + uint32_t& v4() { return ip_2_ip4(&_ip)->addr; } bool operator==(const IPAddress& addr) const { return ip_addr_cmp(&_ip, &addr._ip); @@ -100,14 +97,14 @@ class IPAddress: public Printable { bool operator==(uint32_t addr) const { return isV4() && v4() == addr; } - bool operator==(u32_t addr) const { - return isV4() && v4() == addr; + bool operator==(unsigned long addr) const { + return isV4() && v4() == (uint32_t)addr; } bool operator!=(uint32_t addr) const { return !(isV4() && v4() == addr); } - bool operator!=(u32_t addr) const { - return !(isV4() && v4() == addr); + bool operator!=(unsigned long addr) const { + return isV4() && v4() != (uint32_t)addr; } bool operator==(const uint8_t* addr) const; @@ -117,11 +114,18 @@ class IPAddress: public Printable { // Overloaded index operator to allow getting and setting individual octets of the address uint8_t operator[](int index) const { - return isV4()? *(raw_address() + index): 0; + if (!isV4()) { + return 0; + } + + return ip4_addr_get_byte_val(*ip_2_ip4(&_ip), index); } + uint8_t& operator[](int index) { setV4(); - return *(raw_address() + index); + + uint8_t* ptr = reinterpret_cast(&v4()); + return *(ptr + index); } // Overloaded copy operators to allow initialisation of IPAddress objects from other types diff --git a/cores/esp8266/LwipDhcpServer-NonOS.cpp b/cores/esp8266/LwipDhcpServer-NonOS.cpp index aee22b6d5c..a5324ec5d5 100644 --- a/cores/esp8266/LwipDhcpServer-NonOS.cpp +++ b/cores/esp8266/LwipDhcpServer-NonOS.cpp @@ -1,63 +1,97 @@ /* - lwIPDhcpServer-NonOS.cpp - DHCP server wrapper + NonOS DHCP server helpers - Copyright (c) 2020 esp8266 arduino. All rights reserved. + Copyright (c) 2020-2022 esp8266 arduino. All rights reserved. This file is part of the esp8266 core for Arduino environment. - 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -// STARTS/STOPS DHCP SERVER ON WIFI AP INTERFACE -// these functions must exists as-is with "C" interface, -// nonos-sdk calls them at boot time and later - -#include // LWIP_VERSION +#include "LwipDhcpServer-NonOS.h" +#include #include -#include "LwipDhcpServer.h" -extern netif netif_git[2]; - -// global DHCP instance for softAP interface -DhcpServer dhcpSoftAP(&netif_git[SOFTAP_IF]); +// Global static DHCP instance for softAP interface +// (since the netif object never goes away, even when AP is disabled) +// Initial version fully emulates nonos-sdk api in DhcpServer class, +// before trying to further change it and possibly break legacy behaviour +DhcpServer& getNonOSDhcpServer() +{ + extern netif netif_git[2]; + static DhcpServer server(&netif_git[SOFTAP_IF]); + return server; +} extern "C" { - void dhcps_start(struct ip_info* info, netif* apnetif) - { - // apnetif is esp interface, replaced by lwip2's - // netif_git[SOFTAP_IF] interface in constructor - (void)apnetif; - -#if 0 - // can't use C++ now, global ctors are not initialized yet - dhcpSoftAP.begin(info); -#else - (void)info; - // initial version: emulate nonos-sdk in DhcpServer class before - // trying to change legacy behavor - // `fw_has_started_softap_dhcps` will be read in DhcpServer::DhcpServer - // which is called when c++ ctors are initialized, specifically - // dhcpSoftAP initialized with AP interface number above. - fw_has_started_softap_dhcps = 1; -#endif + // `ip_info` is useless, since we get the information from the netif directly + // `netif` would be netif_git[SOFTAP_IF], which we get from the lwip2 glue + void dhcps_start(ip_info*, netif*) + { + auto& server = getNonOSDhcpServer(); + if (!server.isRunning()) + { + server.begin(); + } } void dhcps_stop() { - dhcpSoftAP.end(); + auto& server = getNonOSDhcpServer(); + if (server.isRunning()) + { + server.end(); + } + } + + // providing the rest of the nonos-sdk API, which was originally removed in 3.0.0 + + bool wifi_softap_set_dhcps_lease(dhcps_lease* please) + { + auto& server = getNonOSDhcpServer(); + return server.set_dhcps_lease(please); + } + + bool wifi_softap_get_dhcps_lease(dhcps_lease* please) + { + auto& server = getNonOSDhcpServer(); + return server.get_dhcps_lease(please); + } + + uint32 wifi_softap_get_dhcps_lease_time() + { + auto& server = getNonOSDhcpServer(); + return server.getLeaseTime(); + } + + bool wifi_softap_set_dhcps_lease_time(uint32 minutes) + { + auto& server = getNonOSDhcpServer(); + server.setLeaseTime(minutes); + return true; + } + + bool wifi_softap_reset_dhcps_lease_time() + { + auto& server = getNonOSDhcpServer(); + server.resetLeaseTime(); + return true; + } + + bool wifi_softap_add_dhcps_lease(uint8* macaddr) + { + auto& server = getNonOSDhcpServer(); + return server.add_dhcps_lease(macaddr); } } // extern "C" diff --git a/cores/esp8266/LwipDhcpServer-NonOS.h b/cores/esp8266/LwipDhcpServer-NonOS.h new file mode 100644 index 0000000000..4da4eca1b3 --- /dev/null +++ b/cores/esp8266/LwipDhcpServer-NonOS.h @@ -0,0 +1,24 @@ +/* + NonOS DHCP server helpers + + Copyright (c) 2020-2022 esp8266 arduino. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#pragma once + +#include "LwipDhcpServer.h" + +// Global static DHCP instance for softAP interface +DhcpServer& getNonOSDhcpServer(); diff --git a/cores/esp8266/LwipDhcpServer.cpp b/cores/esp8266/LwipDhcpServer.cpp index 07270bb6c2..0f5d2bcf32 100644 --- a/cores/esp8266/LwipDhcpServer.cpp +++ b/cores/esp8266/LwipDhcpServer.cpp @@ -36,8 +36,6 @@ #include // LWIP_VERSION -#define DHCPS_LEASE_TIME_DEF (120) - #define USE_DNS #include "lwip/inet.h" @@ -52,6 +50,9 @@ #include "user_interface.h" #include "mem.h" +#include +#include + typedef struct dhcps_state { sint16_t state; @@ -80,12 +81,6 @@ struct dhcps_lease struct ipv4_addr end_ip; }; -enum dhcps_offer_option -{ - OFFER_START = 0x00, - OFFER_ROUTER = 0x01, - OFFER_END -}; #endif typedef enum @@ -109,7 +104,6 @@ struct dhcps_pool dhcps_state_t state; }; -#define DHCPS_LEASE_TIMER dhcps_lease_time // 0x05A0 #define DHCPS_MAX_LEASE 0x64 #define BOOTP_BROADCAST 0x8000 @@ -122,13 +116,13 @@ struct dhcps_pool #define DHCPS_SERVER_PORT 67 #define DHCPS_CLIENT_PORT 68 -#define DHCPDISCOVER 1 -#define DHCPOFFER 2 -#define DHCPREQUEST 3 -#define DHCPDECLINE 4 -#define DHCPACK 5 -#define DHCPNAK 6 -#define DHCPRELEASE 7 +static constexpr uint8_t DHCPDISCOVER = 1; +static constexpr uint8_t DHCPOFFER = 2; +static constexpr uint8_t DHCPREQUEST = 3; +static constexpr uint8_t DHCPDECLINE = 4; +static constexpr uint8_t DHCPACK = 5; +static constexpr uint8_t DHCPNAK = 6; +static constexpr uint8_t DHCPRELEASE = 7; #define DHCP_OPTION_SUBNET_MASK 1 #define DHCP_OPTION_ROUTER 3 @@ -154,8 +148,6 @@ struct dhcps_pool #define DHCPS_STATE_IDLE 5 #define DHCPS_STATE_RELEASE 6 -#define dhcps_router_enabled(offer) ((offer & OFFER_ROUTER) != 0) - #ifdef MEMLEAK_DEBUG const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; #endif @@ -166,7 +158,7 @@ const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; int ret = 1, errval = (err); \ if (errval != ERR_OK) \ { \ - os_printf("DHCPS ERROR: %s (lwip:%d)\n", what, errval); \ + os_printf("DHCPS ERROR: %s (lwip:%s(%d))\n", what, lwip_strerr(errval), errval); \ ret = 0; \ } \ ret; \ @@ -175,49 +167,37 @@ const char mem_debug_file[] ICACHE_RODATA_ATTR = __FILE__; #define LWIP_IS_OK(what, err) ((err) == ERR_OK) #endif -const uint32 DhcpServer::magic_cookie = 0x63538263; // https://tools.ietf.org/html/rfc1497 - -int fw_has_started_softap_dhcps = 0; - //////////////////////////////////////////////////////////////////////////////////// -DhcpServer::DhcpServer(netif* netif) : _netif(netif) +DhcpServer::OptionsBuffer& DhcpServer::OptionsBuffer::add(uint8_t code, const uint8_t* data, + size_t size) { - pcb_dhcps = nullptr; - dns_address.addr = 0; - plist = nullptr; - offer = 0xFF; - renew = false; - dhcps_lease_time = DHCPS_LEASE_TIME_DEF; // minute - - if (netif->num == SOFTAP_IF && fw_has_started_softap_dhcps == 1) + if (size >= UINT8_MAX) { - // When nonos-sdk starts DHCPS at boot: - // 1. `fw_has_started_softap_dhcps` is already initialized to 1 - // 2. global ctor DhcpServer's `dhcpSoftAP(&netif_git[SOFTAP_IF])` is called - // 3. (that's here) => begin(legacy-values) is called - ip_info ip = { - { 0x0104a8c0 }, // IP 192.168.4.1 - { 0x00ffffff }, // netmask 255.255.255.0 - { 0 } // gateway 0.0.0.0 - }; - begin(&ip); - fw_has_started_softap_dhcps = 2; // not 1, ending initial boot sequence + return *this; } -}; -// wifi_softap_set_station_info is missing in user_interface.h: -extern "C" void wifi_softap_set_station_info(uint8_t* mac, struct ipv4_addr*); - -void DhcpServer::dhcps_set_dns(int num, const ipv4_addr_t* dns) -{ - (void)num; - if (!ip4_addr_isany(dns)) + if ((size_t)(_end - _it) < (size + 2)) { - ip4_addr_copy(dns_address, *dns); + return *this; } + + *_it++ = code; + *_it++ = size; + + memcpy_P(_it, data, size); + _it += size; + + return *this; } +//////////////////////////////////////////////////////////////////////////////////// + +DhcpServer::DhcpServer(netif* netif) : _netif(netif) { } + +// wifi_softap_set_station_info is missing in user_interface.h: +extern "C" void wifi_softap_set_station_info(uint8_t* mac, struct ipv4_addr*); + /****************************************************************************** FunctionName : node_insert_to_list Description : insert the node to the list @@ -315,8 +295,8 @@ bool DhcpServer::add_dhcps_lease(uint8* macaddr) struct dhcps_pool* pdhcps_pool = nullptr; list_node* pback_node = nullptr; - uint32 start_ip = dhcps_lease.start_ip.addr; - uint32 end_ip = dhcps_lease.end_ip.addr; + uint32 start_ip = lease.start_ip.addr; + uint32 end_ip = lease.end_ip.addr; for (pback_node = plist; pback_node != nullptr; pback_node = pback_node->pnext) { @@ -345,7 +325,7 @@ bool DhcpServer::add_dhcps_lease(uint8* macaddr) pdhcps_pool = (struct dhcps_pool*)zalloc(sizeof(struct dhcps_pool)); pdhcps_pool->ip.addr = start_ip; memcpy(pdhcps_pool->mac, macaddr, sizeof(pdhcps_pool->mac)); - pdhcps_pool->lease_timer = DHCPS_LEASE_TIMER; + pdhcps_pool->lease_timer = this->lease_time; pdhcps_pool->type = DHCPS_TYPE_STATIC; pdhcps_pool->state = DHCPS_STATE_ONLINE; pback_node = (list_node*)zalloc(sizeof(list_node)); @@ -358,145 +338,50 @@ bool DhcpServer::add_dhcps_lease(uint8* macaddr) /////////////////////////////////////////////////////////////////////////////////// /* - DHCP msg + Set DHCP msg offer options for the given server - @param optptr -- DHCP msg - @param type -- option - - @return uint8_t* DHCP msg + @param buffer -- DHCP options buffer + @param server -- DHCP server instance */ /////////////////////////////////////////////////////////////////////////////////// -uint8_t* DhcpServer::add_msg_type(uint8_t* optptr, uint8_t type) +void DhcpServer::add_offer_options(OptionsBuffer& options) { - *optptr++ = DHCP_OPTION_MSG_TYPE; - *optptr++ = 1; - *optptr++ = type; - return optptr; -} -/////////////////////////////////////////////////////////////////////////////////// -/* - DHCP msg offer + options.add(DHCP_OPTION_SUBNET_MASK, ip_2_ip4(&_netif->netmask)) + .add(DHCP_OPTION_SERVER_ID, ip_2_ip4(&_netif->ip_addr)); - @param optptr -- DHCP msg + // option units are seconds, while server opt is minutes + const uint32_t lease_time_seconds = lease_time * 60; + options.add(DHCP_OPTION_LEASE_TIME, lease_time_seconds); - @return uint8_t* DHCP msg -*/ -/////////////////////////////////////////////////////////////////////////////////// -uint8_t* DhcpServer::add_offer_options(uint8_t* optptr) -{ - // struct ipv4_addr ipadd; - // ipadd.addr = server_address.addr; -#define ipadd (_netif->ip_addr) - - // struct ip_info if_ip; - // bzero(&if_ip, sizeof(struct ip_info)); - // wifi_get_ip_info(SOFTAP_IF, &if_ip); -#define if_ip (*_netif) - - *optptr++ = DHCP_OPTION_SUBNET_MASK; - *optptr++ = 4; - *optptr++ = ip4_addr1(ip_2_ip4(&if_ip.netmask)); - *optptr++ = ip4_addr2(ip_2_ip4(&if_ip.netmask)); - *optptr++ = ip4_addr3(ip_2_ip4(&if_ip.netmask)); - *optptr++ = ip4_addr4(ip_2_ip4(&if_ip.netmask)); - - *optptr++ = DHCP_OPTION_LEASE_TIME; - *optptr++ = 4; - *optptr++ = ((DHCPS_LEASE_TIMER * 60) >> 24) & 0xFF; - *optptr++ = ((DHCPS_LEASE_TIMER * 60) >> 16) & 0xFF; - *optptr++ = ((DHCPS_LEASE_TIMER * 60) >> 8) & 0xFF; - *optptr++ = ((DHCPS_LEASE_TIMER * 60) >> 0) & 0xFF; - - *optptr++ = DHCP_OPTION_SERVER_ID; - *optptr++ = 4; - *optptr++ = ip4_addr1(ip_2_ip4(&ipadd)); - *optptr++ = ip4_addr2(ip_2_ip4(&ipadd)); - *optptr++ = ip4_addr3(ip_2_ip4(&ipadd)); - *optptr++ = ip4_addr4(ip_2_ip4(&ipadd)); - - if (dhcps_router_enabled(offer) && ip_2_ip4(&if_ip.gw)->addr) + if (offer_router && !ip4_addr_isany_val(*ip_2_ip4(&_netif->gw))) { - *optptr++ = DHCP_OPTION_ROUTER; - *optptr++ = 4; - *optptr++ = ip4_addr1(ip_2_ip4(&if_ip.gw)); - *optptr++ = ip4_addr2(ip_2_ip4(&if_ip.gw)); - *optptr++ = ip4_addr3(ip_2_ip4(&if_ip.gw)); - *optptr++ = ip4_addr4(ip_2_ip4(&if_ip.gw)); + options.add(DHCP_OPTION_ROUTER, ip_2_ip4(&_netif->gw)); } #ifdef USE_DNS - *optptr++ = DHCP_OPTION_DNS_SERVER; - *optptr++ = 4; - if (dns_address.addr == 0) - { - *optptr++ = ip4_addr1(ip_2_ip4(&ipadd)); - *optptr++ = ip4_addr2(ip_2_ip4(&ipadd)); - *optptr++ = ip4_addr3(ip_2_ip4(&ipadd)); - *optptr++ = ip4_addr4(ip_2_ip4(&ipadd)); - } - else - { - *optptr++ = ip4_addr1(&dns_address); - *optptr++ = ip4_addr2(&dns_address); - *optptr++ = ip4_addr3(&dns_address); - *optptr++ = ip4_addr4(&dns_address); - } + options.add(DHCP_OPTION_DNS_SERVER, + !ip4_addr_isany_val(dns_address) ? &dns_address : ip_2_ip4(&_netif->ip_addr)); #endif - *optptr++ = DHCP_OPTION_BROADCAST_ADDRESS; - *optptr++ = 4; - // XXXFIXME do better than that, we have netmask - *optptr++ = ip4_addr1(ip_2_ip4(&ipadd)); - *optptr++ = ip4_addr2(ip_2_ip4(&ipadd)); - *optptr++ = ip4_addr3(ip_2_ip4(&ipadd)); - *optptr++ = 255; - - *optptr++ = DHCP_OPTION_INTERFACE_MTU; - *optptr++ = 2; - *optptr++ = 0x05; - *optptr++ = 0xdc; // 1500 - - *optptr++ = DHCP_OPTION_PERFORM_ROUTER_DISCOVERY; - *optptr++ = 1; - *optptr++ = 0x00; - -#if 0 // vendor specific uninitialized (??) - *optptr++ = 43; // vendor specific - *optptr++ = 6; - // uninitialized ? -#endif + { + const auto* addr = ip_2_ip4(&_netif->ip_addr); + const auto* mask = ip_2_ip4(&_netif->netmask); + const auto broadcast = ip4_addr_t { .addr = (addr->addr & mask->addr) | ~mask->addr }; -#if 0 // already set (DHCP_OPTION_SUBNET_MASK==1) (??) - *optptr++ = 0x01; - *optptr++ = 4; - *optptr++ = 0; - *optptr++ = 0; - *optptr++ = 0; - *optptr++ = 2; -#endif + options.add(DHCP_OPTION_BROADCAST_ADDRESS, &broadcast); + } - return optptr; + // TODO: _netif->mtu ? + static constexpr uint16_t Mtu { 1500 }; + options.add(DHCP_OPTION_INTERFACE_MTU, Mtu); -#undef ipadd -#undef if_ip + static constexpr uint8_t RouterDiscovery { 0 }; + options.add(DHCP_OPTION_PERFORM_ROUTER_DISCOVERY, RouterDiscovery); } -/////////////////////////////////////////////////////////////////////////////////// -/* - DHCP msg - @param optptr -- DHCP msg - - @return uint8_t* DHCP msg -*/ /////////////////////////////////////////////////////////////////////////////////// -uint8_t* DhcpServer::add_end(uint8_t* optptr) -{ - *optptr++ = DHCP_OPTION_END; - return optptr; -} /////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////// -void DhcpServer::create_msg(struct dhcps_msg* m) +DhcpServer::OptionsBuffer DhcpServer::create_msg(struct dhcps_msg* m) { struct ipv4_addr client; @@ -516,7 +401,9 @@ void DhcpServer::create_msg(struct dhcps_msg* m) memset((char*)m->sname, 0, sizeof(m->sname)); memset((char*)m->file, 0, sizeof(m->file)); memset((char*)m->options, 0, sizeof(m->options)); - memcpy((char*)m->options, &magic_cookie, sizeof(magic_cookie)); + memcpy((char*)m->options, &MagicCookie, sizeof(MagicCookie)); + + return { &m->options[sizeof(magic_cookie)], std::end(m->options) }; } /////////////////////////////////////////////////////////////////////////////////// /* @@ -527,16 +414,18 @@ void DhcpServer::create_msg(struct dhcps_msg* m) /////////////////////////////////////////////////////////////////////////////////// void DhcpServer::send_offer(struct dhcps_msg* m) { - uint8_t* end; - struct pbuf *p, *q; - u8_t* data; - u16_t cnt = 0; - u16_t i; - create_msg(m); + struct pbuf* p; + + auto options = create_msg(m); + options.add(DHCP_OPTION_MSG_TYPE, DHCPOFFER); + + add_offer_options(options); + if (custom_offer_options) + { + custom_offer_options(*this, options); + } - end = add_msg_type(&m->options[4], DHCPOFFER); - end = add_offer_options(end); - end = add_end(end); + options.add(DHCP_OPTION_END); p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcps_msg), PBUF_RAM); #if DHCPS_DEBUG @@ -549,17 +438,7 @@ void DhcpServer::send_offer(struct dhcps_msg* m) os_printf("dhcps: send_offer>>p->tot_len = %d\n", p->tot_len); os_printf("dhcps: send_offer>>p->len = %d\n", p->len); #endif - q = p; - while (q != nullptr) - { - data = (u8_t*)q->payload; - for (i = 0; i < q->len; i++) - { - data[i] = ((u8_t*)m)[cnt++]; - } - - q = q->next; - } + pbuf_take(p, m, sizeof(struct dhcps_msg)); } else { @@ -591,15 +470,11 @@ void DhcpServer::send_offer(struct dhcps_msg* m) /////////////////////////////////////////////////////////////////////////////////// void DhcpServer::send_nak(struct dhcps_msg* m) { - u8_t* end; - struct pbuf *p, *q; - u8_t* data; - u16_t cnt = 0; - u16_t i; - create_msg(m); + struct pbuf* p; - end = add_msg_type(&m->options[4], DHCPNAK); - end = add_end(end); + auto options = create_msg(m); + options.add(DHCP_OPTION_MSG_TYPE, DHCPNAK); + options.add(DHCP_OPTION_END); p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcps_msg), PBUF_RAM); #if DHCPS_DEBUG @@ -612,17 +487,7 @@ void DhcpServer::send_nak(struct dhcps_msg* m) os_printf("dhcps: send_nak>>p->tot_len = %d\n", p->tot_len); os_printf("dhcps: send_nak>>p->len = %d\n", p->len); #endif - q = p; - while (q != nullptr) - { - data = (u8_t*)q->payload; - for (i = 0; i < q->len; i++) - { - data[i] = ((u8_t*)m)[cnt++]; - } - - q = q->next; - } + pbuf_take(p, m, sizeof(struct dhcps_msg)); } else { @@ -649,16 +514,18 @@ void DhcpServer::send_nak(struct dhcps_msg* m) /////////////////////////////////////////////////////////////////////////////////// void DhcpServer::send_ack(struct dhcps_msg* m) { - u8_t* end; - struct pbuf *p, *q; - u8_t* data; - u16_t cnt = 0; - u16_t i; - create_msg(m); + struct pbuf* p; + + auto options = create_msg(m); + options.add(DHCP_OPTION_MSG_TYPE, DHCPACK); + + add_offer_options(options); + if (custom_offer_options) + { + custom_offer_options(*this, options); + } - end = add_msg_type(&m->options[4], DHCPACK); - end = add_offer_options(end); - end = add_end(end); + options.add(DHCP_OPTION_END); p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcps_msg), PBUF_RAM); #if DHCPS_DEBUG @@ -671,17 +538,7 @@ void DhcpServer::send_ack(struct dhcps_msg* m) os_printf("dhcps: send_ack>>p->tot_len = %d\n", p->tot_len); os_printf("dhcps: send_ack>>p->len = %d\n", p->len); #endif - q = p; - while (q != nullptr) - { - data = (u8_t*)q->payload; - for (i = 0; i < q->len; i++) - { - data[i] = ((u8_t*)m)[cnt++]; - } - - q = q->next; - } + pbuf_take(p, m, sizeof(struct dhcps_msg)); } else { @@ -821,7 +678,7 @@ uint8_t DhcpServer::parse_options(uint8_t* optptr, sint16_t len) /////////////////////////////////////////////////////////////////////////////////// sint16_t DhcpServer::parse_msg(struct dhcps_msg* m, u16_t len) { - if (memcmp((char*)m->options, &magic_cookie, sizeof(magic_cookie)) == 0) + if (memcmp((char*)m->options, &MagicCookie, sizeof(MagicCookie)) == 0) { struct ipv4_addr ip; memcpy(&ip.addr, m->ciaddr, sizeof(ip.addr)); @@ -960,15 +817,15 @@ void DhcpServer::init_dhcps_lease(uint32 ip) uint32 softap_ip = 0, local_ip = 0; uint32 start_ip = 0; uint32 end_ip = 0; - if (dhcps_lease.enable == true) + if (lease.enable == true) { softap_ip = htonl(ip); - start_ip = htonl(dhcps_lease.start_ip.addr); - end_ip = htonl(dhcps_lease.end_ip.addr); + start_ip = htonl(lease.start_ip.addr); + end_ip = htonl(lease.end_ip.addr); /*config ip information can't contain local ip*/ if ((start_ip <= softap_ip) && (softap_ip <= end_ip)) { - dhcps_lease.enable = false; + lease.enable = false; } else { @@ -977,12 +834,12 @@ void DhcpServer::init_dhcps_lease(uint32 ip) if (((start_ip >> 8 != softap_ip) || (end_ip >> 8 != softap_ip)) || (end_ip - start_ip > DHCPS_MAX_LEASE)) { - dhcps_lease.enable = false; + lease.enable = false; } } } - if (dhcps_lease.enable == false) + if (lease.enable == false) { local_ip = softap_ip = htonl(ip); softap_ip &= 0xFFFFFF00; @@ -996,19 +853,19 @@ void DhcpServer::init_dhcps_lease(uint32 ip) local_ip++; } - bzero(&dhcps_lease, sizeof(dhcps_lease)); - dhcps_lease.start_ip.addr = softap_ip | local_ip; - dhcps_lease.end_ip.addr = softap_ip | (local_ip + DHCPS_MAX_LEASE - 1); - dhcps_lease.start_ip.addr = htonl(dhcps_lease.start_ip.addr); - dhcps_lease.end_ip.addr = htonl(dhcps_lease.end_ip.addr); + bzero(&lease, sizeof(lease)); + lease.start_ip.addr = softap_ip | local_ip; + lease.end_ip.addr = softap_ip | (local_ip + DHCPS_MAX_LEASE - 1); + lease.start_ip.addr = htonl(lease.start_ip.addr); + lease.end_ip.addr = htonl(lease.end_ip.addr); } - // dhcps_lease.start_ip.addr = htonl(dhcps_lease.start_ip.addr); - // dhcps_lease.end_ip.addr= htonl(dhcps_lease.end_ip.addr); - // os_printf("start_ip = 0x%x, end_ip = 0x%x\n",dhcps_lease.start_ip, dhcps_lease.end_ip); + // lease.start_ip.addr = htonl(lease.start_ip.addr); + // lease.end_ip.addr= htonl(lease.end_ip.addr); + // os_printf("start_ip = 0x%x, end_ip = 0x%x\n",lease.start_ip, lease.end_ip); } /////////////////////////////////////////////////////////////////////////////////// -bool DhcpServer::begin(struct ip_info* info) +bool DhcpServer::begin() { if (pcb_dhcps != nullptr) { @@ -1016,9 +873,11 @@ bool DhcpServer::begin(struct ip_info* info) } pcb_dhcps = udp_new(); - if (pcb_dhcps == nullptr || info == nullptr) + if (pcb_dhcps == nullptr) { +#if DHCPS_DEBUG os_printf("dhcps_start(): could not obtain pcb\n"); +#endif return false; } @@ -1030,7 +889,7 @@ bool DhcpServer::begin(struct ip_info* info) ip_2_ip4(&broadcast_dhcps)->addr |= ~ip_2_ip4(&_netif->netmask)->addr; // XXXFIXMEIPV6 broadcast address? - server_address = info->ip; + server_address = *ip_2_ip4(&_netif->ip_addr); init_dhcps_lease(server_address.addr); udp_bind(pcb_dhcps, IP_ADDR_ANY, DHCPS_SERVER_PORT); @@ -1040,12 +899,6 @@ bool DhcpServer::begin(struct ip_info* info) "pcb_dhcps\n"); #endif - if (_netif->num == SOFTAP_IF) - { - wifi_set_ip_info(SOFTAP_IF, info); // added for lwip-git, not sure whether useful - } - _netif->flags |= NETIF_FLAG_UP | NETIF_FLAG_LINK_UP; // added for lwip-git - return true; } @@ -1091,9 +944,9 @@ void DhcpServer::end() } } -bool DhcpServer::isRunning() +bool DhcpServer::isRunning() const { - return !!_netif->state; + return pcb_dhcps != nullptr; } /****************************************************************************** @@ -1149,13 +1002,13 @@ bool DhcpServer::set_dhcps_lease(struct dhcps_lease* please) return false; } - bzero(&dhcps_lease, sizeof(dhcps_lease)); - // dhcps_lease.start_ip.addr = start_ip; - // dhcps_lease.end_ip.addr = end_ip; - dhcps_lease.start_ip.addr = please->start_ip.addr; - dhcps_lease.end_ip.addr = please->end_ip.addr; + bzero(&lease, sizeof(lease)); + // lease.start_ip.addr = start_ip; + // lease.end_ip.addr = end_ip; + lease.start_ip.addr = please->start_ip.addr; + lease.end_ip.addr = please->end_ip.addr; } - dhcps_lease.enable = please->enable; + lease.enable = please->enable; // dhcps_lease_flag = false; return true; } @@ -1184,7 +1037,7 @@ bool DhcpServer::get_dhcps_lease(struct dhcps_lease* please) } // if (dhcps_lease_flag){ - if (dhcps_lease.enable == false) + if (lease.enable == false) { if (isRunning()) { @@ -1193,20 +1046,20 @@ bool DhcpServer::get_dhcps_lease(struct dhcps_lease* please) } else { - // bzero(please, sizeof(dhcps_lease)); + // bzero(please, sizeof(*please)); // if (!isRunning()){ - // please->start_ip.addr = htonl(dhcps_lease.start_ip.addr); - // please->end_ip.addr = htonl(dhcps_lease.end_ip.addr); + // please->start_ip.addr = htonl(lease.start_ip.addr); + // please->end_ip.addr = htonl(lease.end_ip.addr); // } } // if (isRunning()){ - // bzero(please, sizeof(dhcps_lease)); - // please->start_ip.addr = dhcps_lease.start_ip.addr; - // please->end_ip.addr = dhcps_lease.end_ip.addr; + // bzero(please, sizeof(*please)); + // please->start_ip.addr = lease.start_ip.addr; + // please->end_ip.addr = lease.end_ip.addr; // } - please->start_ip.addr = dhcps_lease.start_ip.addr; - please->end_ip.addr = dhcps_lease.end_ip.addr; + please->start_ip.addr = lease.start_ip.addr; + please->end_ip.addr = lease.end_ip.addr; return true; } @@ -1276,81 +1129,6 @@ void DhcpServer::dhcps_coarse_tmr(void) } } -bool DhcpServer::set_dhcps_offer_option(uint8 level, void* optarg) -{ - bool offer_flag = true; - // uint8 option = 0; - if (optarg == nullptr && !isRunning()) - { - return false; - } - - if (level <= OFFER_START || level >= OFFER_END) - { - return false; - } - - switch (level) - { - case OFFER_ROUTER: - offer = (*(uint8*)optarg) & 0x01; - offer_flag = true; - break; - default: - offer_flag = false; - break; - } - return offer_flag; -} - -bool DhcpServer::set_dhcps_lease_time(uint32 minute) -{ - if (_netif->num == SOFTAP_IF) - { - uint8 opmode = wifi_get_opmode(); - if (opmode == STATION_MODE || opmode == NULL_MODE) - { - return false; - } - } - - if (isRunning()) - { - return false; - } - - if (minute == 0) - { - return false; - } - dhcps_lease_time = minute; - return true; -} - -bool DhcpServer::reset_dhcps_lease_time(void) -{ - if (_netif->num == SOFTAP_IF) - { - uint8 opmode = wifi_get_opmode(); - if (opmode == STATION_MODE || opmode == NULL_MODE) - { - return false; - } - } - - if (isRunning()) - { - return false; - } - dhcps_lease_time = DHCPS_LEASE_TIME_DEF; - return true; -} - -uint32 DhcpServer::get_dhcps_lease_time(void) // minute -{ - return dhcps_lease_time; -} - void DhcpServer::dhcps_client_leave(u8* bssid, struct ipv4_addr* ip, bool force) { struct dhcps_pool* pdhcps_pool = nullptr; @@ -1407,8 +1185,8 @@ uint32 DhcpServer::dhcps_client_update(u8* bssid, struct ipv4_addr* ip) list_node* pmac_node = nullptr; list_node* pip_node = nullptr; bool flag = false; - uint32 start_ip = dhcps_lease.start_ip.addr; - uint32 end_ip = dhcps_lease.end_ip.addr; + uint32 start_ip = lease.start_ip.addr; + uint32 end_ip = lease.end_ip.addr; dhcps_type_t type = DHCPS_TYPE_DYNAMIC; if (bssid == nullptr) { @@ -1516,7 +1294,7 @@ uint32 DhcpServer::dhcps_client_update(u8* bssid, struct ipv4_addr* ip) type = DHCPS_TYPE_DYNAMIC; } - pdhcps_pool->lease_timer = DHCPS_LEASE_TIMER; + pdhcps_pool->lease_timer = this->lease_time; pdhcps_pool->type = type; pdhcps_pool->state = DHCPS_STATE_ONLINE; } @@ -1537,7 +1315,7 @@ uint32 DhcpServer::dhcps_client_update(u8* bssid, struct ipv4_addr* ip) } node_remove_from_list(&plist, pmac_node); - pdhcps_pool->lease_timer = DHCPS_LEASE_TIMER; + pdhcps_pool->lease_timer = this->lease_time; pdhcps_pool->type = type; pdhcps_pool->state = DHCPS_STATE_ONLINE; node_insert_to_list(&plist, pmac_node); @@ -1553,7 +1331,7 @@ uint32 DhcpServer::dhcps_client_update(u8* bssid, struct ipv4_addr* ip) return IPADDR_ANY; } memcpy(pdhcps_pool->mac, bssid, sizeof(pdhcps_pool->mac)); - pdhcps_pool->lease_timer = DHCPS_LEASE_TIMER; + pdhcps_pool->lease_timer = this->lease_time; pdhcps_pool->type = type; pdhcps_pool->state = DHCPS_STATE_ONLINE; } @@ -1579,7 +1357,7 @@ uint32 DhcpServer::dhcps_client_update(u8* bssid, struct ipv4_addr* ip) return IPADDR_ANY; } memcpy(pdhcps_pool->mac, bssid, sizeof(pdhcps_pool->mac)); - pdhcps_pool->lease_timer = DHCPS_LEASE_TIMER; + pdhcps_pool->lease_timer = this->lease_time; pdhcps_pool->type = type; pdhcps_pool->state = DHCPS_STATE_ONLINE; pback_node = (list_node*)zalloc(sizeof(list_node)); diff --git a/cores/esp8266/LwipDhcpServer.h b/cores/esp8266/LwipDhcpServer.h index d5eb6410ef..152e54c9e6 100644 --- a/cores/esp8266/LwipDhcpServer.h +++ b/cores/esp8266/LwipDhcpServer.h @@ -28,22 +28,147 @@ // nearly as-is. This is an initial version to guaranty legacy behavior // with same default values. -#ifndef __DHCPS_H__ -#define __DHCPS_H__ +#pragma once -#include // LWIP_VERSION +#include + +#include +#include +#include + +#include +#include class DhcpServer { public: + static constexpr int DefaultLeaseTime = 720; // minutes + static constexpr uint32 MagicCookie = 0x63538263; // https://tools.ietf.org/html/rfc1497 + // + struct OptionsBuffer + { + OptionsBuffer(uint8_t* begin, uint8_t* end) : _it(begin), _begin(begin), _end(end) { } + + OptionsBuffer& add(uint8_t code, const uint8_t* data, size_t size); + + OptionsBuffer& add(uint8_t code, const char* data, size_t size) + { + return add(code, reinterpret_cast(data), size); + } + + template + OptionsBuffer& add(uint8_t code, const char (&data)[Size]) + { + return add(code, &data[0], Size - 1); + } + + template + OptionsBuffer& add(uint8_t code, const uint8_t (&data)[Size]) + { + return add(code, &data[0], Size); + } + + OptionsBuffer& add(uint8_t code, std::initializer_list data) + { + return add(code, data.begin(), data.size()); + } + + OptionsBuffer& add(uint8_t code, const ip4_addr_t* addr) + { + return add(code, + { ip4_addr1(addr), ip4_addr2(addr), ip4_addr3(addr), ip4_addr4(addr) }); + } + + OptionsBuffer& add(uint8_t code, uint8_t value) + { + return add(code, { value }); + } + + OptionsBuffer& add(uint8_t code, uint16_t value) + { + return add(code, { static_cast((value >> 8) & 0xff), + static_cast(value & 0xff) }); + } + + OptionsBuffer& add(uint8_t code, uint32_t value) + { + return add(code, { static_cast((value >> 24) & 0xff), + static_cast((value >> 16) & 0xff), + static_cast((value >> 8) & 0xff), + static_cast((value & 0xff)) }); + } + + OptionsBuffer& add(uint8_t code) + { + if (_it != _end) + { + *_it++ = code; + } + return *this; + } + + private: + uint8_t* _it; + uint8_t* _begin; + uint8_t* _end; + }; + + using OptionsBufferHandler = void (*)(const DhcpServer&, OptionsBuffer&); + DhcpServer(netif* netif); ~DhcpServer(); - void setDns(int num, const ipv4_addr_t* dns); + netif* getNetif() const + { + return _netif; + } + + void setRouter(bool value) + { + offer_router = value; + } + + bool getRouter() const + { + return offer_router; + } + + void setDns(ip4_addr_t addr) + { + dns_address = addr; + } + + ip4_addr_t getDns() const + { + return dns_address; + } + + void resetLeaseTime() + { + lease_time = DefaultLeaseTime; + } + + void setLeaseTime(uint32_t minutes) + { + lease_time = minutes; + } + + uint32_t getLeaseTime() const + { + return lease_time; + } + + // Will use provided callback for ACK and OFFER replies + // `options.add(...)` to append to the options list + // (does not check for duplicates!) + void onSendOptions(OptionsBufferHandler handler) + { + custom_offer_options = handler; + } - bool begin(ip_info* info); + bool begin(); void end(); - bool isRunning(); + bool isRunning() const; // this is the C interface encapsulated in a class // (originally dhcpserver.c in lwIP-v1.4 in NonOS-SDK) @@ -52,18 +177,16 @@ class DhcpServer // legacy public C structure and API to eventually turn into C++ - void init_dhcps_lease(uint32 ip); - bool set_dhcps_lease(struct dhcps_lease* please); - bool get_dhcps_lease(struct dhcps_lease* please); - bool set_dhcps_offer_option(uint8 level, void* optarg); - bool set_dhcps_lease_time(uint32 minute); - bool reset_dhcps_lease_time(void); - uint32 get_dhcps_lease_time(void); - bool add_dhcps_lease(uint8* macaddr); + void init_dhcps_lease(uint32 ip); + bool set_dhcps_lease(struct dhcps_lease* please); + bool get_dhcps_lease(struct dhcps_lease* please); + bool add_dhcps_lease(uint8* macaddr); - void dhcps_set_dns(int num, const ipv4_addr_t* dns); + void offers(); protected: + void add_offer_options(OptionsBuffer&); + // legacy C structure and API to eventually turn into C++ typedef struct _list_node @@ -72,12 +195,11 @@ class DhcpServer struct _list_node* pnext; } list_node; - void node_insert_to_list(list_node** phead, list_node* pinsert); - void node_remove_from_list(list_node** phead, list_node* pdelete); - uint8_t* add_msg_type(uint8_t* optptr, uint8_t type); - uint8_t* add_offer_options(uint8_t* optptr); - uint8_t* add_end(uint8_t* optptr); - void create_msg(struct dhcps_msg* m); + void node_insert_to_list(list_node** phead, list_node* pinsert); + void node_remove_from_list(list_node** phead, list_node* pdelete); + + OptionsBuffer create_msg(struct dhcps_msg* m); + void send_offer(struct dhcps_msg* m); void send_nak(struct dhcps_msg* m); void send_ack(struct dhcps_msg* m); @@ -91,25 +213,24 @@ class DhcpServer void dhcps_client_leave(u8* bssid, struct ipv4_addr* ip, bool force); uint32 dhcps_client_update(u8* bssid, struct ipv4_addr* ip); - netif* _netif; + netif* _netif = nullptr; - struct udp_pcb* pcb_dhcps; - ip_addr_t broadcast_dhcps; - struct ipv4_addr server_address; - struct ipv4_addr client_address; - struct ipv4_addr dns_address; - uint32 dhcps_lease_time; + struct udp_pcb* pcb_dhcps = nullptr; + ip_addr_t broadcast_dhcps {}; + ip4_addr_t server_address {}; + ip4_addr_t client_address {}; - struct dhcps_lease dhcps_lease; - list_node* plist; - uint8 offer; - bool renew; + uint32_t lease_time = DefaultLeaseTime; - static const uint32 magic_cookie; -}; + bool offer_router = true; + ip4_addr_t dns_address {}; + + dhcps_lease lease {}; -// SoftAP DHCP server always exists and is started on boot -extern DhcpServer dhcpSoftAP; -extern "C" int fw_has_started_softap_dhcps; + list_node* plist = nullptr; + bool renew = false; -#endif // __DHCPS_H__ + OptionsBufferHandler custom_offer_options = nullptr; + + static const uint32 magic_cookie; +}; diff --git a/cores/esp8266/LwipIntf.cpp b/cores/esp8266/LwipIntf.cpp index 6b84aab16d..675063cd62 100644 --- a/cores/esp8266/LwipIntf.cpp +++ b/cores/esp8266/LwipIntf.cpp @@ -1,3 +1,25 @@ +/* + LwipIntf.cpp + + Arduino interface for lwIP generic callbacks and functions + + Original Copyright (c) 2020 esp8266 Arduino All rights reserved. + This file is part of the esp8266 Arduino core environment. + + 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ extern "C" { @@ -16,6 +38,17 @@ extern "C" #include "debug.h" #include "LwipIntf.h" +// wifi_station_hostname is SDK's station(=global) hostname location +// - It is never nullptr but wifi_station_get_hostname() +// 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 first does not work anymore +// - wifi_station_hostname is overwritten by SDK when wifi is +// woken up in WiFi::mode() +// +extern "C" char* wifi_station_hostname; + // args | esp order arduino order // ---- + --------- ------------- // local_ip | local_ip local_ip @@ -66,7 +99,7 @@ bool LwipIntf::ipAddressReorder(const IPAddress& local_ip, const IPAddress& arg1 */ String LwipIntf::hostname(void) { - return wifi_station_get_hostname(); + return wifi_station_hostname; } /** @@ -75,7 +108,7 @@ String LwipIntf::hostname(void) */ const char* LwipIntf::getHostname(void) { - return wifi_station_get_hostname(); + return wifi_station_hostname; } /** @@ -136,12 +169,9 @@ bool LwipIntf::hostname(const char* aHostname) DEBUGV("hostname '%s' is not compliant with RFC952\n", aHostname); } - bool ret = wifi_station_set_hostname(aHostname); - if (!ret) - { - DEBUGV("WiFi.hostname(%s): wifi_station_set_hostname() failed\n", aHostname); - return false; - } + bool ret = true; + + strcpy(wifi_station_hostname, aHostname); // now we should inform dhcp server for this change, using lwip_renew() // looping through all existing interface @@ -149,7 +179,7 @@ bool LwipIntf::hostname(const char* aHostname) for (netif* intf = netif_list; intf; intf = intf->next) { // unconditionally update all known interfaces - intf->hostname = wifi_station_get_hostname(); + intf->hostname = wifi_station_hostname; if (netif_dhcp_data(intf) != nullptr) { diff --git a/cores/esp8266/LwipIntf.h b/cores/esp8266/LwipIntf.h index 1eee25cce5..40907cd2bc 100644 --- a/cores/esp8266/LwipIntf.h +++ b/cores/esp8266/LwipIntf.h @@ -1,3 +1,25 @@ +/* + LwipIntf.h + + Arduino interface for lwIP generic callbacks and functions + + Original Copyright (c) 2020 esp8266 Arduino All rights reserved. + This file is part of the esp8266 Arduino core environment. + + 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #ifndef _LWIPINTF_H #define _LWIPINTF_H @@ -12,14 +34,12 @@ class LwipIntf public: using CBType = std::function; - static bool stateUpCB(LwipIntf::CBType&& cb); - // reorder WiFi.config() parameters for a esp8266/official Arduino dual-compatibility API // args | esp order arduino order // ---- + --------- ------------- // local_ip | local_ip local_ip // arg1 | gateway dns1 - // arg2 | netmask [Agateway + // arg2 | netmask gateway // arg3 | dns1 netmask // // result stored into gateway/netmask/dns1 @@ -28,20 +48,28 @@ class LwipIntf IPAddress& netmask, IPAddress& dns1); String hostname(); - bool hostname(const String& aHostname) + + bool hostname(const String& aHostname) { return hostname(aHostname.c_str()); } + bool hostname(const char* aHostname); + // ESP32 API compatibility bool setHostname(const char* aHostName) { return hostname(aHostName); } + + // ESP32 API compatibility const char* getHostname(); -protected: - static bool stateChangeSysCB(LwipIntf::CBType&& cb); + // whenever netif status callback is called + static bool statusChangeCB(LwipIntf::CBType); + + static bool stateUpCB(LwipIntf::CBType); + static bool stateDownCB(LwipIntf::CBType); }; #endif // _LWIPINTF_H diff --git a/cores/esp8266/LwipIntfCB.cpp b/cores/esp8266/LwipIntfCB.cpp index 9d0f9feb22..945d7d43ac 100644 --- a/cores/esp8266/LwipIntfCB.cpp +++ b/cores/esp8266/LwipIntfCB.cpp @@ -1,46 +1,78 @@ +/* + LwipIntfCB.cpp + + network generic callback implementation + + Original Copyright (c) 2020 esp8266 Arduino All rights reserved. + This file is part of the esp8266 Arduino core environment. + + 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #include #include #include -#define NETIF_STATUS_CB_SIZE 3 +static constexpr size_t LwipIntfCallbacks = 3; -static int netifStatusChangeListLength = 0; -LwipIntf::CBType netifStatusChangeList[NETIF_STATUS_CB_SIZE]; +static LwipIntf::CBType callbacks[LwipIntfCallbacks]; +static size_t size = 0; +// override empty weak function from glue-lwip extern "C" void netif_status_changed(struct netif* netif) { - // override the default empty weak function - for (int i = 0; i < netifStatusChangeListLength; i++) + for (size_t index = 0; index < size; ++index) { - netifStatusChangeList[i](netif); + callbacks[index](netif); } } -bool LwipIntf::stateChangeSysCB(LwipIntf::CBType&& cb) +bool LwipIntf::statusChangeCB(LwipIntf::CBType cb) { - if (netifStatusChangeListLength >= NETIF_STATUS_CB_SIZE) + if (size < LwipIntfCallbacks) { + callbacks[size++] = std::move(cb); + return true; + } #if defined(DEBUG_ESP_CORE) - DEBUGV("NETIF_STATUS_CB_SIZE is too low\n"); + DEBUGV("LwipIntf::CB %zu/%zu, cannot add more!\n", size, size); #endif - return false; - } - netifStatusChangeList[netifStatusChangeListLength++] = cb; - return true; + return false; +} + +bool LwipIntf::stateUpCB(LwipIntf::CBType cb) +{ + return statusChangeCB( + [cb](netif* interface) + { + if (netif_is_up(interface)) + { + cb(interface); + } + }); } -bool LwipIntf::stateUpCB(LwipIntf::CBType&& cb) +bool LwipIntf::stateDownCB(LwipIntf::CBType cb) { - return stateChangeSysCB( - [cb](netif* nif) + return statusChangeCB( + [cb](netif* interface) { - if (netif_is_up(nif)) - schedule_function( - [cb, nif]() - { - cb(nif); - }); + if (!netif_is_up(interface)) + { + cb(interface); + } }); } diff --git a/cores/esp8266/LwipIntfDev.h b/cores/esp8266/LwipIntfDev.h index e105e36942..d69e2d73d8 100644 --- a/cores/esp8266/LwipIntfDev.h +++ b/cores/esp8266/LwipIntfDev.h @@ -1,9 +1,30 @@ +/* + LwipIntfDev.h + + Arduino network template class for generic device + + Original Copyright (c) 2020 esp8266 Arduino All rights reserved. + This file is part of the esp8266 Arduino core environment. + + 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #ifndef _LWIPINTFDEV_H #define _LWIPINTFDEV_H // TODO: -// remove all Serial.print // unchain pbufs #include @@ -25,6 +46,13 @@ #define DEFAULT_MTU 1500 #endif +enum EthernetLinkStatus +{ + Unknown, + LinkON, + LinkOFF +}; + template class LwipIntfDev: public LwipIntf, public RawDev { @@ -35,17 +63,31 @@ class LwipIntfDev: public LwipIntf, public RawDev memset(&_netif, 0, sizeof(_netif)); } + //The argument order for ESP is not the same as for Arduino. However, there is compatibility code under the hood + //to detect Arduino arg order, and handle it correctly. boolean config(const IPAddress& local_ip, const IPAddress& arg1, const IPAddress& arg2, const IPAddress& arg3 = IPADDR_NONE, const IPAddress& dns2 = IPADDR_NONE); + // two and one parameter version. 2nd parameter is DNS like in Arduino. IPv4 only + [[deprecated("It is discouraged to use this 1 or 2 parameters network configuration legacy " + "function config(ip[,dns]) as chosen defaults may not match the local network " + "configuration")]] boolean + config(IPAddress local_ip, IPAddress dns = INADDR_ANY); + // default mac-address is inferred from esp8266's STA interface boolean begin(const uint8_t* macAddress = nullptr, const uint16_t mtu = DEFAULT_MTU); + void end(); const netif* getNetIf() const { return &_netif; } + uint8_t* macAddress(uint8_t* mac) + { + memcpy(mac, &_netif.hwaddr, 6); + return mac; + } IPAddress localIP() const { return IPAddress(ip4_addr_get_u32(ip_2_ip4(&_netif.ip_addr))); @@ -58,21 +100,56 @@ class LwipIntfDev: public LwipIntf, public RawDev { return IPAddress(ip4_addr_get_u32(ip_2_ip4(&_netif.gw))); } + IPAddress dnsIP(int n = 0) const + { + return IPAddress(dns_getserver(n)); + } + void setDNS(IPAddress dns1, IPAddress dns2 = INADDR_ANY) + { + if (dns1.isSet()) + { + dns_setserver(0, dns1); + } + if (dns2.isSet()) + { + dns_setserver(1, dns2); + } + } - void setDefault(); + // 1. Currently when no default is set, esp8266-Arduino uses the first + // DHCP client interface receiving a valid address and gateway to + // become the new lwIP default interface. + // 2. Otherwise - when using static addresses - lwIP for every packets by + // defaults selects automatically the best suited output interface + // matching the destination address. If several interfaces match, + // the first one is picked. On esp8266/Arduno: WiFi interfaces are + // checked first. + // 3. Or, use `::setDefault(true)` to force using this interface's gateway + // as default router. + void setDefault(bool deflt = true); // true if interface has a valid IPv4 address + // (and ethernet link status is not detectable or is up) bool connected() { - return !!ip4_addr_get_u32(ip_2_ip4(&_netif.ip_addr)); + return !!ip4_addr_get_u32(ip_2_ip4(&_netif.ip_addr)) + && (!RawDev::isLinkDetectable() || RawDev::isLinked()); } - // ESP8266WiFi API compatibility + bool routable() + { + return !ip_addr_isany(&_netif.gw); + } + // ESP8266WiFi API compatibility wl_status_t status(); + // Arduino Ethernet compatibility + EthernetLinkStatus linkStatus(); + protected: err_t netif_init(); + void check_route(); void netif_status_callback(); static err_t netif_init_s(netif* netif); @@ -90,6 +167,7 @@ class LwipIntfDev: public LwipIntf, public RawDev int8_t _intrPin; uint8_t _macAddress[6]; bool _started; + bool _scheduled; bool _default; }; @@ -127,6 +205,24 @@ boolean LwipIntfDev::config(const IPAddress& localIP, const IPAddress& g return true; } +template +boolean LwipIntfDev::config(IPAddress local_ip, IPAddress dns) +{ + if (!local_ip.isSet()) + return config(INADDR_ANY, INADDR_ANY, INADDR_ANY); + + if (!local_ip.isV4()) + return false; + + IPAddress gw(local_ip); + gw[3] = 1; + if (!dns.isSet()) + { + dns = gw; + } + return config(local_ip, gw, IPAddress(255, 255, 255, 0), dns); +} + template boolean LwipIntfDev::begin(const uint8_t* macAddress, const uint16_t mtu) { @@ -181,26 +277,34 @@ boolean LwipIntfDev::begin(const uint8_t* macAddress, const uint16_t mtu if (!netif_add(&_netif, ip_2_ip4(&ip_addr), ip_2_ip4(&netmask), ip_2_ip4(&gw), this, netif_init_s, ethernet_input)) { + RawDev::end(); return false; } - _netif.flags |= NETIF_FLAG_UP; - if (localIP().v4() == 0) { + // IP not set, starting DHCP + _netif.flags |= NETIF_FLAG_UP; switch (dhcp_start(&_netif)) { case ERR_OK: break; case ERR_IF: + RawDev::end(); return false; default: - netif_remove(&_netif); + end(); return false; } } + else + { + // IP is set, static config + netif_set_link_up(&_netif); + netif_set_up(&_netif); + } _started = true; @@ -218,28 +322,53 @@ boolean LwipIntfDev::begin(const uint8_t* macAddress, const uint16_t mtu } } - if (_intrPin < 0 - && !schedule_recurrent_function_us( + if (_intrPin < 0 && !_scheduled) + { + _scheduled = schedule_recurrent_function_us( [&]() { + if (!_started) + { + _scheduled = false; + return false; + } this->handlePackets(); return true; }, - 100)) - { - netif_remove(&_netif); - return false; + 100); + if (!_scheduled) + { + end(); + return false; + } } return true; } +template +void LwipIntfDev::end() +{ + if (_started) + { + netif_remove(&_netif); + _started = false; + RawDev::end(); + } +} + template wl_status_t LwipIntfDev::status() { return _started ? (connected() ? WL_CONNECTED : WL_DISCONNECTED) : WL_NO_SHIELD; } +template +EthernetLinkStatus LwipIntfDev::linkStatus() +{ + return RawDev::isLinkDetectable() ? _started && RawDev::isLinked() ? LinkON : LinkOFF : Unknown; +} + template err_t LwipIntfDev::linkoutput_s(netif* netif, struct pbuf* pbuf) { @@ -301,16 +430,25 @@ err_t LwipIntfDev::netif_init() template void LwipIntfDev::netif_status_callback() { + check_route(); if (connected()) { - if (_default || (netif_default == nullptr && !ip_addr_isany(&_netif.gw))) + sntp_stop(); + sntp_init(); + } +} + +template +void LwipIntfDev::check_route() +{ + if (connected()) + { + if (_default || (netif_default == nullptr && routable())) { // on user request, - // or if there is no current default interface, but a gateway is valid + // or if there is no current default interface, but our gateway is valid netif_set_default(&_netif); } - sntp_stop(); - sntp_init(); } else if (netif_default == &_netif) { @@ -386,13 +524,10 @@ err_t LwipIntfDev::handlePackets() } template -void LwipIntfDev::setDefault() +void LwipIntfDev::setDefault(bool deflt) { - _default = true; - if (connected()) - { - netif_set_default(&_netif); - } + _default = deflt; + check_route(); } #endif // _LWIPINTFDEV_H diff --git a/cores/esp8266/Schedule.cpp b/cores/esp8266/Schedule.cpp index 279e78ff6c..f6c650fcf9 100644 --- a/cores/esp8266/Schedule.cpp +++ b/cores/esp8266/Schedule.cpp @@ -17,6 +17,7 @@ */ #include +#include #include "Schedule.h" #include "PolledTimeout.h" @@ -34,6 +35,7 @@ static scheduled_fn_t* sFirst = nullptr; static scheduled_fn_t* sLast = nullptr; static scheduled_fn_t* sUnused = nullptr; static int sCount = 0; +static uint32_t recurrent_max_grain_mS = 0; typedef std::function mRecFuncT; struct recurrent_fn_t @@ -130,9 +132,39 @@ bool schedule_recurrent_function_us(const std::function& fn, } rLast = item; + // grain needs to be recomputed + recurrent_max_grain_mS = 0; + return true; } +uint32_t compute_scheduled_recurrent_grain () +{ + if (recurrent_max_grain_mS == 0) + { + if (rFirst) + { + uint32_t recurrent_max_grain_uS = rFirst->callNow.getTimeout(); + for (auto it = rFirst->mNext; it; it = it->mNext) + recurrent_max_grain_uS = std::gcd(recurrent_max_grain_uS, it->callNow.getTimeout()); + if (recurrent_max_grain_uS) + // round to the upper millis + recurrent_max_grain_mS = recurrent_max_grain_uS <= 1000? 1: (recurrent_max_grain_uS + 999) / 1000; + } + +#ifdef DEBUG_ESP_CORE + static uint32_t last_grain = 0; + if (recurrent_max_grain_mS != last_grain) + { + ::printf(":rsf %u->%u\n", last_grain, recurrent_max_grain_mS); + last_grain = recurrent_max_grain_mS; + } +#endif + } + + return recurrent_max_grain_mS; +} + void run_scheduled_functions() { // prevent scheduling of new functions during this run @@ -226,6 +258,9 @@ void run_scheduled_recurrent_functions() } delete(to_ditch); + + // grain needs to be recomputed + recurrent_max_grain_mS = 0; } else { diff --git a/cores/esp8266/Schedule.h b/cores/esp8266/Schedule.h index da86e5b7f5..362d15b5f3 100644 --- a/cores/esp8266/Schedule.h +++ b/cores/esp8266/Schedule.h @@ -39,6 +39,11 @@ // scheduled function happen more often: every yield() (vs every loop()), // and time resolution is microsecond (vs millisecond). Details are below. +// compute_scheduled_recurrent_grain() is used by delay() to give a chance to +// all recurrent functions to run per their timing requirement. + +uint32_t compute_scheduled_recurrent_grain (); + // scheduled functions called once: // // * internal queue is FIFO. diff --git a/cores/esp8266/StackThunk.cpp b/cores/esp8266/StackThunk.cpp index 7456fcaeeb..baa793bdc5 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 */ @@ -142,11 +150,45 @@ void stack_thunk_dump_stack() ets_printf("<< cont stacks */ + "movi a2, stack_thunk_yield_save\n\t" + "s32i.n a1, a2, 0\n\t" + "movi a2, stack_thunk_save\n\t" + "l32i.n a1, a2, 0\n\t" +/* optimistic_yield(10000) without extra l32r */ + "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" + "\n" +/* 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 ffaeb32947..350775ec24 100644 --- a/cores/esp8266/StackThunk.h +++ b/cores/esp8266/StackThunk.h @@ -31,6 +31,8 @@ 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(); @@ -41,7 +43,7 @@ extern uint32_t stack_thunk_get_stack_bot(); extern uint32_t stack_thunk_get_cont_sp(); extern uint32_t stack_thunk_get_max_usage(); extern void stack_thunk_dump_stack(); -extern void stack_thunk_fatal_overflow(); +extern void stack_thunk_fatal_smashing(); // Globals required for thunking operation extern uint32_t *stack_thunk_ptr; @@ -75,7 +77,7 @@ thunk_"#fcnToThunk":\n\ l32i.n a15, a15, 0 /* A15 now has contents of last stack entry */\n\ l32r a0, .LC_STACK_VALUE"#fcnToThunk" /* A0 now has the check value */\n\ beq a0, a15, .L1"#fcnToThunk"\n\ - call0 stack_thunk_fatal_overflow\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\ diff --git a/cores/esp8266/Stream.cpp b/cores/esp8266/Stream.cpp index f6aa7487c7..b9b5b95f65 100644 --- a/cores/esp8266/Stream.cpp +++ b/cores/esp8266/Stream.cpp @@ -58,16 +58,16 @@ int Stream::timedPeek() { // returns peek of the next digit in the stream or -1 if timeout // discards non-numeric characters -int Stream::peekNextDigit() { +int Stream::peekNextDigit(bool detectDecimal) { int c; while(1) { c = timedPeek(); - if(c < 0) - return c; // timeout - if(c == '-') - return c; - if(c >= '0' && c <= '9') + if( c < 0 || // timeout + c == '-' || + ( c >= '0' && c <= '9' ) || + ( detectDecimal && c == '.' ) ) { return c; + } read(); // discard non-numeric } } @@ -141,7 +141,7 @@ long Stream::parseInt(char skipChar) { long value = 0; int c; - c = peekNextDigit(); + c = peekNextDigit(false); // ignore non numeric leading characters if(c < 0) return 0; // zero returned if timeout @@ -176,7 +176,7 @@ float Stream::parseFloat(char skipChar) { int c; float fraction = 1.0f; - c = peekNextDigit(); + c = peekNextDigit(true); // ignore non numeric leading characters if(c < 0) return 0; // zero returned if timeout @@ -262,6 +262,32 @@ String Stream::readStringUntil(char terminator) { return ret; } +String Stream::readStringUntil(const char* terminator, uint32_t untilTotalNumberOfOccurrences) { + String ret; + int c; + uint32_t occurrences = 0; + size_t termLen = strlen(terminator); + size_t termIndex = 0; + size_t index = 0; + + while ((c = timedRead()) > 0) { + ret += (char) c; + index++; + + if (terminator[termIndex] == c) { + if (++termIndex == termLen && ++occurrences == untilTotalNumberOfOccurrences) { + // don't include terminator in returned string + ret.remove(index - termIndex, termLen); + break; + } + } else { + termIndex = 0; + } + } + + return ret; +} + // read what can be read, immediate exit on unavailable data // prototype similar to Arduino's `int Client::read(buf, len)` int Stream::read (uint8_t* buffer, size_t maxLen) diff --git a/cores/esp8266/Stream.h b/cores/esp8266/Stream.h index 1dd2ee24a6..0706bec001 100644 --- a/cores/esp8266/Stream.h +++ b/cores/esp8266/Stream.h @@ -53,7 +53,7 @@ class Stream: public Print { unsigned long _startMillis; // used for timeout measurement int timedRead(); // private method to read stream with timeout int timedPeek(); // private method to peek stream with timeout - int peekNextDigit(); // returns the next numeric digit in the stream or -1 if timeout + int peekNextDigit(bool detectDecimal = false); // returns the next numeric digit in the stream or -1 if timeout public: virtual int available() = 0; @@ -115,6 +115,7 @@ class Stream: public Print { // Arduino String functions to be added here virtual String readString(); String readStringUntil(char terminator); + String readStringUntil(const char* terminator, uint32_t untilTotalNumberOfOccurrences = 1); virtual int read (uint8_t* buffer, size_t len); int read (char* buffer, size_t len) { return read((uint8_t*)buffer, len); } @@ -167,25 +168,53 @@ class Stream: public Print { // When result is 0 or less than requested maxLen, Print::getLastSend() // contains an error reason. +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + + // transfers already buffered / immediately available data (no timeout) + // returns number of transferred bytes + [[deprecated]] size_t sendAvailable (Print* to) { return sendGeneric(to, -1, -1, oneShotMs::alwaysExpired); } + [[deprecated]] size_t sendAvailable (Print& to) { return sendAvailable(&to); } + + // transfers data until timeout + // returns number of transferred bytes + [[deprecated]] size_t sendAll (Print* to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, -1, -1, timeoutMs); } + [[deprecated]] size_t sendAll (Print& 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 + [[deprecated]] size_t sendUntil (Print* to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, -1, readUntilChar, timeoutMs); } + [[deprecated]] size_t sendUntil (Print& 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 + [[deprecated]] size_t sendSize (Print* to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, maxLen, -1, timeoutMs); } + [[deprecated]] size_t sendSize (Print& to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendSize(&to, maxLen, timeoutMs); } + +#pragma GCC diagnostic pop + // transfers already buffered / immediately available data (no timeout) // returns number of transferred bytes - size_t sendAvailable (Print* to) { return sendGeneric(to, -1, -1, oneShotMs::alwaysExpired); } - size_t sendAvailable (Print& to) { return sendAvailable(&to); } + 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 (Print* to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, -1, -1, timeoutMs); } - size_t sendAll (Print& to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendAll(&to, timeoutMs); } + 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 (Print* to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, -1, readUntilChar, timeoutMs); } - size_t sendUntil (Print& 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 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 (Print* to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, maxLen, -1, timeoutMs); } - size_t sendSize (Print& 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 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; } @@ -202,11 +231,17 @@ class Stream: public Print { Report getLastSendReport () const { return _sendReport; } protected: + [[deprecated]] size_t sendGeneric (Print* to, const ssize_t len = -1, const int readUntilChar = -1, oneShotMs::timeType timeoutMs = oneShotMs::neverExpires /* neverExpires=>getTimeout() */); + size_t sendGeneric (Stream* to, + const ssize_t len = -1, + const int readUntilChar = -1, + oneShotMs::timeType timeoutMs = oneShotMs::neverExpires /* neverExpires=>getTimeout() */); + size_t SendGenericPeekBuffer(Print* to, const ssize_t len, const int readUntilChar, const oneShotMs::timeType timeoutMs); size_t SendGenericRegularUntil(Print* to, const ssize_t len, const int readUntilChar, const oneShotMs::timeType timeoutMs); size_t SendGenericRegular(Print* to, const ssize_t len, const oneShotMs::timeType timeoutMs); diff --git a/cores/esp8266/StreamSend.cpp b/cores/esp8266/StreamSend.cpp index bf07f397b8..b46d1c1560 100644 --- a/cores/esp8266/StreamSend.cpp +++ b/cores/esp8266/StreamSend.cpp @@ -22,9 +22,54 @@ #include #include +size_t Stream::sendGeneric(Stream* to, const ssize_t len, const int readUntilChar, + const esp8266::polledTimeout::oneShotFastMs::timeType timeoutMs) +{ + // "neverExpires (default, impossible)" is translated to default timeout + esp8266::polledTimeout::oneShotFastMs::timeType inputTimeoutMs + = timeoutMs >= esp8266::polledTimeout::oneShotFastMs::neverExpires ? getTimeout() + : timeoutMs; + + esp8266::polledTimeout::oneShotFastMs::timeType mainTimeoutMs = std::max( + inputTimeoutMs, (esp8266::polledTimeout::oneShotFastMs::timeType)to->getTimeout()); + + setReport(Report::Success); + + if (len == 0) + { + return 0; // conveniently avoids timeout for no requested data + } + + // There are two timeouts: + // - read (network, serial, ...) + // - write (network, serial, ...) + // However + // - getTimeout() is for reading only + // - there is no getOutputTimeout() api + // So we use getTimeout() for both, + // (also when inputCanTimeout() is false) + + if (hasPeekBufferAPI()) + { + return SendGenericPeekBuffer(to, len, readUntilChar, mainTimeoutMs); + } + + if (readUntilChar >= 0) + { + return SendGenericRegularUntil(to, len, readUntilChar, mainTimeoutMs); + } + + return SendGenericRegular(to, len, mainTimeoutMs); +} + size_t Stream::sendGeneric(Print* to, const ssize_t len, const int readUntilChar, const esp8266::polledTimeout::oneShotFastMs::timeType timeoutMs) { + // "neverExpires (default, impossible)" is translated to default timeout + esp8266::polledTimeout::oneShotFastMs::timeType inputTimeoutMs + = timeoutMs >= esp8266::polledTimeout::oneShotFastMs::neverExpires ? getTimeout() + : timeoutMs; + setReport(Report::Success); if (len == 0) @@ -43,25 +88,23 @@ size_t Stream::sendGeneric(Print* to, const ssize_t len, const int readUntilChar if (hasPeekBufferAPI()) { - return SendGenericPeekBuffer(to, len, readUntilChar, timeoutMs); + return SendGenericPeekBuffer(to, len, readUntilChar, inputTimeoutMs); } if (readUntilChar >= 0) { - return SendGenericRegularUntil(to, len, readUntilChar, timeoutMs); + return SendGenericRegularUntil(to, len, readUntilChar, inputTimeoutMs); } - return SendGenericRegular(to, len, timeoutMs); + return SendGenericRegular(to, len, inputTimeoutMs); } size_t Stream::SendGenericPeekBuffer(Print* to, const ssize_t len, const int readUntilChar, const esp8266::polledTimeout::oneShotFastMs::timeType timeoutMs) { - // "neverExpires (default, impossible)" is translated to default timeout - esp8266::polledTimeout::oneShotFastMs timedOut( - timeoutMs >= esp8266::polledTimeout::oneShotFastMs::neverExpires ? getTimeout() - : timeoutMs); + esp8266::polledTimeout::oneShotFastMs timedOut(timeoutMs); + // len==-1 => maxLen=0 <=> until starvation const size_t maxLen = std::max((ssize_t)0, len); size_t written = 0; @@ -152,10 +195,8 @@ Stream::SendGenericRegularUntil(Print* to, const ssize_t len, const int readUnti // regular Stream API // no other choice than reading byte by byte - // "neverExpires (default, impossible)" is translated to default timeout - esp8266::polledTimeout::oneShotFastMs timedOut( - timeoutMs >= esp8266::polledTimeout::oneShotFastMs::neverExpires ? getTimeout() - : timeoutMs); + esp8266::polledTimeout::oneShotFastMs timedOut(timeoutMs); + // len==-1 => maxLen=0 <=> until starvation const size_t maxLen = std::max((ssize_t)0, len); size_t written = 0; @@ -231,10 +272,8 @@ size_t Stream::SendGenericRegular(Print* to, const ssize_t len, // regular Stream API // use an intermediary buffer - // "neverExpires (default, impossible)" is translated to default timeout - esp8266::polledTimeout::oneShotFastMs timedOut( - timeoutMs >= esp8266::polledTimeout::oneShotFastMs::neverExpires ? getTimeout() - : timeoutMs); + esp8266::polledTimeout::oneShotFastMs timedOut(timeoutMs); + // len==-1 => maxLen=0 <=> until starvation const size_t maxLen = std::max((ssize_t)0, len); size_t written = 0; 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/TZ.h b/cores/esp8266/TZ.h index ea53c363a8..478abb16c0 100644 --- a/cores/esp8266/TZ.h +++ b/cores/esp8266/TZ.h @@ -1,22 +1,16 @@ +// ! ! ! DO NOT EDIT, AUTOMATICALLY GENERATED ! ! ! +// File created 2024-02-05 00:00:00.000000+00:00 +// Based on IANA database 2023d +// Re-run /tools/tools/format_tzdata.py to update -// autogenerated from https://raw.githubusercontent.com/nayarsystems/posix_tz_db/master/zones.csv -// by script /tools/TZupdate.sh -// Mon Jul 26 20:04:37 UTC 2021 -// -// This database is autogenerated from IANA timezone database -// https://raw.githubusercontent.com/nayarsystems/posix_tz_db/master/zones.csv -// (using https://www.iana.org/time-zones) -// and can be updated on demand in this repository -// or by yourself using the above script - -#ifndef TZDB_H -#define TZDB_H +#pragma once #define TZ_Africa_Abidjan PSTR("GMT0") #define TZ_Africa_Accra PSTR("GMT0") #define TZ_Africa_Addis_Ababa PSTR("EAT-3") #define TZ_Africa_Algiers PSTR("CET-1") #define TZ_Africa_Asmara PSTR("EAT-3") +#define TZ_Africa_Asmera PSTR("EAT-3") #define TZ_Africa_Bamako PSTR("GMT0") #define TZ_Africa_Bangui PSTR("WAT-1") #define TZ_Africa_Banjul PSTR("GMT0") @@ -24,7 +18,7 @@ #define TZ_Africa_Blantyre PSTR("CAT-2") #define TZ_Africa_Brazzaville PSTR("WAT-1") #define TZ_Africa_Bujumbura PSTR("CAT-2") -#define TZ_Africa_Cairo PSTR("EET-2") +#define TZ_Africa_Cairo PSTR("EET-2EEST,M4.5.5/0,M10.5.4/24") #define TZ_Africa_Casablanca PSTR("<+01>-1") #define TZ_Africa_Ceuta PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Africa_Conakry PSTR("GMT0") @@ -37,7 +31,7 @@ #define TZ_Africa_Gaborone PSTR("CAT-2") #define TZ_Africa_Harare PSTR("CAT-2") #define TZ_Africa_Johannesburg PSTR("SAST-2") -#define TZ_Africa_Juba PSTR("EAT-3") +#define TZ_Africa_Juba PSTR("CAT-2") #define TZ_Africa_Kampala PSTR("EAT-3") #define TZ_Africa_Khartoum PSTR("CAT-2") #define TZ_Africa_Kigali PSTR("CAT-2") @@ -61,6 +55,7 @@ #define TZ_Africa_Ouagadougou PSTR("GMT0") #define TZ_Africa_PortomNovo PSTR("WAT-1") #define TZ_Africa_Sao_Tome PSTR("GMT0") +#define TZ_Africa_Timbuktu PSTR("GMT0") #define TZ_Africa_Tripoli PSTR("EET-2") #define TZ_Africa_Tunis PSTR("CET-1") #define TZ_Africa_Windhoek PSTR("CAT-2") @@ -71,6 +66,7 @@ #define TZ_America_Araguaina PSTR("<-03>3") #define TZ_America_Argentina_Buenos_Aires PSTR("<-03>3") #define TZ_America_Argentina_Catamarca PSTR("<-03>3") +#define TZ_America_Argentina_ComodRivadavia PSTR("<-03>3") #define TZ_America_Argentina_Cordoba PSTR("<-03>3") #define TZ_America_Argentina_Jujuy PSTR("<-03>3") #define TZ_America_Argentina_La_Rioja PSTR("<-03>3") @@ -84,8 +80,9 @@ #define TZ_America_Aruba PSTR("AST4") #define TZ_America_Asuncion PSTR("<-04>4<-03>,M10.1.0/0,M3.4.0/0") #define TZ_America_Atikokan PSTR("EST5") +#define TZ_America_Atka PSTR("HST10HDT,M3.2.0,M11.1.0") #define TZ_America_Bahia PSTR("<-03>3") -#define TZ_America_Bahia_Banderas PSTR("CST6CDT,M4.1.0,M10.5.0") +#define TZ_America_Bahia_Banderas PSTR("CST6") #define TZ_America_Barbados PSTR("AST4") #define TZ_America_Belem PSTR("<-03>3") #define TZ_America_Belize PSTR("CST6") @@ -93,14 +90,19 @@ #define TZ_America_Boa_Vista PSTR("<-04>4") #define TZ_America_Bogota PSTR("<-05>5") #define TZ_America_Boise PSTR("MST7MDT,M3.2.0,M11.1.0") +#define TZ_America_Buenos_Aires PSTR("<-03>3") #define TZ_America_Cambridge_Bay PSTR("MST7MDT,M3.2.0,M11.1.0") #define TZ_America_Campo_Grande PSTR("<-04>4") #define TZ_America_Cancun PSTR("EST5") #define TZ_America_Caracas PSTR("<-04>4") +#define TZ_America_Catamarca PSTR("<-03>3") #define TZ_America_Cayenne PSTR("<-03>3") #define TZ_America_Cayman PSTR("EST5") #define TZ_America_Chicago PSTR("CST6CDT,M3.2.0,M11.1.0") -#define TZ_America_Chihuahua PSTR("MST7MDT,M4.1.0,M10.5.0") +#define TZ_America_Chihuahua PSTR("CST6") +#define TZ_America_Ciudad_Juarez PSTR("MST7MDT,M3.2.0,M11.1.0") +#define TZ_America_Coral_Harbour PSTR("EST5") +#define TZ_America_Cordoba PSTR("<-03>3") #define TZ_America_Costa_Rica PSTR("CST6") #define TZ_America_Creston PSTR("MST7") #define TZ_America_Cuiaba PSTR("<-04>4") @@ -114,10 +116,12 @@ #define TZ_America_Edmonton PSTR("MST7MDT,M3.2.0,M11.1.0") #define TZ_America_Eirunepe PSTR("<-05>5") #define TZ_America_El_Salvador PSTR("CST6") -#define TZ_America_Fortaleza PSTR("<-03>3") +#define TZ_America_Ensenada PSTR("PST8PDT,M3.2.0,M11.1.0") #define TZ_America_Fort_Nelson PSTR("MST7") +#define TZ_America_Fort_Wayne PSTR("EST5EDT,M3.2.0,M11.1.0") +#define TZ_America_Fortaleza PSTR("<-03>3") #define TZ_America_Glace_Bay PSTR("AST4ADT,M3.2.0,M11.1.0") -#define TZ_America_Godthab PSTR("<-03>3<-02>,M3.5.0/-2,M10.5.0/-1") +#define TZ_America_Godthab PSTR("<-02>2<-01>,M3.5.0/-1,M10.5.0/0") #define TZ_America_Goose_Bay PSTR("AST4ADT,M3.2.0,M11.1.0") #define TZ_America_Grand_Turk PSTR("EST5EDT,M3.2.0,M11.1.0") #define TZ_America_Grenada PSTR("AST4") @@ -136,16 +140,20 @@ #define TZ_America_Indiana_Vevay PSTR("EST5EDT,M3.2.0,M11.1.0") #define TZ_America_Indiana_Vincennes PSTR("EST5EDT,M3.2.0,M11.1.0") #define TZ_America_Indiana_Winamac PSTR("EST5EDT,M3.2.0,M11.1.0") +#define TZ_America_Indianapolis PSTR("EST5EDT,M3.2.0,M11.1.0") #define TZ_America_Inuvik PSTR("MST7MDT,M3.2.0,M11.1.0") #define TZ_America_Iqaluit PSTR("EST5EDT,M3.2.0,M11.1.0") #define TZ_America_Jamaica PSTR("EST5") +#define TZ_America_Jujuy PSTR("<-03>3") #define TZ_America_Juneau PSTR("AKST9AKDT,M3.2.0,M11.1.0") #define TZ_America_Kentucky_Louisville PSTR("EST5EDT,M3.2.0,M11.1.0") #define TZ_America_Kentucky_Monticello PSTR("EST5EDT,M3.2.0,M11.1.0") +#define TZ_America_Knox_IN PSTR("CST6CDT,M3.2.0,M11.1.0") #define TZ_America_Kralendijk PSTR("AST4") #define TZ_America_La_Paz PSTR("<-04>4") #define TZ_America_Lima PSTR("<-05>5") #define TZ_America_Los_Angeles PSTR("PST8PDT,M3.2.0,M11.1.0") +#define TZ_America_Louisville PSTR("EST5EDT,M3.2.0,M11.1.0") #define TZ_America_Lower_Princes PSTR("AST4") #define TZ_America_Maceio PSTR("<-03>3") #define TZ_America_Managua PSTR("CST6") @@ -153,14 +161,15 @@ #define TZ_America_Marigot PSTR("AST4") #define TZ_America_Martinique PSTR("AST4") #define TZ_America_Matamoros PSTR("CST6CDT,M3.2.0,M11.1.0") -#define TZ_America_Mazatlan PSTR("MST7MDT,M4.1.0,M10.5.0") +#define TZ_America_Mazatlan PSTR("MST7") +#define TZ_America_Mendoza PSTR("<-03>3") #define TZ_America_Menominee PSTR("CST6CDT,M3.2.0,M11.1.0") -#define TZ_America_Merida PSTR("CST6CDT,M4.1.0,M10.5.0") +#define TZ_America_Merida PSTR("CST6") #define TZ_America_Metlakatla PSTR("AKST9AKDT,M3.2.0,M11.1.0") -#define TZ_America_Mexico_City PSTR("CST6CDT,M4.1.0,M10.5.0") +#define TZ_America_Mexico_City PSTR("CST6") #define TZ_America_Miquelon PSTR("<-03>3<-02>,M3.2.0,M11.1.0") #define TZ_America_Moncton PSTR("AST4ADT,M3.2.0,M11.1.0") -#define TZ_America_Monterrey PSTR("CST6CDT,M4.1.0,M10.5.0") +#define TZ_America_Monterrey PSTR("CST6") #define TZ_America_Montevideo PSTR("<-03>3") #define TZ_America_Montreal PSTR("EST5EDT,M3.2.0,M11.1.0") #define TZ_America_Montserrat PSTR("AST4") @@ -172,14 +181,15 @@ #define TZ_America_North_Dakota_Beulah PSTR("CST6CDT,M3.2.0,M11.1.0") #define TZ_America_North_Dakota_Center PSTR("CST6CDT,M3.2.0,M11.1.0") #define TZ_America_North_Dakota_New_Salem PSTR("CST6CDT,M3.2.0,M11.1.0") -#define TZ_America_Nuuk PSTR("<-03>3<-02>,M3.5.0/-2,M10.5.0/-1") -#define TZ_America_Ojinaga PSTR("MST7MDT,M3.2.0,M11.1.0") +#define TZ_America_Nuuk PSTR("<-02>2<-01>,M3.5.0/-1,M10.5.0/0") +#define TZ_America_Ojinaga PSTR("CST6CDT,M3.2.0,M11.1.0") #define TZ_America_Panama PSTR("EST5") #define TZ_America_Pangnirtung PSTR("EST5EDT,M3.2.0,M11.1.0") #define TZ_America_Paramaribo PSTR("<-03>3") #define TZ_America_Phoenix PSTR("MST7") #define TZ_America_PortmaumPrince PSTR("EST5EDT,M3.2.0,M11.1.0") #define TZ_America_Port_of_Spain PSTR("AST4") +#define TZ_America_Porto_Acre PSTR("<-05>5") #define TZ_America_Porto_Velho PSTR("<-04>4") #define TZ_America_Puerto_Rico PSTR("AST4") #define TZ_America_Punta_Arenas PSTR("<-03>3") @@ -189,11 +199,14 @@ #define TZ_America_Regina PSTR("CST6") #define TZ_America_Resolute PSTR("CST6CDT,M3.2.0,M11.1.0") #define TZ_America_Rio_Branco PSTR("<-05>5") +#define TZ_America_Rosario PSTR("<-03>3") +#define TZ_America_Santa_Isabel PSTR("PST8PDT,M3.2.0,M11.1.0") #define TZ_America_Santarem PSTR("<-03>3") #define TZ_America_Santiago PSTR("<-04>4<-03>,M9.1.6/24,M4.1.6/24") #define TZ_America_Santo_Domingo PSTR("AST4") #define TZ_America_Sao_Paulo PSTR("<-03>3") -#define TZ_America_Scoresbysund PSTR("<-01>1<+00>,M3.5.0/0,M10.5.0/1") +#define TZ_America_Scoresbysund PSTR("<-02>2<-01>,M3.5.0/-1,M10.5.0/0") +#define TZ_America_Shiprock PSTR("MST7MDT,M3.2.0,M11.1.0") #define TZ_America_Sitka PSTR("AKST9AKDT,M3.2.0,M11.1.0") #define TZ_America_St_Barthelemy PSTR("AST4") #define TZ_America_St_Johns PSTR("NST3:30NDT,M3.2.0,M11.1.0") @@ -209,11 +222,12 @@ #define TZ_America_Toronto PSTR("EST5EDT,M3.2.0,M11.1.0") #define TZ_America_Tortola PSTR("AST4") #define TZ_America_Vancouver PSTR("PST8PDT,M3.2.0,M11.1.0") +#define TZ_America_Virgin PSTR("AST4") #define TZ_America_Whitehorse PSTR("MST7") #define TZ_America_Winnipeg PSTR("CST6CDT,M3.2.0,M11.1.0") #define TZ_America_Yakutat PSTR("AKST9AKDT,M3.2.0,M11.1.0") #define TZ_America_Yellowknife PSTR("MST7MDT,M3.2.0,M11.1.0") -#define TZ_Antarctica_Casey PSTR("<+11>-11") +#define TZ_Antarctica_Casey PSTR("<+08>-8") #define TZ_Antarctica_Davis PSTR("<+07>-7") #define TZ_Antarctica_DumontDUrville PSTR("<+10>-10") #define TZ_Antarctica_Macquarie PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3") @@ -221,17 +235,19 @@ #define TZ_Antarctica_McMurdo PSTR("NZST-12NZDT,M9.5.0,M4.1.0/3") #define TZ_Antarctica_Palmer PSTR("<-03>3") #define TZ_Antarctica_Rothera PSTR("<-03>3") +#define TZ_Antarctica_South_Pole PSTR("NZST-12NZDT,M9.5.0,M4.1.0/3") #define TZ_Antarctica_Syowa PSTR("<+03>-3") #define TZ_Antarctica_Troll PSTR("<+00>0<+02>-2,M3.5.0/1,M10.5.0/3") -#define TZ_Antarctica_Vostok PSTR("<+06>-6") +#define TZ_Antarctica_Vostok PSTR("<+05>-5") #define TZ_Arctic_Longyearbyen PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Asia_Aden PSTR("<+03>-3") #define TZ_Asia_Almaty PSTR("<+06>-6") -#define TZ_Asia_Amman PSTR("EET-2EEST,M3.5.4/24,M10.5.5/1") +#define TZ_Asia_Amman PSTR("<+03>-3") #define TZ_Asia_Anadyr PSTR("<+12>-12") #define TZ_Asia_Aqtau PSTR("<+05>-5") #define TZ_Asia_Aqtobe PSTR("<+05>-5") #define TZ_Asia_Ashgabat PSTR("<+05>-5") +#define TZ_Asia_Ashkhabad PSTR("<+05>-5") #define TZ_Asia_Atyrau PSTR("<+05>-5") #define TZ_Asia_Baghdad PSTR("<+03>-3") #define TZ_Asia_Bahrain PSTR("<+03>-3") @@ -241,34 +257,43 @@ #define TZ_Asia_Beirut PSTR("EET-2EEST,M3.5.0/0,M10.5.0/0") #define TZ_Asia_Bishkek PSTR("<+06>-6") #define TZ_Asia_Brunei PSTR("<+08>-8") +#define TZ_Asia_Calcutta PSTR("IST-5:30") #define TZ_Asia_Chita PSTR("<+09>-9") #define TZ_Asia_Choibalsan PSTR("<+08>-8") +#define TZ_Asia_Chongqing PSTR("CST-8") +#define TZ_Asia_Chungking PSTR("CST-8") #define TZ_Asia_Colombo PSTR("<+0530>-5:30") -#define TZ_Asia_Damascus PSTR("EET-2EEST,M3.5.5/0,M10.5.5/0") +#define TZ_Asia_Dacca PSTR("<+06>-6") +#define TZ_Asia_Damascus PSTR("<+03>-3") #define TZ_Asia_Dhaka PSTR("<+06>-6") #define TZ_Asia_Dili PSTR("<+09>-9") #define TZ_Asia_Dubai PSTR("<+04>-4") #define TZ_Asia_Dushanbe PSTR("<+05>-5") #define TZ_Asia_Famagusta PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4") -#define TZ_Asia_Gaza PSTR("EET-2EEST,M3.4.4/48,M10.4.4/49") -#define TZ_Asia_Hebron PSTR("EET-2EEST,M3.4.4/48,M10.4.4/49") +#define TZ_Asia_Gaza PSTR("EET-2EEST,M3.4.4/50,M10.4.4/50") +#define TZ_Asia_Harbin PSTR("CST-8") +#define TZ_Asia_Hebron PSTR("EET-2EEST,M3.4.4/50,M10.4.4/50") #define TZ_Asia_Ho_Chi_Minh PSTR("<+07>-7") #define TZ_Asia_Hong_Kong PSTR("HKT-8") #define TZ_Asia_Hovd PSTR("<+07>-7") #define TZ_Asia_Irkutsk PSTR("<+08>-8") +#define TZ_Asia_Istanbul PSTR("<+03>-3") #define TZ_Asia_Jakarta PSTR("WIB-7") #define TZ_Asia_Jayapura PSTR("WIT-9") #define TZ_Asia_Jerusalem PSTR("IST-2IDT,M3.4.4/26,M10.5.0") #define TZ_Asia_Kabul PSTR("<+0430>-4:30") #define TZ_Asia_Kamchatka PSTR("<+12>-12") #define TZ_Asia_Karachi PSTR("PKT-5") +#define TZ_Asia_Kashgar PSTR("<+06>-6") #define TZ_Asia_Kathmandu PSTR("<+0545>-5:45") +#define TZ_Asia_Katmandu PSTR("<+0545>-5:45") #define TZ_Asia_Khandyga PSTR("<+09>-9") #define TZ_Asia_Kolkata PSTR("IST-5:30") #define TZ_Asia_Krasnoyarsk PSTR("<+07>-7") #define TZ_Asia_Kuala_Lumpur PSTR("<+08>-8") #define TZ_Asia_Kuching PSTR("<+08>-8") #define TZ_Asia_Kuwait PSTR("<+03>-3") +#define TZ_Asia_Macao PSTR("CST-8") #define TZ_Asia_Macau PSTR("CST-8") #define TZ_Asia_Magadan PSTR("<+11>-11") #define TZ_Asia_Makassar PSTR("WITA-8") @@ -283,8 +308,11 @@ #define TZ_Asia_Pontianak PSTR("WIB-7") #define TZ_Asia_Pyongyang PSTR("KST-9") #define TZ_Asia_Qatar PSTR("<+03>-3") +#define TZ_Asia_Qostanay PSTR("<+06>-6") #define TZ_Asia_Qyzylorda PSTR("<+05>-5") +#define TZ_Asia_Rangoon PSTR("<+0630>-6:30") #define TZ_Asia_Riyadh PSTR("<+03>-3") +#define TZ_Asia_Saigon PSTR("<+07>-7") #define TZ_Asia_Sakhalin PSTR("<+11>-11") #define TZ_Asia_Samarkand PSTR("<+05>-5") #define TZ_Asia_Seoul PSTR("KST-9") @@ -294,11 +322,15 @@ #define TZ_Asia_Taipei PSTR("CST-8") #define TZ_Asia_Tashkent PSTR("<+05>-5") #define TZ_Asia_Tbilisi PSTR("<+04>-4") -#define TZ_Asia_Tehran PSTR("<+0330>-3:30<+0430>,J79/24,J263/24") +#define TZ_Asia_Tehran PSTR("<+0330>-3:30") +#define TZ_Asia_Tel_Aviv PSTR("IST-2IDT,M3.4.4/26,M10.5.0") +#define TZ_Asia_Thimbu PSTR("<+06>-6") #define TZ_Asia_Thimphu PSTR("<+06>-6") #define TZ_Asia_Tokyo PSTR("JST-9") #define TZ_Asia_Tomsk PSTR("<+07>-7") +#define TZ_Asia_Ujung_Pandang PSTR("WITA-8") #define TZ_Asia_Ulaanbaatar PSTR("<+08>-8") +#define TZ_Asia_Ulan_Bator PSTR("<+08>-8") #define TZ_Asia_Urumqi PSTR("<+06>-6") #define TZ_Asia_UstmNera PSTR("<+10>-10") #define TZ_Asia_Vientiane PSTR("<+07>-7") @@ -311,28 +343,99 @@ #define TZ_Atlantic_Bermuda PSTR("AST4ADT,M3.2.0,M11.1.0") #define TZ_Atlantic_Canary PSTR("WET0WEST,M3.5.0/1,M10.5.0") #define TZ_Atlantic_Cape_Verde PSTR("<-01>1") +#define TZ_Atlantic_Faeroe PSTR("WET0WEST,M3.5.0/1,M10.5.0") #define TZ_Atlantic_Faroe PSTR("WET0WEST,M3.5.0/1,M10.5.0") +#define TZ_Atlantic_Jan_Mayen PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Atlantic_Madeira PSTR("WET0WEST,M3.5.0/1,M10.5.0") #define TZ_Atlantic_Reykjavik PSTR("GMT0") #define TZ_Atlantic_South_Georgia PSTR("<-02>2") -#define TZ_Atlantic_Stanley PSTR("<-03>3") #define TZ_Atlantic_St_Helena PSTR("GMT0") +#define TZ_Atlantic_Stanley PSTR("<-03>3") +#define TZ_Australia_ACT PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3") #define TZ_Australia_Adelaide PSTR("ACST-9:30ACDT,M10.1.0,M4.1.0/3") #define TZ_Australia_Brisbane PSTR("AEST-10") #define TZ_Australia_Broken_Hill PSTR("ACST-9:30ACDT,M10.1.0,M4.1.0/3") +#define TZ_Australia_Canberra PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3") #define TZ_Australia_Currie PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3") #define TZ_Australia_Darwin PSTR("ACST-9:30") #define TZ_Australia_Eucla PSTR("<+0845>-8:45") #define TZ_Australia_Hobart PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3") +#define TZ_Australia_LHI PSTR("<+1030>-10:30<+11>-11,M10.1.0,M4.1.0") #define TZ_Australia_Lindeman PSTR("AEST-10") #define TZ_Australia_Lord_Howe PSTR("<+1030>-10:30<+11>-11,M10.1.0,M4.1.0") #define TZ_Australia_Melbourne PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3") +#define TZ_Australia_NSW PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3") +#define TZ_Australia_North PSTR("ACST-9:30") #define TZ_Australia_Perth PSTR("AWST-8") +#define TZ_Australia_Queensland PSTR("AEST-10") +#define TZ_Australia_South PSTR("ACST-9:30ACDT,M10.1.0,M4.1.0/3") #define TZ_Australia_Sydney PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3") +#define TZ_Australia_Tasmania PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3") +#define TZ_Australia_Victoria PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3") +#define TZ_Australia_West PSTR("AWST-8") +#define TZ_Australia_Yancowinna PSTR("ACST-9:30ACDT,M10.1.0,M4.1.0/3") +#define TZ_Brazil_Acre PSTR("<-05>5") +#define TZ_Brazil_DeNoronha PSTR("<-02>2") +#define TZ_Brazil_East PSTR("<-03>3") +#define TZ_Brazil_West PSTR("<-04>4") +#define TZ_CET PSTR("CET-1CEST,M3.5.0,M10.5.0/3") +#define TZ_CST6CDT PSTR("CST6CDT,M3.2.0,M11.1.0") +#define TZ_Canada_Atlantic PSTR("AST4ADT,M3.2.0,M11.1.0") +#define TZ_Canada_Central PSTR("CST6CDT,M3.2.0,M11.1.0") +#define TZ_Canada_Eastern PSTR("EST5EDT,M3.2.0,M11.1.0") +#define TZ_Canada_Mountain PSTR("MST7MDT,M3.2.0,M11.1.0") +#define TZ_Canada_Newfoundland PSTR("NST3:30NDT,M3.2.0,M11.1.0") +#define TZ_Canada_Pacific PSTR("PST8PDT,M3.2.0,M11.1.0") +#define TZ_Canada_Saskatchewan PSTR("CST6") +#define TZ_Canada_Yukon PSTR("MST7") +#define TZ_Chile_Continental PSTR("<-04>4<-03>,M9.1.6/24,M4.1.6/24") +#define TZ_Chile_EasterIsland PSTR("<-06>6<-05>,M9.1.6/22,M4.1.6/22") +#define TZ_Cuba PSTR("CST5CDT,M3.2.0/0,M11.1.0/1") +#define TZ_EET PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4") +#define TZ_EST PSTR("EST5") +#define TZ_EST5EDT PSTR("EST5EDT,M3.2.0,M11.1.0") +#define TZ_Egypt PSTR("EET-2EEST,M4.5.5/0,M10.5.4/24") +#define TZ_Eire PSTR("IST-1GMT0,M10.5.0,M3.5.0/1") +#define TZ_Etc_GMT PSTR("GMT0") +#define TZ_Etc_GMTp0 PSTR("GMT0") +#define TZ_Etc_GMTp1 PSTR("<-01>1") +#define TZ_Etc_GMTp10 PSTR("<-10>10") +#define TZ_Etc_GMTp11 PSTR("<-11>11") +#define TZ_Etc_GMTp12 PSTR("<-12>12") +#define TZ_Etc_GMTp2 PSTR("<-02>2") +#define TZ_Etc_GMTp3 PSTR("<-03>3") +#define TZ_Etc_GMTp4 PSTR("<-04>4") +#define TZ_Etc_GMTp5 PSTR("<-05>5") +#define TZ_Etc_GMTp6 PSTR("<-06>6") +#define TZ_Etc_GMTp7 PSTR("<-07>7") +#define TZ_Etc_GMTp8 PSTR("<-08>8") +#define TZ_Etc_GMTp9 PSTR("<-09>9") +#define TZ_Etc_GMTm0 PSTR("GMT0") +#define TZ_Etc_GMTm1 PSTR("<+01>-1") +#define TZ_Etc_GMTm10 PSTR("<+10>-10") +#define TZ_Etc_GMTm11 PSTR("<+11>-11") +#define TZ_Etc_GMTm12 PSTR("<+12>-12") +#define TZ_Etc_GMTm13 PSTR("<+13>-13") +#define TZ_Etc_GMTm14 PSTR("<+14>-14") +#define TZ_Etc_GMTm2 PSTR("<+02>-2") +#define TZ_Etc_GMTm3 PSTR("<+03>-3") +#define TZ_Etc_GMTm4 PSTR("<+04>-4") +#define TZ_Etc_GMTm5 PSTR("<+05>-5") +#define TZ_Etc_GMTm6 PSTR("<+06>-6") +#define TZ_Etc_GMTm7 PSTR("<+07>-7") +#define TZ_Etc_GMTm8 PSTR("<+08>-8") +#define TZ_Etc_GMTm9 PSTR("<+09>-9") +#define TZ_Etc_GMT0 PSTR("GMT0") +#define TZ_Etc_Greenwich PSTR("GMT0") +#define TZ_Etc_UCT PSTR("UTC0") +#define TZ_Etc_UTC PSTR("UTC0") +#define TZ_Etc_Universal PSTR("UTC0") +#define TZ_Etc_Zulu PSTR("UTC0") #define TZ_Europe_Amsterdam PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Europe_Andorra PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Europe_Astrakhan PSTR("<+04>-4") #define TZ_Europe_Athens PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4") +#define TZ_Europe_Belfast PSTR("GMT0BST,M3.5.0/1,M10.5.0") #define TZ_Europe_Belgrade PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Europe_Berlin PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Europe_Bratislava PSTR("CET-1CEST,M3.5.0,M10.5.0/3") @@ -351,7 +454,8 @@ #define TZ_Europe_Jersey PSTR("GMT0BST,M3.5.0/1,M10.5.0") #define TZ_Europe_Kaliningrad PSTR("EET-2") #define TZ_Europe_Kiev PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4") -#define TZ_Europe_Kirov PSTR("<+03>-3") +#define TZ_Europe_Kirov PSTR("MSK-3") +#define TZ_Europe_Kyiv PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4") #define TZ_Europe_Lisbon PSTR("WET0WEST,M3.5.0/1,M10.5.0") #define TZ_Europe_Ljubljana PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Europe_London PSTR("GMT0BST,M3.5.0/1,M10.5.0") @@ -362,6 +466,7 @@ #define TZ_Europe_Minsk PSTR("<+03>-3") #define TZ_Europe_Monaco PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Europe_Moscow PSTR("MSK-3") +#define TZ_Europe_Nicosia PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4") #define TZ_Europe_Oslo PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Europe_Paris PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Europe_Podgorica PSTR("CET-1CEST,M3.5.0,M10.5.0/3") @@ -378,17 +483,31 @@ #define TZ_Europe_Stockholm PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Europe_Tallinn PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4") #define TZ_Europe_Tirane PSTR("CET-1CEST,M3.5.0,M10.5.0/3") +#define TZ_Europe_Tiraspol PSTR("EET-2EEST,M3.5.0,M10.5.0/3") #define TZ_Europe_Ulyanovsk PSTR("<+04>-4") #define TZ_Europe_Uzhgorod PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4") +#define TZ_Europe_Uzhhorod PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4") #define TZ_Europe_Vaduz PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Europe_Vatican PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Europe_Vienna PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Europe_Vilnius PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4") -#define TZ_Europe_Volgograd PSTR("<+03>-3") +#define TZ_Europe_Volgograd PSTR("MSK-3") #define TZ_Europe_Warsaw PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Europe_Zagreb PSTR("CET-1CEST,M3.5.0,M10.5.0/3") +#define TZ_Europe_Zaporizhzhia PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4") #define TZ_Europe_Zaporozhye PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4") #define TZ_Europe_Zurich PSTR("CET-1CEST,M3.5.0,M10.5.0/3") +#define TZ_Factory PSTR("<-00>0") +#define TZ_GB PSTR("GMT0BST,M3.5.0/1,M10.5.0") +#define TZ_GBmEire PSTR("GMT0BST,M3.5.0/1,M10.5.0") +#define TZ_GMT PSTR("GMT0") +#define TZ_GMTp0 PSTR("GMT0") +#define TZ_GMTm0 PSTR("GMT0") +#define TZ_GMT0 PSTR("GMT0") +#define TZ_Greenwich PSTR("GMT0") +#define TZ_HST PSTR("HST10") +#define TZ_Hongkong PSTR("HKT-8") +#define TZ_Iceland PSTR("GMT0") #define TZ_Indian_Antananarivo PSTR("EAT-3") #define TZ_Indian_Chagos PSTR("<+06>-6") #define TZ_Indian_Christmas PSTR("<+07>-7") @@ -400,7 +519,24 @@ #define TZ_Indian_Mauritius PSTR("<+04>-4") #define TZ_Indian_Mayotte PSTR("EAT-3") #define TZ_Indian_Reunion PSTR("<+04>-4") -#define TZ_Pacific_Apia PSTR("<+13>-13<+14>,M9.5.0/3,M4.1.0/4") +#define TZ_Iran PSTR("<+0330>-3:30") +#define TZ_Israel PSTR("IST-2IDT,M3.4.4/26,M10.5.0") +#define TZ_Jamaica PSTR("EST5") +#define TZ_Japan PSTR("JST-9") +#define TZ_Kwajalein PSTR("<+12>-12") +#define TZ_Libya PSTR("EET-2") +#define TZ_MET PSTR("MET-1MEST,M3.5.0,M10.5.0/3") +#define TZ_MST PSTR("MST7") +#define TZ_MST7MDT PSTR("MST7MDT,M3.2.0,M11.1.0") +#define TZ_Mexico_BajaNorte PSTR("PST8PDT,M3.2.0,M11.1.0") +#define TZ_Mexico_BajaSur PSTR("MST7") +#define TZ_Mexico_General PSTR("CST6") +#define TZ_NZ PSTR("NZST-12NZDT,M9.5.0,M4.1.0/3") +#define TZ_NZmCHAT PSTR("<+1245>-12:45<+1345>,M9.5.0/2:45,M4.1.0/3:45") +#define TZ_Navajo PSTR("MST7MDT,M3.2.0,M11.1.0") +#define TZ_PRC PSTR("CST-8") +#define TZ_PST8PDT PSTR("PST8PDT,M3.2.0,M11.1.0") +#define TZ_Pacific_Apia PSTR("<+13>-13") #define TZ_Pacific_Auckland PSTR("NZST-12NZDT,M9.5.0,M4.1.0/3") #define TZ_Pacific_Bougainville PSTR("<+11>-11") #define TZ_Pacific_Chatham PSTR("<+1245>-12:45<+1345>,M9.5.0/2:45,M4.1.0/3:45") @@ -409,13 +545,15 @@ #define TZ_Pacific_Efate PSTR("<+11>-11") #define TZ_Pacific_Enderbury PSTR("<+13>-13") #define TZ_Pacific_Fakaofo PSTR("<+13>-13") -#define TZ_Pacific_Fiji PSTR("<+12>-12<+13>,M11.2.0,M1.2.3/99") +#define TZ_Pacific_Fiji PSTR("<+12>-12") #define TZ_Pacific_Funafuti PSTR("<+12>-12") #define TZ_Pacific_Galapagos PSTR("<-06>6") #define TZ_Pacific_Gambier PSTR("<-09>9") #define TZ_Pacific_Guadalcanal PSTR("<+11>-11") #define TZ_Pacific_Guam PSTR("ChST-10") #define TZ_Pacific_Honolulu PSTR("HST10") +#define TZ_Pacific_Johnston PSTR("HST10") +#define TZ_Pacific_Kanton PSTR("<+13>-13") #define TZ_Pacific_Kiritimati PSTR("<+14>-14") #define TZ_Pacific_Kosrae PSTR("<+11>-11") #define TZ_Pacific_Kwajalein PSTR("<+12>-12") @@ -430,48 +568,39 @@ #define TZ_Pacific_Palau PSTR("<+09>-9") #define TZ_Pacific_Pitcairn PSTR("<-08>8") #define TZ_Pacific_Pohnpei PSTR("<+11>-11") +#define TZ_Pacific_Ponape PSTR("<+11>-11") #define TZ_Pacific_Port_Moresby PSTR("<+10>-10") #define TZ_Pacific_Rarotonga PSTR("<-10>10") #define TZ_Pacific_Saipan PSTR("ChST-10") +#define TZ_Pacific_Samoa PSTR("SST11") #define TZ_Pacific_Tahiti PSTR("<-10>10") #define TZ_Pacific_Tarawa PSTR("<+12>-12") #define TZ_Pacific_Tongatapu PSTR("<+13>-13") +#define TZ_Pacific_Truk PSTR("<+10>-10") #define TZ_Pacific_Wake PSTR("<+12>-12") #define TZ_Pacific_Wallis PSTR("<+12>-12") -#define TZ_Etc_GMT PSTR("GMT0") -#define TZ_Etc_GMTm0 PSTR("GMT0") -#define TZ_Etc_GMTm1 PSTR("<+01>-1") -#define TZ_Etc_GMTm2 PSTR("<+02>-2") -#define TZ_Etc_GMTm3 PSTR("<+03>-3") -#define TZ_Etc_GMTm4 PSTR("<+04>-4") -#define TZ_Etc_GMTm5 PSTR("<+05>-5") -#define TZ_Etc_GMTm6 PSTR("<+06>-6") -#define TZ_Etc_GMTm7 PSTR("<+07>-7") -#define TZ_Etc_GMTm8 PSTR("<+08>-8") -#define TZ_Etc_GMTm9 PSTR("<+09>-9") -#define TZ_Etc_GMTm10 PSTR("<+10>-10") -#define TZ_Etc_GMTm11 PSTR("<+11>-11") -#define TZ_Etc_GMTm12 PSTR("<+12>-12") -#define TZ_Etc_GMTm13 PSTR("<+13>-13") -#define TZ_Etc_GMTm14 PSTR("<+14>-14") -#define TZ_Etc_GMT0 PSTR("GMT0") -#define TZ_Etc_GMTp0 PSTR("GMT0") -#define TZ_Etc_GMTp1 PSTR("<-01>1") -#define TZ_Etc_GMTp2 PSTR("<-02>2") -#define TZ_Etc_GMTp3 PSTR("<-03>3") -#define TZ_Etc_GMTp4 PSTR("<-04>4") -#define TZ_Etc_GMTp5 PSTR("<-05>5") -#define TZ_Etc_GMTp6 PSTR("<-06>6") -#define TZ_Etc_GMTp7 PSTR("<-07>7") -#define TZ_Etc_GMTp8 PSTR("<-08>8") -#define TZ_Etc_GMTp9 PSTR("<-09>9") -#define TZ_Etc_GMTp10 PSTR("<-10>10") -#define TZ_Etc_GMTp11 PSTR("<-11>11") -#define TZ_Etc_GMTp12 PSTR("<-12>12") -#define TZ_Etc_UCT PSTR("UTC0") -#define TZ_Etc_UTC PSTR("UTC0") -#define TZ_Etc_Greenwich PSTR("GMT0") -#define TZ_Etc_Universal PSTR("UTC0") -#define TZ_Etc_Zulu PSTR("UTC0") - -#endif // TZDB_H +#define TZ_Pacific_Yap PSTR("<+10>-10") +#define TZ_Poland PSTR("CET-1CEST,M3.5.0,M10.5.0/3") +#define TZ_Portugal PSTR("WET0WEST,M3.5.0/1,M10.5.0") +#define TZ_ROC PSTR("CST-8") +#define TZ_ROK PSTR("KST-9") +#define TZ_Singapore PSTR("<+08>-8") +#define TZ_Turkey PSTR("<+03>-3") +#define TZ_UCT PSTR("UTC0") +#define TZ_US_Alaska PSTR("AKST9AKDT,M3.2.0,M11.1.0") +#define TZ_US_Aleutian PSTR("HST10HDT,M3.2.0,M11.1.0") +#define TZ_US_Arizona PSTR("MST7") +#define TZ_US_Central PSTR("CST6CDT,M3.2.0,M11.1.0") +#define TZ_US_EastmIndiana PSTR("EST5EDT,M3.2.0,M11.1.0") +#define TZ_US_Eastern PSTR("EST5EDT,M3.2.0,M11.1.0") +#define TZ_US_Hawaii PSTR("HST10") +#define TZ_US_IndianamStarke PSTR("CST6CDT,M3.2.0,M11.1.0") +#define TZ_US_Michigan PSTR("EST5EDT,M3.2.0,M11.1.0") +#define TZ_US_Mountain PSTR("MST7MDT,M3.2.0,M11.1.0") +#define TZ_US_Pacific PSTR("PST8PDT,M3.2.0,M11.1.0") +#define TZ_US_Samoa PSTR("SST11") +#define TZ_UTC PSTR("UTC0") +#define TZ_Universal PSTR("UTC0") +#define TZ_WmSU PSTR("MSK-3") +#define TZ_WET PSTR("WET0WEST,M3.5.0/1,M10.5.0") +#define TZ_Zulu PSTR("UTC0") diff --git a/cores/esp8266/Udp.h b/cores/esp8266/Udp.h index 6c8ee4b385..37f2fb922b 100644 --- a/cores/esp8266/Udp.h +++ b/cores/esp8266/Udp.h @@ -43,6 +43,7 @@ class UDP: public Stream { public: virtual ~UDP() {}; virtual uint8_t begin(uint16_t) =0; // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use + virtual uint8_t beginMulticast(IPAddress, uint16_t) { return 0; } // initialize, start listening on specified multicast IP address and port. Returns 1 if successful, 0 on failure virtual void stop() =0; // Finish with the UDP socket // Sending UDP packets diff --git a/cores/esp8266/Updater.cpp b/cores/esp8266/Updater.cpp index c1584cf982..ef79a5cbf3 100644 --- a/cores/esp8266/Updater.cpp +++ b/cores/esp8266/Updater.cpp @@ -1,9 +1,12 @@ +#include #include "Updater.h" #include "eboot_command.h" #include #include #include "StackThunk.h" +#include + //#define DEBUG_UPDATER Serial #include @@ -41,21 +44,22 @@ UpdaterClass::~UpdaterClass() #endif } -UpdaterClass& UpdaterClass::onProgress(THandlerFunction_Progress fn) { - _progress_callback = fn; - return *this; -} - -void UpdaterClass::_reset() { - if (_buffer) +void UpdaterClass::_reset(bool callback) { + if (_buffer) { delete[] _buffer; - _buffer = 0; + } + + _buffer = nullptr; _bufferLen = 0; _startAddress = 0; _currentAddress = 0; _size = 0; _command = U_FLASH; + if (callback && _end_callback) { + _end_callback(); + } + if(_ledPin != -1) { digitalWrite(_ledPin, !_ledOn); // off } @@ -66,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; } @@ -82,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.")); @@ -129,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; } } @@ -158,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; } @@ -170,7 +176,13 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) { } else { _bufferSize = 256; } - _buffer = new uint8_t[_bufferSize]; + _buffer = new (std::nothrow) uint8_t[_bufferSize]; + if (!_buffer) { + _setError(UPDATE_ERROR_OOM); + _reset(false); + return false; + } + _command = command; #ifdef DEBUG_UPDATER @@ -182,6 +194,11 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) { if (!_verify) { _md5.begin(); } + + if (_start_callback) { + _start_callback(); + } + return true; } @@ -223,58 +240,88 @@ bool UpdaterClass::end(bool evenIfRemaining){ _size = progress(); } - uint32_t sigLen = 0; if (_verify) { - ESP.flashRead(_startAddress + _size - sizeof(uint32_t), &sigLen, sizeof(uint32_t)); + // If expectedSigLen is non-zero, we expect the last four bytes of the buffer to + // contain a matching length field, preceded by the bytes of the signature itself. + // But if expectedSigLen is zero, we expect neither a signature nor a length field; + static constexpr uint32_t SigSize = sizeof(uint32_t); + const uint32_t expectedSigLen = _verify->length(); + const uint32_t sigLenAddr = _startAddress + _size - SigSize; + uint32_t sigLen = 0; + +#ifdef DEBUG_UPDATER + DEBUG_UPDATER.printf_P(PSTR("[Updater] expected sigLen: %u\n"), expectedSigLen); +#endif + if (expectedSigLen > 0) { + ESP.flashRead(sigLenAddr, &sigLen, SigSize); #ifdef DEBUG_UPDATER - DEBUG_UPDATER.printf_P(PSTR("[Updater] sigLen: %d\n"), sigLen); + DEBUG_UPDATER.printf_P(PSTR("[Updater] sigLen from flash: %u\n"), sigLen); #endif - if (sigLen != _verify->length()) { + } + + if (sigLen != expectedSigLen) { _setError(UPDATE_ERROR_SIGN); _reset(); return false; } - int binSize = _size - sigLen - sizeof(uint32_t) /* The siglen word */; - _hash->begin(); + auto binSize = _size; + if (expectedSigLen > 0) { + if (binSize < (sigLen + SigSize)) { + _setError(UPDATE_ERROR_SIGN); + _reset(); + return false; + } + binSize -= (sigLen + SigSize); #ifdef DEBUG_UPDATER - DEBUG_UPDATER.printf_P(PSTR("[Updater] Adjusted binsize: %d\n"), binSize); + DEBUG_UPDATER.printf_P(PSTR("[Updater] Adjusted size (without the signature and sigLen): %zu\n"), binSize); #endif - // Calculate the MD5 and hash using proper size - uint8_t buff[128] __attribute__((aligned(4))); - for(int i = 0; i < binSize; i += sizeof(buff)) { - ESP.flashRead(_startAddress + i, (uint32_t *)buff, sizeof(buff)); - size_t read = std::min((int)sizeof(buff), binSize - i); - _hash->add(buff, read); + } + + // Calculate hash of the payload, 128 bytes at a time + alignas(alignof(uint32_t)) uint8_t buff[128]; + + _hash->begin(); + for (uint32_t offset = 0; offset < binSize; offset += sizeof(buff)) { + auto len = std::min(sizeof(buff), binSize - offset); + ESP.flashRead(_startAddress + offset, buff, len); + _hash->add(buff, len); } _hash->end(); + #ifdef DEBUG_UPDATER - unsigned char *ret = (unsigned char *)_hash->hash(); - DEBUG_UPDATER.printf_P(PSTR("[Updater] Computed Hash:")); - for (int i=0; i<_hash->len(); i++) DEBUG_UPDATER.printf(" %02x", ret[i]); - DEBUG_UPDATER.printf("\n"); + auto debugByteArray = [](const char *name, const unsigned char *hash, int len) { + DEBUG_UPDATER.printf_P("[Updater] %s:", name); + for (int i = 0; i < len; ++i) { + DEBUG_UPDATER.printf(" %02x", hash[i]); + } + DEBUG_UPDATER.printf("\n"); + }; + debugByteArray(PSTR("Computed Hash"), + reinterpret_cast(_hash->hash()), + _hash->len()); #endif - uint8_t *sig = (uint8_t*)malloc(sigLen); - if (!sig) { - _setError(UPDATE_ERROR_SIGN); - _reset(); - return false; - } - ESP.flashRead(_startAddress + binSize, sig, sigLen); + + std::unique_ptr sig; + if (expectedSigLen > 0) { + const uint32_t sigAddr = _startAddress + binSize; + sig.reset(new (std::nothrow) uint8_t[sigLen]); + if (!sig) { + _setError(UPDATE_ERROR_OOM); + _reset(); + return false; + } + ESP.flashRead(sigAddr, sig.get(), sigLen); #ifdef DEBUG_UPDATER - DEBUG_UPDATER.printf_P(PSTR("[Updater] Received Signature:")); - for (size_t i=0; iverify(_hash, (void *)sig, sigLen)) { - free(sig); + } + if (!_verify->verify(_hash, sig.get(), sigLen)) { _setError(UPDATE_ERROR_SIGN); _reset(); return false; } - free(sig); + _size = binSize; // Adjust size to remove signature, not part of bin payload #ifdef DEBUG_UPDATER @@ -287,7 +334,7 @@ bool UpdaterClass::end(bool evenIfRemaining){ return false; } #ifdef DEBUG_UPDATER - else DEBUG_UPDATER.printf_P(PSTR("MD5 Success: %s\n"), _target_md5.c_str()); + else DEBUG_UPDATER.printf_P(PSTR("[Updater] MD5 Success: %s\n"), _target_md5.c_str()); #endif } @@ -359,7 +406,7 @@ bool UpdaterClass::_writeBuffer(){ modifyFlashMode = true; } } - + if (eraseResult) { if(!_async) yield(); writeResult = ESP.flashWrite(_currentAddress, _buffer, _bufferLen); @@ -443,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; } @@ -455,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; } @@ -467,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 @@ -541,45 +588,85 @@ size_t UpdaterClass::writeStream(Stream &data, uint16_t streamTimeout) { void UpdaterClass::_setError(int error){ _error = error; + if (_error_callback) { + _error_callback(error); + } #ifdef DEBUG_UPDATER printError(DEBUG_UPDATER); #endif _reset(); // Any error condition invalidates the entire update, so clear partial status } -void UpdaterClass::printError(Print &out){ - out.printf_P(PSTR("ERROR[%u]: "), _error); - if(_error == UPDATE_ERROR_OK){ - out.println(F("No Error")); - } else if(_error == UPDATE_ERROR_WRITE){ - out.println(F("Flash Write Failed")); - } else if(_error == UPDATE_ERROR_ERASE){ - out.println(F("Flash Erase Failed")); - } else if(_error == UPDATE_ERROR_READ){ - out.println(F("Flash Read Failed")); - } else if(_error == UPDATE_ERROR_SPACE){ - out.println(F("Not Enough Space")); - } else if(_error == UPDATE_ERROR_SIZE){ - out.println(F("Bad Size Given")); - } else if(_error == UPDATE_ERROR_STREAM){ - out.println(F("Stream Read Timeout")); - } else if(_error == UPDATE_ERROR_NO_DATA){ - out.println(F("No data supplied")); - } else if(_error == UPDATE_ERROR_MD5){ - out.printf_P(PSTR("MD5 Failed: expected:%s, calculated:%s\n"), _target_md5.c_str(), _md5.toString().c_str()); - } else if(_error == UPDATE_ERROR_SIGN){ - out.println(F("Signature verification failed")); - } else if(_error == UPDATE_ERROR_FLASH_CONFIG){ - out.printf_P(PSTR("Flash config wrong real: %d IDE: %d\n"), ESP.getFlashChipRealSize(), ESP.getFlashChipSize()); - } else if(_error == UPDATE_ERROR_NEW_FLASH_CONFIG){ - out.printf_P(PSTR("new Flash config wrong real: %d\n"), ESP.getFlashChipRealSize()); - } else if(_error == UPDATE_ERROR_MAGIC_BYTE){ - out.println(F("Magic byte is wrong, not 0xE9")); - } else if (_error == UPDATE_ERROR_BOOTSTRAP){ - out.println(F("Invalid bootstrapping state, reset ESP8266 before updating")); - } else { - out.println(F("UNKNOWN")); +String UpdaterClass::getErrorString() const { + String out; + + switch (_error) { + case UPDATE_ERROR_OK: + out = F("No Error"); + break; + case UPDATE_ERROR_WRITE: + out = F("Flash Write Failed"); + break; + case UPDATE_ERROR_ERASE: + out = F("Flash Erase Failed"); + break; + case UPDATE_ERROR_READ: + out = F("Flash Read Failed"); + break; + case UPDATE_ERROR_SPACE: + out = F("Not Enough Space"); + break; + case UPDATE_ERROR_SIZE: + out = F("Bad Size Given"); + break; + case UPDATE_ERROR_STREAM: + out = F("Stream Read Timeout"); + break; + case UPDATE_ERROR_MD5: + out += F("MD5 verification failed: "); + out += F("expected: ") + _target_md5; + out += F(", calculated: ") + _md5.toString(); + break; + case UPDATE_ERROR_FLASH_CONFIG: + out += F("Flash config wrong: "); + out += F("real: ") + String(ESP.getFlashChipRealSize(), 10); + out += F(", SDK: ") + String(ESP.getFlashChipSize(), 10); + break; + case UPDATE_ERROR_NEW_FLASH_CONFIG: + out += F("new Flash config wrong, real size: "); + out += String(ESP.getFlashChipRealSize(), 10); + break; + case UPDATE_ERROR_MAGIC_BYTE: + out = F("Magic byte is not 0xE9"); + break; + case UPDATE_ERROR_BOOTSTRAP: + out = F("Invalid bootstrapping state, reset ESP8266 before updating"); + break; + case UPDATE_ERROR_SIGN: + out = F("Signature verification failed"); + break; + case UPDATE_ERROR_NO_DATA: + out = F("No data supplied"); + break; + 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; } + + return out; +} + +void UpdaterClass::printError(Print &out){ + out.printf_P(PSTR("ERROR[%hhu]: %s\n"), _error, getErrorString().c_str()); } UpdaterClass Update; diff --git a/cores/esp8266/Updater.h b/cores/esp8266/Updater.h index 911ddf7784..7ee1d28311 100644 --- a/cores/esp8266/Updater.h +++ b/cores/esp8266/Updater.h @@ -20,6 +20,9 @@ #define UPDATE_ERROR_BOOTSTRAP (11) #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 @@ -51,8 +54,10 @@ class UpdaterVerifyClass { class UpdaterClass { public: - typedef std::function THandlerFunction_Progress; - + using THandlerFunction_Progress = std::function; + using THandlerFunction_Error = std::function; + using THandlerFunction = std::function; + UpdaterClass(); ~UpdaterClass(); @@ -66,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; } @@ -97,6 +102,11 @@ class UpdaterClass { */ bool end(bool evenIfRemaining = false); + /* + Gets the last error description as string + */ + String getErrorString() const; + /* Prints the last error to an output stream */ @@ -120,7 +130,34 @@ class UpdaterClass { /* This callback will be called when Updater is receiving data */ - UpdaterClass& onProgress(THandlerFunction_Progress fn); + UpdaterClass& onProgress(THandlerFunction_Progress fn) { + _progress_callback = std::move(fn); + return *this; + } + + /* + This callback will be called when Updater ends + */ + UpdaterClass& onError(THandlerFunction_Error fn) { + _error_callback = std::move(fn); + return *this; + } + + /* + This callback will be called when Updater begins + */ + UpdaterClass& onStart(THandlerFunction fn) { + _start_callback = std::move(fn); + return *this; + } + + /* + This callback will be called when Updater ends + */ + UpdaterClass& onEnd(THandlerFunction fn) { + _end_callback = std::move(fn); + return *this; + } //Helpers uint8_t getError(){ return _error; } @@ -175,13 +212,13 @@ class UpdaterClass { } private: - void _reset(); + void _reset(bool callback = true); bool _writeBuffer(); bool _verifyHeader(uint8_t data); bool _verifyEnd(); - void _setError(int error); + void _setError(int error); bool _async = false; uint8_t _error = 0; @@ -202,8 +239,12 @@ class UpdaterClass { // Optional signed binary verification UpdaterHashClass *_hash = nullptr; UpdaterVerifyClass *_verify = nullptr; - // Optional progress callback function + + // Optional lifetime callback functions THandlerFunction_Progress _progress_callback = nullptr; + THandlerFunction_Error _error_callback = nullptr; + THandlerFunction _start_callback = nullptr; + THandlerFunction _end_callback = nullptr; }; extern UpdaterClass Update; diff --git a/cores/esp8266/WString.cpp b/cores/esp8266/WString.cpp index f77ded194e..1e608c3c92 100644 --- a/cores/esp8266/WString.cpp +++ b/cores/esp8266/WString.cpp @@ -25,6 +25,8 @@ #include "WString.h" #include "stdlib_noniso.h" +#include + #define OOM_STRING_BORDER_DISPLAY 10 #define OOM_STRING_THRESHOLD_REALLOC_WARN 128 @@ -38,7 +40,7 @@ static String toString(unsigned char value, unsigned char base) { String out; - char buf[1 + 8 * sizeof(unsigned char)]; + char buf[1 + std::numeric_limits::digits]; out = utoa(value, buf, base); return out; @@ -47,12 +49,8 @@ static String toString(unsigned char value, unsigned char base) { static String toString(int value, unsigned char base) { String out; - char buf[2 + 8 * sizeof(int)]; - if (base == 10) { - out.concat(buf, sprintf(buf, "%d", value)); - } else { - out = itoa(value, buf, base); - } + char buf[2 + std::numeric_limits::digits]; + out = itoa(value, buf, base); return out; } @@ -60,7 +58,7 @@ static String toString(int value, unsigned char base) { static String toString(unsigned int value, unsigned char base) { String out; - char buf[1 + 8 * sizeof(unsigned int)]; + char buf[1 + std::numeric_limits::digits]; out = utoa(value, buf, base); return out; @@ -69,12 +67,8 @@ static String toString(unsigned int value, unsigned char base) { static String toString(long value, unsigned char base) { String out; - char buf[2 + 8 * sizeof(long)]; - if (base == 10) { - out.concat(buf, sprintf(buf, "%ld", value)); - } else { - out = ltoa(value, buf, base); - } + char buf[2 + std::numeric_limits::digits]; + out = ltoa(value, buf, base); return out; } @@ -82,7 +76,7 @@ static String toString(long value, unsigned char base) { static String toString(unsigned long value, unsigned char base) { String out; - char buf[1 + 8 * sizeof(unsigned long)]; + char buf[1 + std::numeric_limits::digits]; out = ultoa(value, buf, base); return out; @@ -93,12 +87,8 @@ static String toString(unsigned long value, unsigned char base) { static String toString(long long value, unsigned char base) { String out; - char buf[2 + 8 * sizeof(long long)]; - if (base == 10) { - out.concat(buf, sprintf(buf, "%lld", value)); - } else { - out = lltoa(value, buf, sizeof(buf), base); - } + char buf[2 + std::numeric_limits::digits]; + out = lltoa(value, buf, sizeof(buf), base); return out; } @@ -106,17 +96,13 @@ static String toString(long long value, unsigned char base) { static String toString(unsigned long long value, unsigned char base) { String out; - char buf[1 + 8 * sizeof(unsigned long long)]; - if (base == 10) { - out.concat(buf, sprintf(buf, "%llu", value)); - } else { - out = ulltoa(value, buf, sizeof(buf), base); - } + char buf[1 + std::numeric_limits::digits]; + out = ulltoa(value, buf, sizeof(buf), base); return out; } -static String toString(float value, unsigned char decimalPlaces) { +static String toString(double value, unsigned char decimalPlaces) { String out; char buf[33]; @@ -125,13 +111,8 @@ static String toString(float value, unsigned char decimalPlaces) { return out; } -static String toString(double value, unsigned char decimalPlaces) { - String out; - - char buf[33]; - out = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf); - - return out; +static String toString(float value, unsigned char decimalPlaces) { + return toString(static_cast(value), decimalPlaces); } /*********************************************/ @@ -216,6 +197,15 @@ bool String::reserve(unsigned int size) { return false; } +#ifdef DEBUG_ESP_PORT +static void identifyString (const String& badOne) +{ + DEBUG_ESP_PORT.printf("[String] '%." STR(OOM_STRING_BORDER_DISPLAY) "s ... %." STR(OOM_STRING_BORDER_DISPLAY) "s': ", + badOne.c_str(), + badOne.length() > OOM_STRING_BORDER_DISPLAY? badOne.c_str() + std::max((int)badOne.length() - OOM_STRING_BORDER_DISPLAY, OOM_STRING_BORDER_DISPLAY): ""); +} +#endif + bool String::changeBuffer(unsigned int maxStrLen) { // Can we use SSO here to avoid allocation? if (maxStrLen < sizeof(sso.buff) - 1) { @@ -237,16 +227,19 @@ bool String::changeBuffer(unsigned int maxStrLen) { } // Fallthrough to normal allocator size_t newSize = (maxStrLen + 16) & (~0xf); -#ifdef DEBUG_ESP_OOM +#ifdef DEBUG_ESP_PORT if (!isSSO() && capacity() >= OOM_STRING_THRESHOLD_REALLOC_WARN && maxStrLen > capacity()) { // warn when badly re-allocating - DEBUGV("[String] Reallocating large String(%d -> %d bytes) '%." STR(OOM_STRING_BORDER_DISPLAY) "s ... %." STR(OOM_STRING_BORDER_DISPLAY) "s'\n", - len(), maxStrLen, c_str(), - len() > OOM_STRING_BORDER_DISPLAY? c_str() + std::max((int)len() - OOM_STRING_BORDER_DISPLAY, OOM_STRING_BORDER_DISPLAY): ""); + identifyString(*this); + DEBUG_ESP_PORT.printf("Reallocating large String(%d -> %d bytes)\n", len(), maxStrLen); } #endif // Make sure we can fit newsize in the buffer if (newSize > CAPACITY_MAX) { +#ifdef DEBUG_ESP_PORT + identifyString(*this); + DEBUG_ESP_PORT.printf("Maximum capacity reached (" STR(CAPACITY_MAX) ")\n"); +#endif return false; } uint16_t oldLen = len(); @@ -266,6 +259,10 @@ bool String::changeBuffer(unsigned int maxStrLen) { setBuffer(newbuffer); return true; } +#ifdef DEBUG_ESP_PORT + identifyString(*this); + DEBUG_ESP_PORT.printf("OOM: %d -> %zu bytes\n", isSSO() ? 0: capacity(), newSize); +#endif return false; } @@ -279,7 +276,8 @@ String &String::copy(const char *cstr, unsigned int length) { return *this; } setLen(length); - memmove_P(wbuffer(), cstr, length + 1); + memmove_P(wbuffer(), cstr, length); + wbuffer()[length] = 0; return *this; } @@ -289,7 +287,8 @@ String &String::copy(const __FlashStringHelper *pstr, unsigned int length) { return *this; } setLen(length); - memcpy_P(wbuffer(), (PGM_P)pstr, length + 1); // We know wbuffer() cannot ever be in PROGMEM, so memcpy safe here + memcpy_P(wbuffer(), (PGM_P)pstr, length); // We know wbuffer() cannot ever be in PROGMEM, so memcpy safe here + wbuffer()[length] = 0; return *this; } @@ -430,8 +429,9 @@ bool String::concat(const __FlashStringHelper *str) { unsigned int newlen = len() + length; if (!reserve(newlen)) return false; - memcpy_P(wbuffer() + len(), (PGM_P)str, length + 1); + memcpy_P(wbuffer() + len(), (PGM_P)str, length); setLen(newlen); + wbuffer()[newlen] = 0; return true; } @@ -820,7 +820,7 @@ void String::replace(const String &find, const String &replace) { if (size == len()) return; if (size > capacity() && !changeBuffer(size)) - return; // XXX: tell user! + return; int index = len() - 1; while (index >= 0 && (index = lastIndexOf(find, index)) >= 0) { readFrom = wbuffer() + index + find.len(); diff --git a/cores/esp8266/WString.h b/cores/esp8266/WString.h index 780c7fdf60..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)) @@ -58,6 +58,7 @@ class String { String(const String &str); String(const __FlashStringHelper *str); String(String &&rval) noexcept; + explicit String(char c) { sso.buff[0] = c; sso.buff[1] = 0; @@ -203,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); @@ -226,18 +227,12 @@ class String { bool operator ==(const char *cstr) const { return equals(cstr); } - bool operator ==(const __FlashStringHelper *rhs) const { - return equals(rhs); - } bool operator !=(const String &rhs) const { return !equals(rhs); } bool operator !=(const char *cstr) const { return !equals(cstr); } - bool operator !=(const __FlashStringHelper *rhs) const { - return !equals(rhs); - } bool operator <(const String &rhs) const; bool operator >(const String &rhs) const; bool operator <=(const String &rhs) const; @@ -348,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 6932416b68..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,9 +67,11 @@ void cont_run(cont_t*, void (*pfn)(void)); // execution state (registers and stack) void cont_suspend(cont_t*); -// Check guard bytes around the stack. Return 0 in case everything is ok, -// return 1 if guard bytes were overwritten. -int cont_check(cont_t* cont); +// 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_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) @@ -79,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 fd72ee5d18..6c49a38fcf 100644 --- a/cores/esp8266/cont_util.cpp +++ b/cores/esp8266/cont_util.cpp @@ -18,34 +18,50 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "cont.h" +#include + #include #include -#include "ets_sys.h" -extern "C" { +#include "core_esp8266_features.h" +#include "debug.h" + +#include "cont.h" + +extern "C" +{ -#define 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; } } -int IRAM_ATTR cont_check(cont_t* cont) { - if(cont->stack_guard1 != CONT_STACKGUARD || cont->stack_guard2 != CONT_STACKGUARD) return 1; +void IRAM_ATTR cont_check_guard(cont_t* cont) { + if ((cont->stack_guard1 != CONT_STACKGUARD) + || (cont->stack_guard2 != CONT_STACKGUARD)) + { + __stack_chk_fail(); + __builtin_unreachable(); + } +} - return 0; +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_features.cpp b/cores/esp8266/core_esp8266_features.cpp index 68f631d05c..fa51ad8431 100644 --- a/cores/esp8266/core_esp8266_features.cpp +++ b/cores/esp8266/core_esp8266_features.cpp @@ -20,30 +20,45 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include #include +#ifdef __cplusplus +extern "C" { +#endif + /* precache() * pre-loads flash data into the flash cache * if f==0, preloads instructions starting at the address we were called from. * otherwise preloads flash at the given address. * All preloads are word aligned. */ -#ifdef __cplusplus -extern "C" { -#endif - void precache(void *f, uint32_t bytes) { // Size of a cache page in bytes. We only need to read one word per // page (ie 1 word in 8) for this to work. #define CACHE_PAGE_SIZE 32 - uint32_t a0; - __asm__("mov.n %0, a0" : "=r"(a0)); - uint32_t lines = (bytes/CACHE_PAGE_SIZE)+2; - volatile uint32_t *p = (uint32_t*)((f ? (uint32_t)f : a0) & ~0x03); - uint32_t x; - for (uint32_t i=0; i 0) + || ((data[1] & (1 << 16)) > 0); } #ifdef __cplusplus diff --git a/cores/esp8266/core_esp8266_features.h b/cores/esp8266/core_esp8266_features.h index 6609a0d082..9c574f20c7 100644 --- a/cores/esp8266/core_esp8266_features.h +++ b/cores/esp8266/core_esp8266_features.h @@ -20,11 +20,9 @@ */ - #ifndef CORE_ESP8266_FEATURES_H #define CORE_ESP8266_FEATURES_H - #define CORE_HAS_LIBB64 #define CORE_HAS_BASE64_CLASS #define CORE_HAS_CXA_GUARD @@ -33,9 +31,10 @@ #define WIFI_HAS_EVENT_CALLBACK #define WIFI_IS_OFF_AT_BOOT -#include // malloc() +#include // bool #include // size_t #include +#include // malloc() #ifndef __STRINGIFY #define __STRINGIFY(a) #a @@ -118,14 +117,26 @@ int esp_get_cpu_freq_mhz() #else inline int esp_get_cpu_freq_mhz() { + uint8_t system_get_cpu_freq(void); return system_get_cpu_freq(); } #endif + // Call this function in your setup() to cause the phase locked version of the generator to // be linked in automatically. Otherwise, the default PWM locked version will be used. void enablePhaseLockedWaveform(void); +// Determine when the sketch runs on ESP8285 +#if !defined(CORE_MOCK) +bool esp_is_8285() __attribute__((const, nothrow)); +#else +inline bool esp_is_8285() +{ + return false; +} +#endif + #ifdef __cplusplus } #endif 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 430808f19d..d7f14b02c8 100644 --- a/cores/esp8266/core_esp8266_main.cpp +++ b/cores/esp8266/core_esp8266_main.cpp @@ -22,6 +22,10 @@ //This may be used to change user task stack size: //#define CONT_STACKSIZE 4096 + +#include +#include + #include #include "Schedule.h" extern "C" { @@ -163,12 +167,25 @@ extern "C" void __esp_delay(unsigned long ms) { extern "C" void esp_delay(unsigned long ms) __attribute__((weak, alias("__esp_delay"))); bool esp_try_delay(const uint32_t start_ms, const uint32_t timeout_ms, const uint32_t intvl_ms) { + if (!timeout_ms) { + esp_yield(); + return true; + } + uint32_t expired = millis() - start_ms; if (expired >= timeout_ms) { - return true; + return true; // expired } - esp_delay(std::min((timeout_ms - expired), intvl_ms)); - return false; + + // compute greatest chunked delay with respect to scheduled recurrent functions + uint32_t grain_ms = std::gcd(intvl_ms, compute_scheduled_recurrent_grain()); + + // recurrent scheduled functions will be called from esp_delay()->esp_suspend() + esp_delay(grain_ms > 0 ? + std::min((timeout_ms - expired), grain_ms): + (timeout_ms - expired)); + + return false; // expiration must be checked again } extern "C" void __yield() { @@ -245,21 +262,21 @@ static void loop_wrapper() { } loop(); loop_end(); + cont_check_guard(g_pcont); if (serialEventRun) { serialEventRun(); } esp_schedule(); } +extern "C" void __stack_chk_fail(void); + static void loop_task(os_event_t *events) { (void) events; s_cycles_at_resume = ESP.getCycleCount(); ESP.resetHeap(); cont_run(g_pcont, &loop_wrapper); ESP.setDramHeap(); - if (cont_check(g_pcont) != 0) { - panic(); - } } extern "C" { @@ -400,16 +417,250 @@ extern "C" void __disableWiFiAtBootTime (void) #if FLASH_MAP_SUPPORT #include "flash_hal.h" -extern "C" void flashinit (void); +extern "C" const char *flashinit (void); uint32_t __flashindex; #endif +#if (NONOSDK >= (0x30000)) +#undef ETS_PRINTF +#define ETS_PRINTF(...) ets_uart_printf(__VA_ARGS__) +extern "C" uint8_t uart_rx_one_char_block(); + +#if ! FLASH_MAP_SUPPORT +#include "flash_hal.h" +#endif + +extern "C" void user_rf_pre_init(); + +extern "C" void ICACHE_FLASH_ATTR user_pre_init(void) +{ + const char *flash_map_str = NULL; + const char *chip_sz_str = NULL; + const char *table_regist_str = NULL; + [[maybe_unused]] uint32_t ld_config_chip_size = 0; + uint32_t flash_size = 0; + uint32_t phy_data = 0; + uint32_t rf_cal = 0; + uint32_t system_parameter = 0; + [[maybe_unused]] const partition_item_t *_at_partition_table = NULL; + size_t _at_partition_table_sz = 0; + + do { + #if FLASH_MAP_SUPPORT + flash_map_str = flashinit(); + if (flash_map_str) { + continue; + } + #endif + + // For SDKs 3.0.0 and later, place phy_data readonly overlay on top of + // the EEPROM address. For older SDKs without a system partition, RF_CAL + // and PHY_DATA shared the same flash segment. + // + // For the Arduino ESP8266 core, the sectors for "EEPROM = size - + // 0x5000", "RF_CAL = size - 0x4000", and "SYSTEM_PARAMETER = size - + // 0x3000" are positioned in the last five sectors of flash memory. + // PHY_INIT_DATA is special. It is a one time read of 128 bytes of data + // that is provided by a spoofed flash read. + #if FLASH_MAP_SUPPORT + flash_size = __flashdesc[__flashindex].flash_size_kb * 1024u; + #else + // flashchip->chip_size is updated by the SDK. The size is based on the + // value patched into the .bin header by esptool. + // system_get_flash_size_map() returns that patched value. + flash_size = flashchip->chip_size; + #endif + + // For all configurations, place RF_CAL and system_parameter in the + // last 4 sectors of the flash chip. + rf_cal = flash_size - 0x4000u; + system_parameter = flash_size - 0x3000u; + + // The system_partition_table_regist will not allow partitions to + // overlap. EEPROM_start is a good choice for phy_data overlay. The SDK + // does not need to know about EEPROM_start. So we can omit it from the + // table. The real EEPROM access is after user_init() begins long after + // the PHY_DATA read. So it should be safe from conflicts. + phy_data = EEPROM_start - 0x40200000u; + + // For SDKs 3.0 builds, "sdk3_begin_phy_data_spoof and + // user_rf_cal_sector_set" starts and stops the spoofing logic in + // `core_esp8266_phy.cpp`. + extern void sdk3_begin_phy_data_spoof(); + sdk3_begin_phy_data_spoof(); + + ld_config_chip_size = phy_data + 4096 * 5; + + // -DALLOW_SMALL_FLASH_SIZE=1 + // Allows for small flash-size builds targeted for multiple devices, + // commonly IoT, of varying flash sizes. + #if !defined(FLASH_MAP_SUPPORT) && !defined(ALLOW_SMALL_FLASH_SIZE) + // Note, system_partition_table_regist will only catch when the build + // flash size value set by the Arduino IDE Tools menu is larger than + // the firmware image value detected and updated on the fly by esptool. + if (flashchip->chip_size != ld_config_chip_size) { + // Stop to avoid possible stored flash data corruption. This + // mismatch will not occur with flash size selection "Mapping + // defined by Hardware and Sketch". + chip_sz_str = PSTR("Flash size mismatch, check that the build setting matches the device.\n"); + continue; + } + #elif defined(ALLOW_SMALL_FLASH_SIZE) && !defined(FLASH_MAP_SUPPORT) + // Note, while EEPROM is confined to a smaller flash size, we are still + // placing RF_CAL and SYSTEM_PARAMETER at the end of flash. To prevent + // this, esptool or its equal needs to not update the flash size in the + // .bin image. + #endif + + #if FLASH_MAP_SUPPORT && defined(DEBUG_ESP_PORT) + // I don't think this will ever fail. Everything traces back to the results of spi_flash_get_id() + if (flash_size != flashchip->chip_size) { + chip_sz_str = PSTR("Flash size mismatch, check that the build setting matches the device.\n"); + continue; + } + #endif + + // All the examples I find, show the partition table in the global address space. + static const partition_item_t at_partition_table[] = + { + { SYSTEM_PARTITION_PHY_DATA, phy_data, 0x1000 }, // type 5 + { SYSTEM_PARTITION_RF_CAL, rf_cal, 0x1000 }, // type 4 + { SYSTEM_PARTITION_SYSTEM_PARAMETER, system_parameter, 0x3000 }, // type 6 + }; + _at_partition_table = at_partition_table; + _at_partition_table_sz = std::size(at_partition_table); + // SDK 3.0's `system_partition_table_regist` is FOTA-centric. It will report + // on BOOT, OTA1, and OTA2 being missing. We are Non-FOTA. I don't see + // anything we can do about this. Other than maybe turning off os_print. + if (!system_partition_table_regist(at_partition_table, _at_partition_table_sz, system_get_flash_size_map())) { + table_regist_str = PSTR("System partition table registration failed!\n"); + continue; + } + } while(false); + + if (chip_sz_str || flash_map_str || table_regist_str) { + // user_pre_init() is called very early in the SDK startup. When called, + // the PLL CPU clock calibration hasn't not run. Since we are failing, the + // calibration will never complete. And the process will repeat over and + // over. The effective data rate will always be 74880 bps. If we had a + // successful boot, the effective data rate would be 115200 on a restart + // or HWDT. This hack relies on the CPU clock calibration never having + // completed. This assumes we are starting from a hard reset. + + // A possible exception would be a soft reset after flashing. In which + // case the message will not be readable until after a hard reset or + // power cycle. + + // After flashing, the Arduino Serial Monitor needs a moment to + // reconnect. This also allows time for the FIFO to clear and the host + // serial port to clear any framing errors. + ets_delay_us(200u * 1000u); // For an uncalibrated CPU Clock, this is close enough. + + #if !defined(F_CRYSTAL) + #define F_CRYSTAL 26000000 + #endif + // For print messages to be readable, the UART clock rate is based on the + // precalibration rate. + if (F_CRYSTAL != 40000000) { + uart_div_modify(0, F_CRYSTAL * 2 / 115200); + ets_delay_us(150); + } + do { + ETS_PRINTF("\n\n"); + // Because SDK v3.0.x always has a non-32-bit wide exception handler + // installed, we can use PROGMEM strings with Boot ROM print functions. +#if defined(DEBUG_ESP_CORE) || defined(DEBUG_ESP_PORT) // DEBUG_ESP_CORE => verbose + #if FLASH_MAP_SUPPORT + if (flash_map_str) { + ETS_PRINTF(flash_map_str); + #if defined(DEBUG_ESP_CORE) + size_t num = __flashindex; // On failure __flashindex is the size of __flashdesc[]; :/ + ETS_PRINTF(PSTR("Table of __flashdesc[%u].flash_size_kb entries converted to bytes:\n"), num); + for (size_t i = 0; i < num; i++) { + uint32_t size = __flashdesc[i].flash_size_kb << 10; + ETS_PRINTF(PSTR(" [%02u] 0x%08X %8u\n"), i, size, size); + } + #endif + ETS_PRINTF(PSTR("Reference info:\n")); + uint32_t flash_chip_size = 1 << ((spi_flash_get_id() >> 16) & 0xff); + ETS_PRINTF(PSTR(" %-24s 0x%08X %8u\n"), PSTR("fn(spi_flash_get_id())"), flash_chip_size, flash_chip_size); + ETS_PRINTF(PSTR(" %-24s 0x%08X %8u\n"), PSTR("bin_chip_size"), flashchip->chip_size, flashchip->chip_size); + } else + #endif + if (chip_sz_str) { + ETS_PRINTF(chip_sz_str); + } else + if (table_regist_str) { + ETS_PRINTF(table_regist_str); + // (printing now works) repeat ...regist error messages + system_partition_table_regist(_at_partition_table, _at_partition_table_sz, system_get_flash_size_map()); + } + if (chip_sz_str || table_regist_str) { + ETS_PRINTF(PSTR("Reference info:\n")); + #if FLASH_MAP_SUPPORT + ETS_PRINTF(PSTR(" %-24s 0x%08X %8u\n"), PSTR("fn(...ex].flash_size_kb)"), flash_size, flash_size); + uint32_t flash_chip_size = 1 << ((spi_flash_get_id() >> 16) & 0xff); + ETS_PRINTF(PSTR(" %-24s 0x%08X %8u\n"), PSTR("fn(spi_flash_get_id())"), flash_chip_size, flash_chip_size); + ETS_PRINTF(PSTR(" %-24s 0x%08X %8u\n"), PSTR("bin_chip_size"), flashchip->chip_size, flashchip->chip_size); + #else + ETS_PRINTF(PSTR(" %-24s 0x%08X %8u\n"), PSTR("config_flash_size"), ld_config_chip_size, ld_config_chip_size); + ETS_PRINTF(PSTR(" %-24s 0x%08X %8u\n"), PSTR("bin_chip_size"), flashchip->chip_size, flashchip->chip_size); + #endif + #if defined(DEBUG_ESP_CORE) + ETS_PRINTF(PSTR(" %-24s 0x%08X\n"), PSTR("PHY_DATA"), phy_data); + ETS_PRINTF(PSTR(" %-24s 0x%08X\n"), PSTR("RF_CAL"), rf_cal); + ETS_PRINTF(PSTR(" %-24s 0x%08X\n"), PSTR("SYSTEM_PARAMETER"), system_parameter); + ETS_PRINTF(PSTR(" %-24s 0x%08X\n"), PSTR("EEPROM_start"), EEPROM_start); + ETS_PRINTF(PSTR(" %-24s 0x%08X\n"), PSTR("FS_start"), FS_start); + ETS_PRINTF(PSTR(" %-24s 0x%08X\n"), PSTR("FS_end"), FS_end); + ETS_PRINTF(PSTR(" %-24s 0x%08X\n"), PSTR("FS_page"), FS_page); + ETS_PRINTF(PSTR(" %-24s 0x%08X\n"), PSTR("FS_block"), FS_block); + #endif + } +#else + if (flash_map_str) { + ETS_PRINTF(flash_map_str); + } else + if (chip_sz_str) { + ETS_PRINTF(chip_sz_str); + } else + if (table_regist_str) { + ETS_PRINTF(table_regist_str); + } +#endif + uart_rx_one_char_block(); // Someone said hello - repeat message + } while(true); + } + /* + The function user_rf_pre_init() is no longer called from SDK 3.0 and up. + The SDK manual and release notes skipped this detail. The 2023 ESP-FAQ + hints at the change with "* Call system_phy_set_powerup_option(3) in + function user_pre_init or user_rf_pre_init" + https://docs.espressif.com/_/downloads/espressif-esp-faq/en/latest/pdf/#page=14 + + Add call to user_rf_pre_init(), so we can still perform early calls like + system_phy_set_rfoption(rf_mode), system_phy_set_powerup_option(2), etc. + + Placement, should this be at the beginning or end of user_pre_init()? + By the end, we have registered the PHY_DATA partition; however, PHY_DATA + read occurs after return and before user_init() is called. + */ + user_rf_pre_init(); +} +#endif // #if (NONOSDK >= (0x30000)) + extern "C" void user_init(void) { + struct rst_info *rtc_info_ptr = system_get_rst_info(); memcpy((void *) &resetInfo, (void *) rtc_info_ptr, sizeof(resetInfo)); uart_div_modify(0, UART_CLK_FREQ / (115200)); +#if FLASH_MAP_SUPPORT && (NONOSDK < (0x30000)) + const char *err_msg = flashinit(); + if (err_msg) __panic_func(err_msg, 0, NULL); +#endif + init(); // in core_esp8266_wiring.c, inits hw regs and sdk timer initVariant(); @@ -426,15 +677,12 @@ extern "C" void user_init(void) { install_vm_exception_handler(); #endif -#if defined(NON32XFER_HANDLER) || defined(MMU_IRAM_HEAP) +#if defined(NON32XFER_HANDLER) || (defined(MMU_IRAM_HEAP) && (NONOSDK < (0x30000))) install_non32xfer_exception_handler(); #endif #if defined(MMU_IRAM_HEAP) umm_init_iram(); -#endif -#if FLASH_MAP_SUPPORT - flashinit(); #endif preinit(); // Prior to C++ Dynamic Init (not related to above init() ). Meant to be user redefinable. __disableWiFiAtBootTime(); // default weak function disables WiFi diff --git a/cores/esp8266/core_esp8266_non32xfer.cpp b/cores/esp8266/core_esp8266_non32xfer.cpp index 5047e8de5a..eaf2323c96 100644 --- a/cores/esp8266/core_esp8266_non32xfer.cpp +++ b/cores/esp8266/core_esp8266_non32xfer.cpp @@ -26,10 +26,14 @@ * Code taken directly from @pvvx's public domain code in * https://github.com/pvvx/esp8266web/blob/master/app/sdklib/system/app_main.c * + * In Espressif versions NONOSDK v3.0.0+ a similar feature was added + * load_non_32_wide_handler. Theirs is always loaded. Add weak attribute to + * theirs so we can choose by adding an alias to ours. * */ #include +#include #define VERIFY_C_ASM_EXCEPTION_FRAME_STRUCTURE #include #include @@ -58,10 +62,13 @@ extern "C" { #define EXCCAUSE_LOAD_STORE_ERROR 3 /* Non 32-bit read/write error */ +#if (defined(NON32XFER_HANDLER) || defined(MMU_IRAM_HEAP)) && (NONOSDK < (0x30000)) static fn_c_exception_handler_t old_c_handler = NULL; +#endif +#if defined(NON32XFER_HANDLER) || (defined(MMU_IRAM_HEAP) && (NONOSDK < (0x30000))) static -IRAM_ATTR void non32xfer_exception_handler(struct __exception_frame *ef, int cause) +IRAM_ATTR void non32xfer_exception_handler(struct __exception_frame *ef, [[maybe_unused]] int cause) { do { uint32_t insn, excvaddr; @@ -138,6 +145,7 @@ IRAM_ATTR void non32xfer_exception_handler(struct __exception_frame *ef, int cau } while(false); /* Fail request, die */ +#if (NONOSDK < (0x30000)) /* The old handler points to the SDK. Be alert for HWDT when Calling with INTLEVEL != 0. I cannot create it any more. I thought I saw this as a @@ -148,16 +156,23 @@ IRAM_ATTR void non32xfer_exception_handler(struct __exception_frame *ef, int cau old_c_handler(ef, cause); return; } - +#endif /* Calling _xtos_unhandled_exception(ef, cause) in the Boot ROM, gets us a hardware wdt. Use panic instead as a fall back. It will produce a stack trace. */ +#if defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_MMU) panic(); +#else + // For non-debug builds, save on resources + abort(); +#endif } +#endif // #if defined(NON32XFER_HANDLER) || (defined(MMU_IRAM_HEAP) && (NONOSDK < (0x30000))) +#if (defined(NON32XFER_HANDLER) || defined(MMU_IRAM_HEAP)) && (NONOSDK < (0x30000)) /* To operate reliably, this module requires the new `_xtos_set_exception_handler` from `exc-sethandler.cpp` and @@ -174,5 +189,17 @@ void install_non32xfer_exception_handler(void) { non32xfer_exception_handler); } } +#else +// For v3.0.x SDKs, no need for install - call_user_start will do the job. +// Need this for build dependencies +void install_non32xfer_exception_handler(void) __attribute__((weak)); +void install_non32xfer_exception_handler(void) {} +#endif + +#if defined(NON32XFER_HANDLER) +// For SDKs 3.0.x, we made load_non_32_wide_handler in +// libmain.c:user_exceptions.o a weak symbol allowing this override to work. +extern void load_non_32_wide_handler(struct __exception_frame *ef, int cause) __attribute__((alias("non32xfer_exception_handler"))); +#endif }; 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_phy.cpp b/cores/esp8266/core_esp8266_phy.cpp index f06a698bf7..4657bb0081 100644 --- a/cores/esp8266/core_esp8266_phy.cpp +++ b/cores/esp8266/core_esp8266_phy.cpp @@ -303,12 +303,26 @@ extern int __real_spi_flash_read(uint32_t addr, uint32_t* dst, size_t size); extern int IRAM_ATTR __wrap_spi_flash_read(uint32_t addr, uint32_t* dst, size_t size); extern int __get_adc_mode(); +/* + Verified that the wide filtering of all 128 byte flash reads during + spoof_init_data continues to be safe for SDK 3.0.5 + From start call to user_pre_init() to stop call with user_rf_pre_init(). + + flash read count during spoof_init_data 4 + flash read 0x00000 8 // system_get_flash_size_map() + flash read 0x00000 4 // system_partition_table_regist() + flash read 0xFB000 128 // PHY_DATA (EEPROM address space) + flash read 0xFC000 628 // RC_CAL + */ extern int IRAM_ATTR __wrap_spi_flash_read(uint32_t addr, uint32_t* dst, size_t size) { if (!spoof_init_data || size != 128) { return __real_spi_flash_read(addr, dst, size); } +#if (NONOSDK >= (0x30000)) + spoof_init_data = false; +#endif memcpy(dst, phy_init_data, sizeof(phy_init_data)); ((uint8_t*)dst)[107] = __get_adc_mode(); return 0; @@ -329,23 +343,29 @@ extern int __get_adc_mode(void) extern void __run_user_rf_pre_init(void) __attribute__((weak)); extern void __run_user_rf_pre_init(void) { - return; // default do noting + return; // default do nothing } +#if (NONOSDK >= (0x30000)) +void sdk3_begin_phy_data_spoof(void) +{ + spoof_init_data = true; +} +#else uint32_t user_rf_cal_sector_set(void) { spoof_init_data = true; return flashchip->chip_size/SPI_FLASH_SEC_SIZE - 4; } +#endif void user_rf_pre_init() { // *((volatile uint32_t*) 0x60000710) = 0; +#if (NONOSDK < (0x30000)) spoof_init_data = false; - volatile uint32_t* rtc_reg = (volatile uint32_t*) 0x60001000; - rtc_reg[30] = 0; +#endif - system_set_os_print(0); int rf_mode = __get_rf_mode(); if (rf_mode >= 0) { system_phy_set_rfoption(rf_mode); diff --git a/cores/esp8266/core_esp8266_postmortem.cpp b/cores/esp8266/core_esp8266_postmortem.cpp index 2279401b54..95844534e8 100644 --- a/cores/esp8266/core_esp8266_postmortem.cpp +++ b/cores/esp8266/core_esp8266_postmortem.cpp @@ -42,10 +42,13 @@ static int s_panic_line = 0; static const char* s_panic_func = 0; static const char* s_panic_what = 0; +// Our wiring for abort() and C++ exceptions static bool s_abort_called = false; static const char* s_unhandled_exception = NULL; -static uint32_t s_stacksmash_addr = 0; +// Common way to notify about where the stack smashing happened +// (but, **only** if caller uses our handler function) +static uint32_t s_stack_chk_addr = 0; void abort() __attribute__((noreturn)); static void uart_write_char_d(char c); @@ -56,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 }; @@ -96,7 +100,9 @@ static void ets_printf_P(const char *str, ...) { } static void cut_here() { - ets_putc('\n'); + // https://tinyurl.com/8266dcdr => https://arduino-esp8266.readthedocs.io/en/latest/faq/a02-my-esp-crashes.html#exception + ets_printf_P(PSTR("\nTo make this dump useful, DECODE IT - https://tinyurl.com/8266dcdr\n")); + for (auto i = 0; i < 15; i++ ) { ets_putc('-'); } @@ -107,10 +113,30 @@ static void cut_here() { ets_putc('\n'); } -void __wrap_system_restart_local() { - register uint32_t sp asm("a1"); - uint32_t sp_dump = sp; +static inline bool is_pc_valid(uint32_t pc) { + return pc >= XCHAL_INSTRAM0_VADDR && pc < (XCHAL_INSTROM0_VADDR + XCHAL_INSTROM0_SIZE); +} +/* + Add some assembly to grab the stack pointer and pass it as an argument before + it grows for the target function. Should stabilize the stack offsets, used to + find the relevant stack content for dumping. +*/ +extern "C" void __wrap_system_restart_local(void); +asm( + ".section .text.__wrap_system_restart_local,\"ax\",@progbits\n\t" + ".literal_position\n\t" + ".align 4\n\t" + ".global __wrap_system_restart_local\n\t" + ".type __wrap_system_restart_local, @function\n\t" + "\n" +"__wrap_system_restart_local:\n\t" + "mov a2, a1\n\t" + "j.l postmortem_report, a3\n\t" + ".size __wrap_system_restart_local, .-__wrap_system_restart_local\n\t" +); + +static void postmortem_report(uint32_t sp_dump) { struct rst_info rst_info; memset(&rst_info, 0, sizeof(rst_info)); if (s_user_reset_reason == REASON_DEFAULT_RST) @@ -137,6 +163,9 @@ void __wrap_system_restart_local() { } ets_putc('\n'); } + else if (s_panic_file) { + ets_printf_P(PSTR("\nPanic %S\n"), s_panic_file); + } else if (s_unhandled_exception) { ets_printf_P(PSTR("\nUnhandled C++ exception: %S\n"), s_unhandled_exception); } @@ -146,38 +175,75 @@ void __wrap_system_restart_local() { else if (rst_info.reason == REASON_EXCEPTION_RST) { // The GCC divide routine in ROM jumps to the address below and executes ILL (00 00 00) on div-by-zero // In that case, print the exception as (6) which is IntegerDivZero - bool div_zero = (rst_info.exccause == 0) && (rst_info.epc1 == 0x4000dce5); + uint32_t epc1 = rst_info.epc1; + uint32_t exccause = rst_info.exccause; + bool div_zero = (exccause == 0) && (epc1 == 0x4000dce5u); + if (div_zero) { + exccause = 6; + // In place of the detached 'ILL' instruction., redirect attention + // back to the code that called the ROM divide function. + __asm__ __volatile__("rsr.excsave1 %0\n\t" : "=r"(epc1) :: "memory"); + } ets_printf_P(PSTR("\nException (%d):\nepc1=0x%08x epc2=0x%08x epc3=0x%08x excvaddr=0x%08x depc=0x%08x\n"), - div_zero ? 6 : rst_info.exccause, rst_info.epc1, rst_info.epc2, rst_info.epc3, rst_info.excvaddr, rst_info.depc); + exccause, epc1, rst_info.epc2, rst_info.epc3, rst_info.excvaddr, rst_info.depc); } else if (rst_info.reason == REASON_SOFT_WDT_RST) { - ets_printf_P(PSTR("\nSoft WDT reset\n")); + ets_printf_P(PSTR("\nSoft WDT reset")); + 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")); + } + ets_putc('\n'); + ets_printf_P(PSTR("\nException (%d):\nepc1=0x%08x epc2=0x%08x epc3=0x%08x excvaddr=0x%08x depc=0x%08x\n"), + 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 overflow 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 - // (determined empirically, might break) + // ~(determined empirically, might break)~ uint32_t offset = 0; if (rst_info.reason == REASON_SOFT_WDT_RST) { - offset = 0x1a0; + // Stack Tally + // 256 User Exception vector handler reserves stack space + // directed to _xtos_l1int_handler function in Boot ROM + // 48 wDev_ProcessFiq - its address appears in a vector table at 0x3FFFC27C + // 16 ?unnamed? - index into a table, pull out pointer, and call if non-zero + // appears near near wDev_ProcessFiq + // 32 pp_soft_wdt_feed_local - gather the specifics and call __wrap_system_restart_local + offset = 32 + 16 + 48 + 256; } else if (rst_info.reason == REASON_EXCEPTION_RST) { - offset = 0x190; + // Stack Tally + // 256 Exception vector reserves stack space + // filled in by "C" wrapper handler + // 16 Handler level 1 - enable icache + // 64 Handler level 2 - exception report + offset = 64 + 16 + 256; } else if (rst_info.reason == REASON_WDT_RST) { - offset = 0x10; + offset = 16; + } + else if (rst_info.reason == REASON_USER_SWEXCEPTION_RST) { + offset = 16; } ets_printf_P(PSTR("\n>>>stack>>>\n")); @@ -190,15 +256,21 @@ void __wrap_system_restart_local() { 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); @@ -237,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) ? '<' : ' '); } } @@ -274,8 +355,9 @@ static void raise_exception() { s_user_reset_reason = REASON_USER_SWEXCEPTION_RST; ets_printf_P(PSTR("\nUser exception (panic/abort/assert)")); - __wrap_system_restart_local(); - + uint32_t sp; + __asm__ __volatile__ ("mov %0, a1\n\t" : "=r"(sp) :: "memory"); + postmortem_report(sp); while (1); // never reached, needed to satisfy "noreturn" attribute } @@ -310,17 +392,28 @@ 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; - ets_printf_P(PSTR("\nPANIC: Stack overrun")); - - 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 - __wrap_system_restart_local(); + uint32_t sp; + __asm__ __volatile__ ("mov %0, a1\n\t" : "=r"(sp) :: "memory"); + postmortem_report(sp); - while (1); // never reached, needed to satisfy "noreturn" attribute + __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/core_esp8266_vm.cpp b/cores/esp8266/core_esp8266_vm.cpp index 3a63d65f7a..00f50e3981 100644 --- a/cores/esp8266/core_esp8266_vm.cpp +++ b/cores/esp8266/core_esp8266_vm.cpp @@ -161,20 +161,21 @@ static struct cache_line *__vm_cache; // Always points to MRU (hence the line be constexpr int addrmask = ~(sizeof(__vm_cache[0].w)-1); // Helper to mask off bits present in cache entry - static void spi_init(spi_regs *spi1) { pinMode(sck, SPECIAL); pinMode(miso, SPECIAL); pinMode(mosi, SPECIAL); pinMode(cs, SPECIAL); - spi1->spi_cmd = 0; + // spi_ctrl appears to need setting before other SPI registers + spi1->spi_ctrl = 0; // MSB first + plain SPI mode + asm("" ::: "memory"); GPMUX &= ~(1 << 9); spi1->spi_clock = spi_clkval; - spi1->spi_ctrl = 0 ; // MSB first + plain SPI mode spi1->spi_ctrl1 = 0; // undocumented, clear for safety? spi1->spi_ctrl2 = 0; // No add'l delays on signals spi1->spi_user2 = 0; // No insn or insn_bits to set + spi1->spi_cmd = 0; } // Note: GCC optimization -O2 and -O3 tried and returned *slower* code than the default diff --git a/cores/esp8266/core_esp8266_wiring.cpp b/cores/esp8266/core_esp8266_wiring.cpp index 40a996d560..3054d39338 100644 --- a/cores/esp8266/core_esp8266_wiring.cpp +++ b/cores/esp8266/core_esp8266_wiring.cpp @@ -34,7 +34,9 @@ static uint32_t micros_overflow_count = 0; #define REPEAT 1 void __delay(unsigned long ms) { - esp_delay(ms); + // Use API letting recurrent scheduled functions run in background + // but stay blocked in delay until ms is expired. + esp_delay(ms, [](){ return true; }); } void delay(unsigned long ms) __attribute__ ((weak, alias("__delay"))); diff --git a/cores/esp8266/coredecls.h b/cores/esp8266/coredecls.h index e06dc6029a..73af6d8cf1 100644 --- a/cores/esp8266/coredecls.h +++ b/cores/esp8266/coredecls.h @@ -1,6 +1,5 @@ -#ifndef __COREDECLS_H -#define __COREDECLS_H +#pragma once #include "core_esp8266_features.h" @@ -20,17 +19,20 @@ void esp_delay(unsigned long ms); void esp_schedule(); void esp_yield(); void tune_timeshift64 (uint64_t now_us); -void disable_extra4k_at_link_time (void) __attribute__((noinline)); bool sntp_set_timezone_in_seconds(int32_t timezone); + +void disable_extra4k_at_link_time (void) __attribute__((noinline)); +void enable_wifi_enterprise_patch(void) __attribute__((noinline)); void __disableWiFiAtBootTime (void) __attribute__((noinline)); void __real_system_restart_local() __attribute__((noreturn)); -uint32_t sqrt32 (uint32_t n); -uint32_t crc32 (const void* data, size_t length, uint32_t crc = 0xffffffff); +uint32_t sqrt32(uint32_t n); #ifdef __cplusplus } +uint32_t crc32(const void* data, size_t length, uint32_t crc = 0xffffffff); + #include using BoolCB = std::function; @@ -52,14 +54,15 @@ inline void esp_suspend(T&& blocked) { // Try to delay until timeout_ms has expired since start_ms. // Returns true if timeout_ms has completely expired on entry. // Otherwise returns false after delaying for the relative -// remainder of timeout_ms, or an absolute intvl_ms, whichever is shorter. +// remainder of timeout_ms, or an absolute intvl_ms, whichever is shorter +// and possibly amended by recurrent scheduled functions timing grain. // The delay may be asynchronously cancelled, before that timeout is reached. bool esp_try_delay(const uint32_t start_ms, const uint32_t timeout_ms, const uint32_t intvl_ms); // This overload of esp_delay() delays for a duration of at most timeout_ms milliseconds. -// Whenever it is resumed, as well as every intvl_ms millisconds, it performs -// the blocked callback, and if that returns true, it keeps delaying for the remainder -// of the original timeout_ms period. +// Whenever it is resumed, as well as at most every intvl_ms millisconds and depending on +// recurrent scheduled functions, it performs the blocked callback, and if that returns true, +// it keeps delaying for the remainder of the original timeout_ms period. template inline void esp_delay(const uint32_t timeout_ms, T&& blocked, const uint32_t intvl_ms) { const auto start_ms = millis(); @@ -76,5 +79,3 @@ inline void esp_delay(const uint32_t timeout_ms, T&& blocked) { } #endif // __cplusplus - -#endif // __COREDECLS_H diff --git a/cores/esp8266/crc32.cpp b/cores/esp8266/crc32.cpp index c2fdf928e7..42de0c1b91 100644 --- a/cores/esp8266/crc32.cpp +++ b/cores/esp8266/crc32.cpp @@ -23,7 +23,7 @@ #include "pgmspace.h" // moved from core_esp8266_eboot_command.cpp -uint32_t crc32 (const void* data, size_t length, uint32_t crc /*= 0xffffffff*/) +uint32_t crc32 (const void* data, size_t length, uint32_t crc) { const uint8_t* ldata = (const uint8_t*)data; while (length--) diff --git a/cores/esp8266/debug.h b/cores/esp8266/debug.h index c6ea8230ef..437479748c 100644 --- a/cores/esp8266/debug.h +++ b/cores/esp8266/debug.h @@ -4,8 +4,15 @@ #include #include +#include "cont.h" + +#define _DEBUG_LEAF_FUNCTION(...) __asm__ __volatile__("" ::: "a0", "memory") + #ifdef DEBUG_ESP_CORE #define DEBUGV(fmt, ...) ::printf((PGM_P)PSTR(fmt), ##__VA_ARGS__) +#define DEBUG_LEAF_FUNCTION _DEBUG_LEAF_FUNCTION +#else +#define DEBUG_LEAF_FUNCTION(...) #endif #ifndef DEBUGV @@ -26,7 +33,8 @@ void hexdump(const void* mem, uint32_t len, uint8_t cols); 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_peri.h b/cores/esp8266/esp8266_peri.h index 33b1e5822f..29a15744b0 100644 --- a/cores/esp8266/esp8266_peri.h +++ b/cores/esp8266/esp8266_peri.h @@ -255,7 +255,7 @@ extern volatile uint32_t* const esp8266_gpioToFn[16]; //UART STATUS Registers Bits #define USTX 31 //TX PIN Level (Doesn't seem to work, always reads as 0 for both uarts. HW bug? Possible workaround: Enable loopback UxC0 |= 1< // returns true when addr can be used without "pgm_" functions or non32xfer service -constexpr bool __byteAddressable(const void* addr) +inline bool __byteAddressable(const void* addr) { return addr < (const void*)(XCHAL_DATARAM0_VADDR + XCHAL_DATARAM0_SIZE); } diff --git a/cores/esp8266/exc-sethandler.cpp b/cores/esp8266/exc-sethandler.cpp index b8f9d69a35..64a45b0307 100644 --- a/cores/esp8266/exc-sethandler.cpp +++ b/cores/esp8266/exc-sethandler.cpp @@ -39,8 +39,10 @@ * architecture, I am not convinced it can be done safely. * */ +#include // need NONOSDK -#if defined(NON32XFER_HANDLER) || defined(MMU_IRAM_HEAP) || defined(NEW_EXC_C_WRAPPER) || defined(MMU_EXTERNAL_HEAP) +#if defined(NON32XFER_HANDLER) || defined(MMU_IRAM_HEAP) || \ + defined(NEW_EXC_C_WRAPPER) || defined(MMU_EXTERNAL_HEAP) || (NONOSDK >= (0x30000)) /* * The original module source code came from: diff --git a/cores/esp8266/flash_hal.h b/cores/esp8266/flash_hal.h index effca0f965..061b1d0b08 100644 --- a/cores/esp8266/flash_hal.h +++ b/cores/esp8266/flash_hal.h @@ -33,21 +33,26 @@ extern "C" { #include extern uint32_t spi_flash_get_id (void); // -extern void flashinit(void); +extern const char *flashinit(void); extern uint32_t __flashindex; extern const flash_map_s __flashdesc[]; +#ifndef QUOTE +#define QUOTE(a) __STRINGIFY(a) +#endif + #define FLASH_MAP_SETUP_CONFIG(conf) FLASH_MAP_SETUP_CONFIG_ATTR(,conf) #define FLASH_MAP_SETUP_CONFIG_ATTR(attr, conf...) \ - const flash_map_s __flashdesc[] PROGMEM = conf; \ - void flashinit (void) attr; \ - void flashinit (void) \ + extern const flash_map_s __flashdesc[] PROGMEM attr = conf; \ + const char *flashinit (void) attr; \ + const char *flashinit (void) \ { \ uint32_t flash_chip_size_kb = 1 << (((spi_flash_get_id() >> 16) & 0xff) - 10); \ for (__flashindex = 0; __flashindex < sizeof(__flashdesc) / sizeof(__flashdesc[0]); __flashindex++) \ if (__flashdesc[__flashindex].flash_size_kb == flash_chip_size_kb) \ - return; \ - panic(); /* configuration not found */ \ + return NULL; \ + static const char fail_msg[] PROGMEM = __FILE__ ":" QUOTE(__LINE__) " flashinit: configuration not found"; \ + return fail_msg; /* configuration not found */ \ } #define EEPROM_start (__flashdesc[__flashindex].eeprom_start) diff --git a/cores/esp8266/hardware_reset.cpp b/cores/esp8266/hardware_reset.cpp new file mode 100644 index 0000000000..827b402a5a --- /dev/null +++ b/cores/esp8266/hardware_reset.cpp @@ -0,0 +1,119 @@ +/* + Make the reset look like an EXT_RST reset by: + * Set INTLEVEL to 15 blocking NMI Software WDT interference + * set "restart reason" to REASON_EXT_SYS_RST + * Config Hardware WDT for 1.6ms + * Disable Hardware WDT Level-1 interrupt option + * wait, ... + + Inspired by RTOS SDK hardware_restart in panic.c +*/ + +#include "Arduino.h" +#include +#include +#include "hardware_reset.h" + + +// Extracted from RTOS_SDK eagle_soc.h +/* + * ESPRSSIF MIT License + * + * Copyright (c) 2015 + * + * Permission is hereby granted for use on ESPRESSIF SYSTEMS ESP8266 only, in which case, + * it is free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#define REG_WRITE(_r, _v) (*(volatile uint32_t *)(_r)) = (_v) +#define REG_READ(_r) (*(volatile uint32_t *)(_r)) + +//Watchdog reg {{ +#define PERIPHS_WDT_BASEADDR 0x60000900 + +#define WDT_CTL_ADDRESS 0 +#define WDT_OP_ADDRESS 0x4 +#define WDT_OP_ND_ADDRESS 0x8 +#define WDT_RST_ADDRESS 0x14 + +#define WDT_CTL_RSTLEN_MASK 0x38 +#define WDT_CTL_RSPMOD_MASK 0x6 +#define WDT_CTL_EN_MASK 0x1 + +#define WDT_CTL_RSTLEN_LSB 0x3 +#define WDT_CTL_RSPMOD_LSB 0x1 +#define WDT_CTL_EN_LSB 0 + +#define WDT_FEED_VALUE 0x73 + +#define WDT_REG_READ(_reg) REG_READ(PERIPHS_WDT_BASEADDR + _reg) +#define WDT_REG_WRITE(_reg, _val) REG_WRITE(PERIPHS_WDT_BASEADDR + _reg, _val) +#define CLEAR_WDT_REG_MASK(_reg, _mask) WDT_REG_WRITE(_reg, WDT_REG_READ(_reg) & (~_mask)) +#define SET_WDT_REG_MASK(_reg, _mask, _val) SET_PERI_REG_BITS((PERIPHS_WDT_BASEADDR + _reg), _mask, _val, 0) +#undef WDT_FEED +#define WDT_FEED() WDT_REG_WRITE(WDT_RST_ADDRESS, WDT_FEED_VALUE) +//}} + +// Inspired by RTOS SDK task_wdt.c and hardware_restart in panic.c + +// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +extern "C" { + void hardware_reset(void) { + volatile uint32_t* const rtc_mem = (volatile uint32_t *)0x60001100u; + + // Block NMI or Software WDT from disturbing out restart reason + xt_rsil(15); + + // An HWDT reason would imply a fault or bug, but this reset was requested. + // Set hint reason to EXT_RST. From empirical evidence, an HWDT looks a lot + // like an EXT_RST. The WDT registers are reset to zero like an EXT_RST; + // however, the PLL initialization is still set. We can still read the Boot + // ROM serial output messages. + // SDK restart reason/hint location + rtc_mem[0] = REASON_EXT_SYS_RST; + + // Disable WDT + CLEAR_WDT_REG_MASK(WDT_CTL_ADDRESS, WDT_CTL_EN_MASK); + + // Set Reset pulse to maximum + // Select Reset only - no level-1 interrupt + SET_WDT_REG_MASK(WDT_CTL_ADDRESS, + WDT_CTL_RSTLEN_MASK | WDT_CTL_RSPMOD_MASK, + (7 << WDT_CTL_RSTLEN_LSB) | (2 << WDT_CTL_RSPMOD_LSB)); + + // Set WDT Reset timer to 1.6 ms. + WDT_REG_WRITE(WDT_OP_ADDRESS, 1); // 2^n * 0.8ms, mask 0xf, n = 1 -> (2^1 = 2) * 0.8 * 0.001 = 0.0016 + + // Enable WDT + SET_WDT_REG_MASK(WDT_CTL_ADDRESS, WDT_CTL_EN_MASK, 1 << WDT_CTL_EN_LSB); + + while (true); + } +}; diff --git a/cores/esp8266/hardware_reset.h b/cores/esp8266/hardware_reset.h new file mode 100644 index 0000000000..38d798b815 --- /dev/null +++ b/cores/esp8266/hardware_reset.h @@ -0,0 +1,14 @@ +#ifndef HARDWARE_RESET_H +#define HARDWARE_RESET_H + +#ifdef __cplusplus +extern "C" { +#endif + +void hardware_reset(void) __attribute__((noreturn)); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cores/esp8266/heap.cpp b/cores/esp8266/heap.cpp index 67f785c712..54db1ede4a 100644 --- a/cores/esp8266/heap.cpp +++ b/cores/esp8266/heap.cpp @@ -5,7 +5,13 @@ #include #include "umm_malloc/umm_malloc.h" -extern "C" size_t umm_umul_sat(const size_t a, const size_t b);; +extern "C" size_t umm_umul_sat(const size_t a, const size_t b); + +// z2EapFree: See wpa2_eap_patch.cpp for details +extern "C" void z2EapFree(void *ptr, const char* file, int line) __attribute__((weak, alias("vPortFree"), nothrow)); +// I don't understand all the compiler noise around this alias. +// Adding "__attribute__ ((nothrow))" seems to resolve the issue. +// This may be relevant: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81824 // Need FORCE_ALWAYS_INLINE to put HeapSelect class constructor/deconstructor in IRAM #define FORCE_ALWAYS_INLINE_HEAP_SELECT @@ -336,8 +342,10 @@ size_t IRAM_ATTR xPortWantedSizeAlign(size_t size) void system_show_malloc(void) { +#ifdef UMM_INFO HeapSelectDram ephemeral; umm_info(NULL, true); +#endif } /* @@ -348,25 +356,25 @@ void system_show_malloc(void) void* IRAM_ATTR pvPortMalloc(size_t size, const char* file, int line) { HeapSelectDram ephemeral; - return heap_pvPortMalloc(size, file, line);; + return heap_pvPortMalloc(size, file, line);; } void* IRAM_ATTR pvPortCalloc(size_t count, size_t size, const char* file, int line) { HeapSelectDram ephemeral; - return heap_pvPortCalloc(count, size, file, line); + return heap_pvPortCalloc(count, size, file, line); } void* IRAM_ATTR pvPortRealloc(void *ptr, size_t size, const char* file, int line) { HeapSelectDram ephemeral; - return heap_pvPortRealloc(ptr, size, file, line); + return heap_pvPortRealloc(ptr, size, file, line); } void* IRAM_ATTR pvPortZalloc(size_t size, const char* file, int line) { HeapSelectDram ephemeral; - return heap_pvPortZalloc(size, file, line); + return heap_pvPortZalloc(size, file, line); } void IRAM_ATTR vPortFree(void *ptr, const char* file, int line) @@ -376,7 +384,98 @@ void IRAM_ATTR vPortFree(void *ptr, const char* file, int line) // correct context. umm_malloc free internally determines the correct heap. HeapSelectDram ephemeral; #endif - return heap_vPortFree(ptr, file, line); + return heap_vPortFree(ptr, file, line); } +#if (NONOSDK >= (0x30000)) +//////////////////////////////////////////////////////////////////////////////// +/* + New for NON-OS SDK 3.0.0 and up + Needed for WPA2 Enterprise support. This was not present in SDK pre 3.0 + + The NON-OS SDK 3.0.x has breaking changes to pvPortMalloc. They added one more + argument for selecting a heap. To avoid breaking the build, I renamed their + breaking version to sdk3_pvPortMalloc. To complete the fix, the LIBS need to + be edited. + + Also in the release are low-level functions pvPortZallocIram and + pvPortCallocIram, which are not documented in the Espressif NONOS SDK manual. + No issues in providing replacements. For the non-Arduino ESP8266 applications, + pvPortZallocIram and pvPortCallocIram would have been selected through the + macros like os_malloc defined in `mem.h`. + + OOM - Implementation strategy - Native v3.0 SDK + * For functions `pvPortMalloc(,,,true);` and `pvPortMallocIram(,,,);` on a + failed IRAM alloc, try DRAM. + * For function `pvPortMalloc(,,,false);` use DRAM only - on fail, do not + try IRAM. + + WPA2 Enterprise connect crashing is fixed at v3.0.2 and up. +*/ +#ifdef UMM_HEAP_IRAM +void* IRAM_ATTR sdk3_pvPortMalloc(size_t size, const char* file, int line, bool iram) +{ + if (iram) { + HeapSelectIram ephemeral; + void* ret = heap_pvPortMalloc(size, file, line); + if (ret) return ret; + } + { + HeapSelectDram ephemeral; + return heap_pvPortMalloc(size, file, line); + } +} + +void* IRAM_ATTR pvPortCallocIram(size_t count, size_t size, const char* file, int line) +{ + { + HeapSelectIram ephemeral; + void* ret = heap_pvPortCalloc(count, size, file, line); + if (ret) return ret; + } + { + HeapSelectDram ephemeral; + return heap_pvPortCalloc(count, size, file, line); + } +} + +void* IRAM_ATTR pvPortZallocIram(size_t size, const char* file, int line) +{ + { + HeapSelectIram ephemeral; + void* ret = heap_pvPortZalloc(size, file, line); + if (ret) return ret; + } + { + HeapSelectDram ephemeral; + return heap_pvPortZalloc(size, file, line); + } +} +#define CONFIG_IRAM_MEMORY 1 + +#else +// For sdk3_pvPortMalloc, the bool argument is ignored and intentionally omitted. +extern "C" void* sdk3_pvPortMalloc(size_t size, const char* file, int line) __attribute__ ((alloc_size(1), malloc, nothrow, alias("pvPortMalloc"))); +extern "C" void* pvPortCallocIram(size_t count, size_t size, const char* file, int line) __attribute__((alloc_size(1, 2), malloc, nothrow, alias("pvPortCalloc"))); +extern "C" void* pvPortZallocIram(size_t size, const char* file, int line) __attribute__((alloc_size(1), malloc, nothrow, alias("pvPortZalloc"))); +#define CONFIG_IRAM_MEMORY 0 +#endif // #ifdef UMM_HEAP_IRAM + +/* + We do not need the function user_iram_memory_is_enabled(). + 1. It was used by mem_manager.o which was replaced with this custom heap + implementation. IRAM memory selection is handled differently for + Arduino ESP8266. + 2. In libmain.a, Cache_Read_Enable_New uses it for cache size. However, When + using IRAM for memory or running with 48K IRAM for code, we use a + replacement Cache_Read_Enable to correct the cache size ignoring + Cache_Read_Enable_New's selected value. + 3. Create a linker conflicts in the event the sketch author tries to control + IRAM heap through this method. +*/ +uint32 IRAM_ATTR user_iram_memory_is_enabled(void) +{ + return CONFIG_IRAM_MEMORY; +} +#endif // #if (NONOSDK >= (0x30000)) }; diff --git a/cores/esp8266/heap_api_debug.h b/cores/esp8266/heap_api_debug.h new file mode 100644 index 0000000000..62f7e7bad5 --- /dev/null +++ b/cores/esp8266/heap_api_debug.h @@ -0,0 +1,74 @@ +/* + * Issolated heap debug helper code from from umm_malloc/umm_malloc_cfg.h. + * Updated umm_malloc/umm_malloc.h and Arduino.h to reference. + * No #ifdef fenceing was used before. From its previous location, this content + * was reassert multiple times through Arduino.h. In case there are legacy + * projects that depend on the previous unfenced behavior, no fencing has been + * added. + */ + +/* + * *alloc redefinition - Included from Arduino.h for DEBUG_ESP_OOM support. + * + * It can also be directly include by the sketch for UMM_POISON_CHECK or + * UMM_POISON_CHECK_LITE builds to get more info about the caller when they + * report on a fail. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef DEBUG_ESP_OOM +#define MEMLEAK_DEBUG + +#include "umm_malloc/umm_malloc_cfg.h" + +#include +// Reuse pvPort* calls, since they already support passing location information. +// Specifically the debug version (heap_...) that does not force DRAM heap. +void *IRAM_ATTR heap_pvPortMalloc(size_t size, const char *file, int line); +void *IRAM_ATTR heap_pvPortCalloc(size_t count, size_t size, const char *file, int line); +void *IRAM_ATTR heap_pvPortRealloc(void *ptr, size_t size, const char *file, int line); +void *IRAM_ATTR heap_pvPortZalloc(size_t size, const char *file, int line); +void IRAM_ATTR heap_vPortFree(void *ptr, const char *file, int line); + +#define malloc(s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_pvPortMalloc(s, mem_debug_file, __LINE__); }) +#define calloc(n,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_pvPortCalloc(n, s, mem_debug_file, __LINE__); }) +#define realloc(p,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_pvPortRealloc(p, s, mem_debug_file, __LINE__); }) + +#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) +#define dbg_heap_free(p) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_vPortFree(p, mem_debug_file, __LINE__); }) +#else +#define dbg_heap_free(p) free(p) +#endif + +#elif defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) // #elif for #ifdef DEBUG_ESP_OOM +#include +void *IRAM_ATTR heap_pvPortRealloc(void *ptr, size_t size, const char *file, int line); +#define realloc(p,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_pvPortRealloc(p, s, mem_debug_file, __LINE__); }) + +void IRAM_ATTR heap_vPortFree(void *ptr, const char *file, int line); +// C - to be discussed +/* + Problem, I would like to report the file and line number with the umm poison + event as close as possible to the event. The #define method works for malloc, + calloc, and realloc those names are not as generic as free. A #define free + captures too much. Classes with methods called free are included :( + Inline functions would report the address of the inline function in the .h + not where they are called. + + Anybody know a trick to make this work? + + Create dbg_heap_free() as an alternative for free() when you need a little + more help in debugging the more challenging problems. +*/ +#define dbg_heap_free(p) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_vPortFree(p, mem_debug_file, __LINE__); }) + +#else +#define dbg_heap_free(p) free(p) +#endif /* DEBUG_ESP_OOM */ + +#ifdef __cplusplus +} +#endif diff --git a/cores/esp8266/hwdt_app_entry.cpp b/cores/esp8266/hwdt_app_entry.cpp index 8177832b9e..0f0bee8d4e 100644 --- a/cores/esp8266/hwdt_app_entry.cpp +++ b/cores/esp8266/hwdt_app_entry.cpp @@ -180,7 +180,9 @@ * tool performing hardware reset and exiting, then the serial monitor * re-engaging. This is not an issue that needs to be addressed here. */ - #define DEBUG_ESP_HWDT_PRINT_GREETING + #ifndef DEBUG_ESP_HWDT_PRINT_GREETING + #define DEBUG_ESP_HWDT_PRINT_GREETING (1) + #endif /* @@ -275,6 +277,7 @@ #include #include #include +#include "umm_malloc/umm_malloc.h" #include "mmu_iram.h" extern "C" { @@ -995,7 +998,7 @@ STATIC void IRAM_MAYBE handle_hwdt(void) { } #endif -#if defined(DEBUG_ESP_HWDT_PRINT_GREETING) +#if DEBUG_ESP_HWDT_PRINT_GREETING ETS_PRINTF("\n\nHardware WDT Stack Dump - enabled\n\n"); #else ETS_PRINTF("\n\n"); diff --git a/cores/esp8266/mmu_iram.cpp b/cores/esp8266/mmu_iram.cpp index 9f1b05d455..0193241890 100644 --- a/cores/esp8266/mmu_iram.cpp +++ b/cores/esp8266/mmu_iram.cpp @@ -122,43 +122,15 @@ void IRAM_ATTR Cache_Read_Disable(void) { } #endif -/* - * Early adjustment for CPU crystal frequency, so debug printing will work. - * This should not be left enabled all the time in Cashe_Read..., I am concerned - * that there may be unknown interference with the NONOS SDK startup. - * - * Inspired by: - * https://github.com/pvvx/esp8266web/blob/2e25559bc489487747205db2ef171d48326b32d4/app/sdklib/system/app_main.c#L581-L591 - */ -extern "C" uint8_t rom_i2c_readReg(uint8_t block, uint8_t host_id, uint8_t reg_add); -extern "C" void rom_i2c_writeReg(uint8_t block, uint8_t host_id, uint8_t reg_add, uint8_t data); - -extern "C" void IRAM_ATTR set_pll(void) -{ -#if !defined(F_CRYSTAL) -#define F_CRYSTAL 26000000 -#endif - if (F_CRYSTAL != 40000000) { - // At Boot ROM(-BIOS) start, it assumes a 40MHz crystal. - // If it is not, we assume a 26MHz crystal. - // There is no support for 24MHz crustal at this time. - if(rom_i2c_readReg(103,4,1) != 136) { // 8: 40MHz, 136: 26MHz - // Assume 26MHz crystal - // soc_param0: 0: 40MHz, 1: 26MHz, 2: 24MHz - // set 80MHz PLL CPU - rom_i2c_writeReg(103,4,1,136); - rom_i2c_writeReg(103,4,2,145); - } - } -} - //C This was used to probe at different stages of boot the state of the PLL //C register. I think we can get rid of this one. +extern "C" uint8_t rom_i2c_readReg(uint8_t block, uint8_t host_id, uint8_t reg_add); +extern "C" void rom_i2c_writeReg(uint8_t block, uint8_t host_id, uint8_t reg_add, uint8_t data); extern "C" void IRAM_ATTR dbg_set_pll(void) { char r103_4_1 = rom_i2c_readReg(103,4,1); char r103_4_2 = rom_i2c_readReg(103,4,2); - set_pll(); + mmu_set_pll(); ets_uart_printf("\nrom_i2c_readReg(103,4,1) == %u\n", r103_4_1); ets_uart_printf( "rom_i2c_readReg(103,4,2) == %u\n", r103_4_2); } @@ -197,9 +169,70 @@ extern void Cache_Read_Enable(uint8_t map, uint8_t p, uint8_t v); #endif // #if (MMU_ICACHE_SIZE == 0x4000) /* - * This wrapper is for running code from IROM (flash) before the SDK starts. + * Early adjustment for CPU crystal frequency will allow early debug printing to + * be readable before the SDK initialization is complete. + * This should not be left enabled all the time in Cashe_Read..., I am concerned + * that there may be unknown interference with the NONOS SDK startup. + * It does low-level calls that could clash with the SDKs startup. + * + * Inspired by: + * https://github.com/pvvx/esp8266web/blob/2e25559bc489487747205db2ef171d48326b32d4/app/sdklib/system/app_main.c#L581-L591 + */ +extern "C" uint8_t rom_i2c_readReg(uint8_t block, uint8_t host_id, uint8_t reg_add); +extern "C" void rom_i2c_writeReg(uint8_t block, uint8_t host_id, uint8_t reg_add, uint8_t data); + +extern "C" void IRAM_ATTR mmu_set_pll(void) +{ +#if !defined(F_CRYSTAL) +#define F_CRYSTAL 26000000 +#endif + if (F_CRYSTAL != 40000000) { + // At Boot ROM(-BIOS) start, it assumes a 40MHz crystal. + // If it is not, we assume a 26MHz crystal. + // There is no support for 24MHz crustal at this time. + if(rom_i2c_readReg(103,4,1) != 136) { // 8: 40MHz, 136: 26MHz + // Assume 26MHz crystal + // soc_param0: 0: 40MHz, 1: 26MHz, 2: 24MHz + // set 80MHz PLL CPU + rom_i2c_writeReg(103,4,1,136); + rom_i2c_writeReg(103,4,2,145); + } + } +} + +/* + * This wrapper is for running code early from IROM (flash) before the SDK + * starts. Since the NONOS SDK will do a full and proper flash device init for + * speed and mode, we only do a minimum to make ICACHE functional, keeping IRAM + * use to a minimum. After the SDK has started, this function is not needed and + * must not be called. */ void IRAM_ATTR mmu_wrap_irom_fn(void (*fn)(void)) { + // Cache Read must be disabled. This is always the case on entry when called + // from the right context. + // Cache_Read_Disable(); + + // The SPI_CS_SETUP parameter has been observed set by RTOS SDK and NONOS SDK + // as part of flash init/configuration. It may be necessary for some flash + // chips to perform correctly with ICACHE hardware access. Turning on and + // leaving it on should be okay. + // + // One SPI bus clock cycle time is inserted between #CS active and 1st SPI bus + // clock cycle. The number of clock cycles is in SPI_CNTRL2 SPI_SETUP_TIME, + // defaults to 1. + SPI0U |= SPIUCSSETUP; // SPI_CS_SETUP or BIT5 + + // phy_get_bb_evm is the key function, called from fix_cache_bug in the NONOS + // SDK. This addition resolves the PUYA Flash issue with exception 0, when + // early Cache_Read_Enable is used. + extern uint32_t phy_get_bb_evm(void); // undocumented + phy_get_bb_evm(); + + // For early Cache_Read_Enable, only do ICACHE_SIZE_16. With this option, + // Cache_Read_Disable will fully restore the original register states. With + // ICACHE_SIZE_32, one bit is missed when disabling. Leave the full access + // calls for the NONOS SDK. + // This only works with image slice 0, which is all we do presently. Cache_Read_Enable(0, 0, ICACHE_SIZE_16); fn(); Cache_Read_Disable(); diff --git a/cores/esp8266/mmu_iram.h b/cores/esp8266/mmu_iram.h index 121226bd4c..e80c5ec348 100644 --- a/cores/esp8266/mmu_iram.h +++ b/cores/esp8266/mmu_iram.h @@ -46,6 +46,19 @@ extern "C" { */ #endif +#if defined(DEV_DEBUG_PRINT) || defined(DEBUG_ESP_MMU) || defined(DEBUG_ESP_CORE) || defined(DEBUG_ESP_PORT) +/* + * Early adjustment for CPU crystal frequency will allow early debug printing to + * be readable before the SDK initialization is complete. + * + * It is unknown if there are any side effects with SDK startup, but a + * possibility. Out of an abundance of caution, limit the use of mmu_set_pll for + * handling printing in failure cases that finish with a reboot. Or for other + * rare debug contexts. + */ +extern void mmu_set_pll(void); +#endif + /* * DEV_DEBUG_PRINT: * Debug printing macros for printing before before, during, and after @@ -56,17 +69,17 @@ extern "C" { #define DEV_DEBUG_PRINT */ -#if defined(DEV_DEBUG_PRINT) || defined(DEBUG_ESP_MMU) +#if (defined(DEV_DEBUG_PRINT) || defined(DEBUG_ESP_MMU)) && !defined(HOST_MOCK) +// Errors follow when `#include ` is present when running CI HOST #include #define DBG_MMU_FLUSH(a) while((USS(a) >> USTXC) & 0xff) {} #if defined(DEV_DEBUG_PRINT) -extern void set_pll(void); extern void dbg_set_pll(void); #define DBG_MMU_PRINTF(fmt, ...) \ -set_pll(); \ +mmu_set_pll(); \ uart_buff_switch(0); \ ets_uart_printf(fmt, ##__VA_ARGS__); \ DBG_MMU_FLUSH(0) 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/cores/esp8266/time.cpp b/cores/esp8266/time.cpp index d8308e62d7..970beada48 100644 --- a/cores/esp8266/time.cpp +++ b/cores/esp8266/time.cpp @@ -208,6 +208,19 @@ void configTime(int timezone_sec, int daylightOffset_sec, const char* server1, c sntp_init(); } +void configTime(int timezone_sec, int daylightOffset_sec, String server1, String server2, String server3) +{ + static String servers[3]; + servers[0] = std::move(server1); + servers[1] = std::move(server2); + servers[2] = std::move(server3); + + configTime(timezone_sec, daylightOffset_sec, + servers[0].length() ? servers[0].c_str() : nullptr, + servers[1].length() ? servers[1].c_str() : nullptr, + servers[2].length() ? servers[2].c_str() : nullptr); +} + void setTZ(const char* tz){ char tzram[strlen_P(tz) + 1]; @@ -228,6 +241,19 @@ void configTime(const char* tz, const char* server1, const char* server2, const sntp_init(); } +void configTime(const char* tz, String server1, String server2, String server3) +{ + static String servers[3]; + servers[0] = std::move(server1); + servers[1] = std::move(server2); + servers[2] = std::move(server3); + + configTime(tz, + servers[0].length() ? servers[0].c_str() : nullptr, + servers[1].length() ? servers[1].c_str() : nullptr, + servers[2].length() ? servers[2].c_str() : nullptr); +} + static BoolCB _settimeofday_cb; void settimeofday_cb (const TrivialCB& cb) diff --git a/cores/esp8266/uart.cpp b/cores/esp8266/uart.cpp index 1f657c9403..802072d438 100644 --- a/cores/esp8266/uart.cpp +++ b/cores/esp8266/uart.cpp @@ -696,7 +696,7 @@ uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, size_t rx } uart_set_baudrate(uart, baudrate); - if(uart->uart_nr == UART0 && invert) + if((uart->uart_nr == UART0 || uart->uart_nr == UART1) && invert) { config |= BIT(UCDTRI) | BIT(UCRTSI) | BIT(UCTXI) | BIT(UCDSRI) | BIT(UCCTSI) | BIT(UCRXI); } diff --git a/cores/esp8266/umm_malloc/Notes.h b/cores/esp8266/umm_malloc/Notes.h index 33b2fc545a..5d076c4b67 100644 --- a/cores/esp8266/umm_malloc/Notes.h +++ b/cores/esp8266/umm_malloc/Notes.h @@ -276,4 +276,81 @@ Enhancement ideas: #endif ``` */ + +/* + Sep 26, 2022 + + History/Overview + + ESP.getFreeHeap() needs a function it can call for free Heap size. The legacy + method was the SDK function `system_get_free_heap_size()` which is in IRAM. + + `system_get_free_heap_size()` calls `xPortGetFreeHeapSize()` to get free heap + size. Our old legacy implementation used umm_info(), employing a + time-consuming method for getting free Heap size and runs with interrupts + blocked. + + Later we used a distributed method that maintained the free heap size with + each malloc API call that changed the Heap. (enabled by build option UMM_STATS + or UMM_STATS_FULL) We used an internally function `umm_free_heap_size_lw()` to + report free heap size. We satisfied the requirements for + `xPortGetFreeHeapSize()` with an alias to `umm_free_heap_size_lw()` + in replacement for the legacy umm_info() call wrapper. + + The upstream umm_malloc later implemented a similar method enabled by build + option UMM_INLINE_METRICS and introduced the function `umm_free_heap_size()`. + + The NONOS SDK alloc request must use the DRAM Heap. Need to Ensure DRAM Heap + results when multiple Heap support is enabled. Since the SDK uses portable + malloc calls pvPortMalloc, ... we leveraged that for a solution - force + pvPortMalloc, ... APIs to serve DRAM only. + + In an oversight, `xPortGetFreeHeapSize()` was left reporting the results for + the current heap selection via `umm_free_heap_size_lw()`. Thus, if an SDK + function like os_printf_plus were called when the current heap selection was + IRAM, it would get the results for the IRAM Heap. Then would receive DRAM with + an alloc request. However, when the free IRAM size is too small, it would + skip the Heap alloc request and use stack space. + + Solution + + The resolution is to rely on build UMM_STATS(default) or UMM_STATS_FULL for + free heap size information. When not available in the build, fallback to the + upstream umm_malloc's `umm_free_heap_size()` and require the build option + UMM_INLINE_METRICS. Otherwise, fail the build. + + Use function name `umm_free_heap_size_lw()` to support external request for + current heap size. When build options result in fallback using umm_info.c, + ensure UMM_INLINE_METRICS enabled and alias to `umm_free_heap_size()`. + + For the multiple Heap case, `xPortGetFreeHeapSize()` becomes a unique function + and reports only DRAM free heap size. Now `system_get_free_heap_size()` will + always report DRAM free Heap size. This might be a breaking change. + + Specifics: + + * Support `umm_free_heap_size_lw()` as an `extern`. + + * When the build options UMM_STATS/UMM_STATS_FULL are not used, fallback to + the upstream umm_malloc's `umm_free_heap_size()` function in umm_info.c + * require the UMM_INLINE_METRICS build option. + * assign `umm_free_heap_size_lw()` as an alias to `umm_free_heap_size()` + + * `xPortGetFreeHeapSize()` + * For single heap builds, alias to `umm_free_heap_size_lw()` + * For multiple Heaps builds, add a dedicated function that always reports + DRAM results. + + + April 22, 2023 + + The umm_poison logic runs outside the UMM_CRITICAL_* umbrella. When interrupt + routines do alloc calls, it is possible to interrupt an in-progress allocation + just before the poison is set, with a new alloc request resulting in a false + "poison check fail" against the in-progress allocation. The SDK does mallocs + from ISRs. SmartConfig can illustrate this issue. + + Move get_poisoned() within UMM_CRITICAL_* in umm_malloc() and umm_realloc(). + +*/ #endif diff --git a/cores/esp8266/umm_malloc/umm_info.c b/cores/esp8266/umm_malloc/umm_info.c index 4a95e994c3..c0ebb7948f 100644 --- a/cores/esp8266/umm_malloc/umm_info.c +++ b/cores/esp8266/umm_malloc/umm_info.c @@ -174,6 +174,14 @@ size_t umm_free_heap_size_core(umm_heap_context_t *_context) { return (size_t)_context->info.freeBlocks * sizeof(umm_block); } +/* + When used as the fallback option for supporting exported function + `umm_free_heap_size_lw()`, the build option UMM_INLINE_METRICS is required. + Otherwise, umm_info() would be used to complete the operation, which uses a + time-consuming method for getting free Heap and runs with interrupts off, + which can negatively impact WiFi operations. Also, it cannot support calls + from ISRs, `umm_info()` runs from flash. +*/ size_t umm_free_heap_size(void) { #ifndef UMM_INLINE_METRICS umm_info(NULL, false); diff --git a/cores/esp8266/umm_malloc/umm_local.c b/cores/esp8266/umm_malloc/umm_local.c index a28fd16d93..c08e2a27ca 100644 --- a/cores/esp8266/umm_malloc/umm_local.c +++ b/cores/esp8266/umm_malloc/umm_local.c @@ -142,8 +142,11 @@ void *umm_poison_realloc_fl(void *ptr, size_t size, const char *file, int line) add_poison_size(&size); ret = umm_realloc(ptr, size); - - ret = get_poisoned(ret, size); + /* + "get_poisoned" is now called from umm_realloc while still in a critical + section. Before umm_realloc returned, the pointer offset was adjusted to + the start of the requested buffer. + */ return ret; } @@ -161,35 +164,97 @@ void umm_poison_free_fl(void *ptr, const char *file, int line) { /* ------------------------------------------------------------------------ */ #if defined(UMM_STATS) || defined(UMM_STATS_FULL) || defined(UMM_INFO) +/* + For internal, mainly used by UMM_STATS_FULL; exported so external components + can perform Heap related calculations. +*/ size_t umm_block_size(void) { return sizeof(umm_block); } #endif +/* + Need to expose a function to support getting the current free heap size. + Export `size_t umm_free_heap_size_lw(void)` for this purpose. + Used by ESP.getFreeHeap(). + + For an expanded discussion see Notes.h, entry dated "Sep 26, 2022" +*/ #if defined(UMM_STATS) || defined(UMM_STATS_FULL) -// Keep complete call path in IRAM +/* + Default build option to support export. + + Keep complete call path in IRAM. +*/ size_t umm_free_heap_size_lw(void) { UMM_CHECK_INITIALIZED(); umm_heap_context_t *_context = umm_get_current_heap(); return (size_t)_context->UMM_FREE_BLOCKS * sizeof(umm_block); } -#endif +#elif defined(UMM_INLINE_METRICS) +/* + For the fallback option using `size_t umm_free_heap_size(void)`, we must have + the UMM_INLINE_METRICS build option enabled to support free heap size + reporting without the use of `umm_info()`. +*/ +size_t umm_free_heap_size_lw(void) __attribute__ ((alias("umm_free_heap_size"))); + +#else /* - I assume xPortGetFreeHeapSize needs to be in IRAM. Since - system_get_free_heap_size is in IRAM. Which would mean, umm_free_heap_size() - in flash, was not a safe alternative for returning the same information. + We require a resource to track and report free Heap size with low overhead. + For an expanded discussion see Notes.h, entry dated "Sep 26, 2022" */ +#error UMM_INLINE_METRICS, UMM_STATS, or UMM_STATS_FULL needs to be defined. +#endif + #if defined(UMM_STATS) || defined(UMM_STATS_FULL) -size_t xPortGetFreeHeapSize(void) __attribute__ ((alias("umm_free_heap_size_lw"))); +size_t umm_free_heap_size_core_lw(umm_heap_context_t *_context) { + return (size_t)_context->UMM_FREE_BLOCKS * sizeof(umm_block); +} + #elif defined(UMM_INFO) -#ifndef UMM_INLINE_METRICS -#warning "No ISR safe function available to implement xPortGetFreeHeapSize()" +// Backfill support for umm_free_heap_size_core_lw() +size_t umm_free_heap_size_core_lw(umm_heap_context_t *_context) __attribute__ ((alias("umm_free_heap_size_core"))); #endif + +/* + This API is called by `system_get_free_heap_size()` which is in IRAM. Driving + the assumption the callee may be in an ISR or Cache_Read_Disable state. Use + IRAM to ensure that the complete call chain is in IRAM. + + To satisfy this requirement, we need UMM_STATS... or UMM_INLINE_METRICS + defined. These support an always available without intense computation + free-Heap value. + + Like the other vPort... APIs used by the SDK, this must always report on the + DRAM Heap not the current Heap. +*/ +#if (UMM_NUM_HEAPS == 1) +// Reduce IRAM usage for the single Heap case +#if defined(UMM_STATS) || defined(UMM_STATS_FULL) +size_t xPortGetFreeHeapSize(void) __attribute__ ((alias("umm_free_heap_size_lw"))); +#else size_t xPortGetFreeHeapSize(void) __attribute__ ((alias("umm_free_heap_size"))); #endif +#else +size_t xPortGetFreeHeapSize(void) { + #if defined(UMM_STATS) || defined(UMM_STATS_FULL) || defined(UMM_INLINE_METRICS) + UMM_CHECK_INITIALIZED(); + umm_heap_context_t *_context = umm_get_heap_by_id(UMM_HEAP_DRAM); + + return umm_free_heap_size_core_lw(_context); + #else + // At this time, this build path is not reachable. In case things change, + // keep build check. + // Not in IRAM, umm_info() would have been used to complete this operation. + #error "No ISR safe function available to implement xPortGetFreeHeapSize()" + #endif +} +#endif + #if defined(UMM_STATS) || defined(UMM_STATS_FULL) void umm_print_stats(int force) { umm_heap_context_t *_context = umm_get_current_heap(); diff --git a/cores/esp8266/umm_malloc/umm_malloc.cpp b/cores/esp8266/umm_malloc/umm_malloc.cpp index 4a1bdfc76c..e130862cf7 100644 --- a/cores/esp8266/umm_malloc/umm_malloc.cpp +++ b/cores/esp8266/umm_malloc/umm_malloc.cpp @@ -909,6 +909,8 @@ void *umm_malloc(size_t size) { ptr = umm_malloc_core(_context, size); + ptr = POISON_CHECK_SET_POISON(ptr, size); + UMM_CRITICAL_EXIT(id_malloc); return ptr; @@ -926,7 +928,7 @@ void *umm_realloc(void *ptr, size_t size) { uint16_t c; - size_t curSize; + [[maybe_unused]] size_t curSize; UMM_CHECK_INITIALIZED(); @@ -1068,7 +1070,7 @@ void *umm_realloc(void *ptr, size_t size) { // Case 2 - block + next block fits EXACTLY } else if ((blockSize + nextBlockSize) == blocks) { DBGLOG_DEBUG("exact realloc using next block - %i\n", blocks); - umm_assimilate_up(c); + umm_assimilate_up(_context, c); STATS__FREE_BLOCKS_UPDATE(-nextBlockSize); blockSize += nextBlockSize; @@ -1087,8 +1089,10 @@ void *umm_realloc(void *ptr, size_t size) { STATS__FREE_BLOCKS_UPDATE(-prevBlockSize); STATS__FREE_BLOCKS_ISR_MIN(); blockSize += prevBlockSize; + // Fix new allocation such that poison checks from an ISR pass. + POISON_CHECK_SET_POISON_BLOCKS((void *)&UMM_DATA(c), blockSize); UMM_CRITICAL_SUSPEND(id_realloc); - memmove((void *)&UMM_DATA(c), ptr, curSize); + UMM_POISON_MEMMOVE((void *)&UMM_DATA(c), ptr, curSize); ptr = (void *)&UMM_DATA(c); UMM_CRITICAL_RESUME(id_realloc); // Case 5 - prev block + block + next block fits @@ -1108,8 +1112,9 @@ void *umm_realloc(void *ptr, size_t size) { #else blockSize += (prevBlockSize + nextBlockSize); #endif + POISON_CHECK_SET_POISON_BLOCKS((void *)&UMM_DATA(c), blockSize); UMM_CRITICAL_SUSPEND(id_realloc); - memmove((void *)&UMM_DATA(c), ptr, curSize); + UMM_POISON_MEMMOVE((void *)&UMM_DATA(c), ptr, curSize); ptr = (void *)&UMM_DATA(c); UMM_CRITICAL_RESUME(id_realloc); @@ -1119,8 +1124,9 @@ void *umm_realloc(void *ptr, size_t size) { void *oldptr = ptr; if ((ptr = umm_malloc_core(_context, size))) { DBGLOG_DEBUG("realloc %i to a bigger block %i, copy, and free the old\n", blockSize, blocks); + (void)POISON_CHECK_SET_POISON(ptr, size); UMM_CRITICAL_SUSPEND(id_realloc); - memcpy(ptr, oldptr, curSize); + UMM_POISON_MEMCPY(ptr, oldptr, curSize); UMM_CRITICAL_RESUME(id_realloc); umm_free_core(_context, oldptr); } else { @@ -1181,8 +1187,10 @@ void *umm_realloc(void *ptr, size_t size) { blockSize = blocks; #endif } + // Fix new allocation such that poison checks from an ISR pass. + POISON_CHECK_SET_POISON_BLOCKS((void *)&UMM_DATA(c), blockSize); UMM_CRITICAL_SUSPEND(id_realloc); - memmove((void *)&UMM_DATA(c), ptr, curSize); + UMM_POISON_MEMMOVE((void *)&UMM_DATA(c), ptr, curSize); ptr = (void *)&UMM_DATA(c); UMM_CRITICAL_RESUME(id_realloc); } else if (blockSize >= blocks) { // 2 @@ -1198,8 +1206,9 @@ void *umm_realloc(void *ptr, size_t size) { void *oldptr = ptr; if ((ptr = umm_malloc_core(_context, size))) { DBGLOG_DEBUG("realloc %d to a bigger block %d, copy, and free the old\n", blockSize, blocks); + (void)POISON_CHECK_SET_POISON(ptr, size); UMM_CRITICAL_SUSPEND(id_realloc); - memcpy(ptr, oldptr, curSize); + UMM_POISON_MEMCPY(ptr, oldptr, curSize); UMM_CRITICAL_RESUME(id_realloc); umm_free_core(_context, oldptr); } else { @@ -1223,8 +1232,9 @@ void *umm_realloc(void *ptr, size_t size) { void *oldptr = ptr; if ((ptr = umm_malloc_core(_context, size))) { DBGLOG_DEBUG("realloc %d to a bigger block %d, copy, and free the old\n", blockSize, blocks); + (void)POISON_CHECK_SET_POISON(ptr, size); UMM_CRITICAL_SUSPEND(id_realloc); - memcpy(ptr, oldptr, curSize); + UMM_POISON_MEMCPY(ptr, oldptr, curSize); UMM_CRITICAL_RESUME(id_realloc); umm_free_core(_context, oldptr); } else { @@ -1250,6 +1260,8 @@ void *umm_realloc(void *ptr, size_t size) { STATS__FREE_BLOCKS_MIN(); + ptr = POISON_CHECK_SET_POISON(ptr, size); + /* Release the critical section... */ UMM_CRITICAL_EXIT(id_realloc); @@ -1258,6 +1270,7 @@ void *umm_realloc(void *ptr, size_t size) { /* ------------------------------------------------------------------------ */ +#if !defined(UMM_POISON_CHECK) && !defined(UMM_POISON_CHECK_LITE) void *umm_calloc(size_t num, size_t item_size) { void *ret; @@ -1273,6 +1286,7 @@ void *umm_calloc(size_t num, size_t item_size) { return ret; } +#endif /* ------------------------------------------------------------------------ */ diff --git a/cores/esp8266/umm_malloc/umm_malloc.h b/cores/esp8266/umm_malloc/umm_malloc.h index d3e3ace561..2c3b22cf74 100644 --- a/cores/esp8266/umm_malloc/umm_malloc.h +++ b/cores/esp8266/umm_malloc/umm_malloc.h @@ -10,8 +10,10 @@ #include -// C This include is not in upstream +// C These includes are not in the upstream #include "umm_malloc_cfg.h" /* user-dependent */ +#include +#include #ifdef __cplusplus extern "C" { diff --git a/cores/esp8266/umm_malloc/umm_malloc_cfg.h b/cores/esp8266/umm_malloc/umm_malloc_cfg.h index 26c9c05563..bcc355f893 100644 --- a/cores/esp8266/umm_malloc/umm_malloc_cfg.h +++ b/cores/esp8266/umm_malloc/umm_malloc_cfg.h @@ -106,31 +106,6 @@ extern "C" { #include "umm_malloc_cfgport.h" #endif -#define UMM_BEST_FIT -#define UMM_INFO -// #define UMM_INLINE_METRICS -#define UMM_STATS - -/* - * To support API call, system_show_malloc(), -DUMM_INFO is required. - * - * For the ESP8266 we need an ISR safe function to call for implementing - * xPortGetFreeHeapSize(). We can get this with one of these options: - * 1) -DUMM_STATS or -DUMM_STATS_FULL - * 2) -DUMM_INLINE_METRICS (and implicitly includes -DUMM_INFO) - * - * If frequent calls are made to ESP.getHeapFragmentation(), - * -DUMM_INLINE_METRICS would reduce long periods of interrupts disabled caused - * by frequent calls to `umm_info()`. Instead, the computations get distributed - * across each malloc, realloc, and free. This appears to require an additional - * 116 bytes of IRAM vs using `UMM_STATS` with `UMM_INFO`. - * - * When both UMM_STATS and UMM_INLINE_METRICS are defined, macros and structures - * have been optimized to reduce duplications. - * - */ - - /* A couple of macros to make packing structures less compiler dependent */ #define UMM_H_ATTPACKPRE @@ -177,12 +152,22 @@ extern "C" { #define UMM_FRAGMENTATION_METRIC_REMOVE(c) #endif // UMM_INLINE_METRICS +struct UMM_HEAP_CONTEXT; +typedef struct UMM_HEAP_CONTEXT umm_heap_context_t; + +/* + Must always be defined. Core support for getting free Heap size. + When possible, access via ESP.getFreeHeap(); +*/ +extern size_t umm_free_heap_size_lw(void); +extern size_t umm_free_heap_size_core_lw(umm_heap_context_t *_context); + /* -------------------------------------------------------------------------- */ /* * -D UMM_INFO : * - * Enables a dup of the heap contents and a function to return the total + * Enables a dump of the heap contents and a function to return the total * heap size that is unallocated - note this is not the same as the largest * unallocated block on the heap! */ @@ -209,32 +194,23 @@ typedef struct UMM_HEAP_INFO_t { UMM_HEAP_INFO; // extern UMM_HEAP_INFO ummHeapInfo; -struct UMM_HEAP_CONTEXT; -typedef struct UMM_HEAP_CONTEXT umm_heap_context_t; - extern ICACHE_FLASH_ATTR void *umm_info(void *ptr, bool force); -#ifdef UMM_INLINE_METRICS -extern size_t umm_free_heap_size(void); -#else +#if defined(UMM_STATS) || defined(UMM_STATS_FULL) extern ICACHE_FLASH_ATTR size_t umm_free_heap_size(void); +extern ICACHE_FLASH_ATTR size_t umm_free_heap_size_core(umm_heap_context_t *_context); +#else +extern size_t umm_free_heap_size(void); +extern size_t umm_free_heap_size_core(umm_heap_context_t *_context); #endif + + // umm_max_block_size changed to umm_max_free_block_size in upstream. extern ICACHE_FLASH_ATTR size_t umm_max_block_size(void); extern ICACHE_FLASH_ATTR int umm_usage_metric(void); extern ICACHE_FLASH_ATTR int umm_fragmentation_metric(void); -extern ICACHE_FLASH_ATTR size_t umm_free_heap_size_core(umm_heap_context_t *_context); extern ICACHE_FLASH_ATTR size_t umm_max_block_size_core(umm_heap_context_t *_context); extern ICACHE_FLASH_ATTR int umm_usage_metric_core(umm_heap_context_t *_context); extern ICACHE_FLASH_ATTR int umm_fragmentation_metric_core(umm_heap_context_t *_context); -#else - #define umm_info(p,b) - #define umm_free_heap_size() (0) - #define umm_max_block_size() (0) - #define umm_fragmentation_metric() (0) - #define umm_usage_metric() (0) - #define umm_free_heap_size_core() (0) - #define umm_max_block_size_core() (0) - #define umm_fragmentation_metric_core() (0) #endif /* @@ -305,7 +281,6 @@ UMM_STATISTICS; #define STATS__OOM_UPDATE() _context->UMM_OOM_COUNT += 1 -extern size_t umm_free_heap_size_lw(void); extern size_t umm_get_oom_count(void); #else // not UMM_STATS or UMM_STATS_FULL @@ -643,6 +618,17 @@ extern bool umm_poison_check(void); // Local Additions to better report location in code of the caller. void *umm_poison_realloc_fl(void *ptr, size_t size, const char *file, int line); void umm_poison_free_fl(void *ptr, const char *file, int line); +#define POISON_CHECK_SET_POISON(p, s) get_poisoned(p, s) +#define POISON_CHECK_SET_POISON_BLOCKS(p, s) \ + do { \ + size_t super_size = (s * sizeof(umm_block)) - (sizeof(((umm_block *)0)->header)); \ + get_poisoned(p, super_size); \ + } while (false) +#define UMM_POISON_SKETCH_PTR(p) ((void *)((uintptr_t)p + sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE)) +#define UMM_POISON_SKETCH_PTRSZ(p) (*(UMM_POISONED_BLOCK_LEN_TYPE *)p) +#define UMM_POISON_MEMMOVE(t, p, s) memmove(UMM_POISON_SKETCH_PTR(t), UMM_POISON_SKETCH_PTR(p), UMM_POISON_SKETCH_PTRSZ(p)) +#define UMM_POISON_MEMCPY(t, p, s) memcpy(UMM_POISON_SKETCH_PTR(t), UMM_POISON_SKETCH_PTR(p), UMM_POISON_SKETCH_PTRSZ(p)) + #if defined(UMM_POISON_CHECK_LITE) /* * We can safely do individual poison checks at free and realloc and stay @@ -662,9 +648,12 @@ void umm_poison_free_fl(void *ptr, const char *file, int line); #else #define POISON_CHECK() 1 #define POISON_CHECK_NEIGHBORS(c) do {} while (false) +#define POISON_CHECK_SET_POISON(p, s) (p) +#define POISON_CHECK_SET_POISON_BLOCKS(p, s) +#define UMM_POISON_MEMMOVE(t, p, s) memmove((t), (p), (s)) +#define UMM_POISON_MEMCPY(t, p, s) memcpy((t), (p), (s)) #endif - #if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) /* * Overhead adjustments needed for free_blocks to express the number of bytes @@ -720,91 +709,9 @@ struct UMM_TIME_STATS_t { UMM_TIME_STAT id_no_tag; }; #endif -///////////////////////////////////////////////// -#ifdef DEBUG_ESP_OOM - -#define MEMLEAK_DEBUG - -// umm_*alloc are not renamed to *alloc -// Assumes umm_malloc.h has already been included. - -#define umm_zalloc(s) umm_calloc(1,s) - -void *malloc_loc(size_t s, const char *file, int line); -void *calloc_loc(size_t n, size_t s, const char *file, int line); -void *realloc_loc(void *p, size_t s, const char *file, int line); -// *alloc are macro calling *alloc_loc calling+checking umm_*alloc() -// they are defined at the bottom of this file - -///////////////////////////////////////////////// - -#elif defined(UMM_POISON_CHECK) -void *realloc_loc(void *p, size_t s, const char *file, int line); -void free_loc(void *p, const char *file, int line); -#else // !defined(ESP_DEBUG_OOM) -#endif - - - #ifdef __cplusplus } #endif #endif /* _UMM_MALLOC_CFG_H */ - -#ifdef __cplusplus -extern "C" { -#endif -#ifdef DEBUG_ESP_OOM -// this must be outside from "#ifndef _UMM_MALLOC_CFG_H" -// because Arduino.h's does #undef *alloc -// Arduino.h recall us to redefine them -#include -// Reuse pvPort* calls, since they already support passing location information. -// Specifically the debug version (heap_...) that does not force DRAM heap. -void *IRAM_ATTR heap_pvPortMalloc(size_t size, const char *file, int line); -void *IRAM_ATTR heap_pvPortCalloc(size_t count, size_t size, const char *file, int line); -void *IRAM_ATTR heap_pvPortRealloc(void *ptr, size_t size, const char *file, int line); -void *IRAM_ATTR heap_pvPortZalloc(size_t size, const char *file, int line); -void IRAM_ATTR heap_vPortFree(void *ptr, const char *file, int line); - -#define malloc(s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_pvPortMalloc(s, mem_debug_file, __LINE__); }) -#define calloc(n,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_pvPortCalloc(n, s, mem_debug_file, __LINE__); }) -#define realloc(p,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_pvPortRealloc(p, s, mem_debug_file, __LINE__); }) - -#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) -#define dbg_heap_free(p) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_vPortFree(p, mem_debug_file, __LINE__); }) -#else -#define dbg_heap_free(p) free(p) -#endif - -#elif defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) // #elif for #ifdef DEBUG_ESP_OOM -#include -void *IRAM_ATTR heap_pvPortRealloc(void *ptr, size_t size, const char *file, int line); -#define realloc(p,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_pvPortRealloc(p, s, mem_debug_file, __LINE__); }) - -void IRAM_ATTR heap_vPortFree(void *ptr, const char *file, int line); -// C - to be discussed -/* - Problem, I would like to report the file and line number with the umm poison - event as close as possible to the event. The #define method works for malloc, - calloc, and realloc those names are not as generic as free. A #define free - captures too much. Classes with methods called free are included :( - Inline functions would report the address of the inline function in the .h - not where they are called. - - Anybody know a trick to make this work? - - Create dbg_heap_free() as an alternative for free() when you need a little - more help in debugging the more challenging problems. -*/ -#define dbg_heap_free(p) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_vPortFree(p, mem_debug_file, __LINE__); }) - -#else -#define dbg_heap_free(p) free(p) -#endif /* DEBUG_ESP_OOM */ - -#ifdef __cplusplus -} -#endif diff --git a/cores/esp8266/umm_malloc/umm_malloc_cfgport.h b/cores/esp8266/umm_malloc/umm_malloc_cfgport.h index 02db6fc66c..233671304f 100644 --- a/cores/esp8266/umm_malloc/umm_malloc_cfgport.h +++ b/cores/esp8266/umm_malloc/umm_malloc_cfgport.h @@ -1,13 +1,9 @@ -#ifndef _UMM_MALLOC_CFGPORT_H -#define _UMM_MALLOC_CFGPORT_H - -#ifndef _UMM_MALLOC_CFG_H -#error "This include file must be used with umm_malloc_cfg.h" -#endif - /* * Arduino ESP8266 core umm_malloc port config */ + +#ifdef _UMM_MALLOC_CFG_H +// Additional includes for "umm_malloc_cfg.h" only #include #include #include "../debug.h" @@ -18,9 +14,20 @@ #include #include "c_types.h" +#endif + + +#ifndef _UMM_MALLOC_CFGPORT_H +#define _UMM_MALLOC_CFGPORT_H /* - * -DUMM_INIT_USE_IRAM + * Between UMM_BEST_FIT or UMM_FIRST_FIT, UMM_BEST_FIT is the better option for + * reducing heap fragmentation. With no selection made, UMM_BEST_FIT is used. + * See umm_malloc_cfg.h for more information. + */ + +/* + * -DUMM_INIT_USE_ICACHE * * Historically, the umm_init() call path has been in IRAM. The umm_init() call * path is now in ICACHE (flash). Use the build option UMM_INIT_USE_IRAM to @@ -30,10 +37,16 @@ * app_entry_redefinable() in core_esp8266_app_entry_noextra4k.cpp for an * example of how to toggle between ICACHE and IRAM in your build. * - * The default is to use ICACHE. + * ~The default is to use ICACHE.~ + * For now revert default back to IRAM + * define UMM_INIT_USE_ICACHE to use ICACHE/IROM */ -// #define UMM_INIT_USE_IRAM 1 - +#ifdef UMM_INIT_USE_ICACHE +#undef UMM_INIT_USE_IRAM +#else +#undef UMM_INIT_USE_IRAM +#define UMM_INIT_USE_IRAM 1 +#endif /* * Start addresses and the size of the heap @@ -87,5 +100,120 @@ extern char _heap_start[]; #define UMM_HEAP_STACK_DEPTH 32 #endif +/* + * The NONOS SDK API requires function `umm_info()` for implementing + * `system_show_malloc()`. Build option `-DUMM_INFO` enables this support. + * + * Also, `-DUMM_INFO` is needed to support several EspClass methods. + * Partial EspClass method list: + * `uint32_t EspClass::getMaxFreeBlockSize()` + * `void EspClass::getHeapStats(uint32_t* hfree, uint32_t* hmax, uint8_t* hfrag)` + * `uint8_t EspClass::getHeapFragmentation()` + * + * The NONOS SDK API requires an ISR safe function to call for implementing + * `xPortGetFreeHeapSize()`. Use one of these options: + * 1) `-DUMM_STATS` or `-DUMM_STATS_FULL` + * 2) `-DUMM_INLINE_METRICS` (implicitly includes `-DUMM_INFO`) + * + * If frequent calls are made to `ESP.getHeapFragmentation()`, using build + * option `-DUMM_INLINE_METRICS` would reduce long periods of interrupts + * disabled caused by frequent calls to `umm_info().` Instead, the computations + * get distributed across each malloc, realloc, and free. Requires approximately + * 116 more bytes of IRAM when compared to the build option `-DUMM_STATS` with + * `-DUMM_INFO.` + * + * When both `-DUMM_STATS` and `-DUMM_INLINE_METRICS` are defined, macros and + * structures are optimized to reduce duplications. + * + * You can use `-DUMM_INFO` with `-DUMM_INLINE_METRICS` and drop + * `-DUMM_STATS(_FULL)` gaining back some IROM at the expense of IRAM. + * + * If you don't require the methods in EspClass that are dependent on functions + * from the `-DUMM_INFO` build option, you can use only `-DUMM_STATS` and save + * on IROM and a little IRAM. + * + */ +#if defined(UMM_STATS) || defined(UMM_STATS_FULL) || defined(UMM_INLINE_METRICS) || defined(UMM_INFO) +/* + User defined via build options eg. Sketch.ino.globals.h +*/ +#else +/* + Set expected/implicit defaults for complete support of EspClass methods. +*/ +#define UMM_INFO 1 +#define UMM_STATS 1 +#endif + +/* + For `-Dname`, gcc assigns a value of 1 and this works fine; however, + if `-Dname=0` is used, the intended results will not be obtained. + + Make value and valueless defines compliant with their usage in umm_malloc: + `#define name` => #define name 1 + `#define name 0` => #undef name +*/ +#if ((1 - UMM_BEST_FIT - 1) == 2) +// When UMM_BEST_FIT is defined w/o value, the computation becomes +// (1 - - 1) == 2 => (1 + 1) == 2 +#undef UMM_BEST_FIT +#define UMM_BEST_FIT 1 +#elif ((1 - UMM_BEST_FIT - 1) == 0) +#undef UMM_BEST_FIT +#endif +#if ((1 - UMM_FIRST_FIT - 1) == 2) +#undef UMM_FIRST_FIT +#define UMM_FIRST_FIT 1 +#elif ((1 - UMM_FIRST_FIT - 1) == 0) +#undef UMM_FIRST_FIT +#endif + +#if ((1 - UMM_INFO - 1) == 2) +#undef UMM_INFO +#define UMM_INFO 1 +#elif ((1 - UMM_INFO - 1) == 0) +#undef UMM_INFO +#endif +#if ((1 - UMM_INLINE_METRICS - 1) == 2) +#undef UMM_INLINE_METRICS +#define UMM_INLINE_METRICS 1 +#elif ((1 - UMM_INLINE_METRICS - 1) == 0) +#undef UMM_INLINE_METRICS +#endif + +#if ((1 - UMM_STATS - 1) == 2) +#undef UMM_STATS +#define UMM_STATS 1 +#elif ((1 - UMM_STATS - 1) == 0) +#undef UMM_STATS +#endif +#if ((1 - UMM_STATS_FULL - 1) == 2) +#undef UMM_STATS_FULL +#define UMM_STATS_FULL 1 +#elif ((1 - UMM_STATS_FULL - 1) == 0) +#undef UMM_STATS_FULL +#endif + + +#if defined(UMM_INLINE_METRICS) +// Dependent on UMM_INFO if missing enable. +#ifndef UMM_INFO +#define UMM_INFO 1 +#endif +#endif + +#if defined(UMM_STATS) || defined(UMM_STATS_FULL) +// We have support for free Heap size +#if defined(UMM_STATS) && defined(UMM_STATS_FULL) +#error "Build option conflict, specify either UMM_STATS or UMM_STATS_FULL." +#endif +#elif defined(UMM_INFO) +// ensure fallback support for free Heap size +#ifndef UMM_INLINE_METRICS +#define UMM_INLINE_METRICS 1 +#endif +#else +#error "Specify at least one of these build options: (UMM_STATS or UMM_STATS_FULL) and/or UMM_INFO and/or UMM_INLINE_METRICS" +#endif #endif diff --git a/cores/esp8266/umm_malloc/umm_poison.c b/cores/esp8266/umm_malloc/umm_poison.c index dc9d5322bf..ca41cabf4f 100644 --- a/cores/esp8266/umm_malloc/umm_poison.c +++ b/cores/esp8266/umm_malloc/umm_poison.c @@ -15,7 +15,7 @@ * If `s` is 0, returns 0. * If result overflows/wraps, return saturation value. */ -static void add_poison_size(size_t* s) { +static void add_poison_size(size_t *s) { if (*s == 0) { return; } @@ -163,8 +163,11 @@ void *umm_poison_malloc(size_t size) { add_poison_size(&size); ret = umm_malloc(size); - - ret = get_poisoned(ret, size); + /* + "get_poisoned" is now called from umm_malloc while still in a critical + section. Before umm_malloc returned, the pointer offset was adjusted to + the start of the requested buffer. + */ return ret; } @@ -177,17 +180,16 @@ void *umm_poison_calloc(size_t num, size_t item_size) { // Use saturated multiply. // Rely on umm_malloc to supply the fail response as needed. size_t size = umm_umul_sat(num, item_size); + size_t request_sz = size; add_poison_size(&size); ret = umm_malloc(size); if (NULL != ret) { - memset(ret, 0x00, size); + memset(ret, 0x00, request_sz); } - ret = get_poisoned(ret, size); - return ret; } @@ -200,8 +202,11 @@ void *umm_poison_realloc(void *ptr, size_t size) { add_poison_size(&size); ret = umm_realloc(ptr, size); - - ret = get_poisoned(ret, size); + /* + "get_poisoned" is now called from umm_realloc while still in a critical + section. Before umm_realloc returned, the pointer offset was adjusted to + the start of the requested buffer. + */ return ret; } diff --git a/cores/esp8266/wl_definitions.h b/cores/esp8266/wl_definitions.h index b58085ac59..df175c446f 100644 --- a/cores/esp8266/wl_definitions.h +++ b/cores/esp8266/wl_definitions.h @@ -69,20 +69,8 @@ enum wl_enc_type { /* Values map to 802.11 encryption suites... */ ENC_TYPE_AUTO = 8 }; -#if !defined(LWIP_INTERNAL) && !defined(__LWIP_TCP_H__) && !defined(LWIP_HDR_TCPBASE_H) -enum wl_tcp_state { - CLOSED = 0, - LISTEN = 1, - SYN_SENT = 2, - SYN_RCVD = 3, - ESTABLISHED = 4, - FIN_WAIT_1 = 5, - FIN_WAIT_2 = 6, - CLOSE_WAIT = 7, - CLOSING = 8, - LAST_ACK = 9, - TIME_WAIT = 10 -}; -#endif +#include +#include +using wl_tcp_state = tcp_state; #endif /* WL_DEFINITIONS_H_ */ diff --git a/cores/esp8266/wpa2_eap_patch.cpp b/cores/esp8266/wpa2_eap_patch.cpp new file mode 100644 index 0000000000..268c65e7b2 --- /dev/null +++ b/cores/esp8266/wpa2_eap_patch.cpp @@ -0,0 +1,194 @@ +/* + * To implement this patch, the SDK module `eap.o` from archive `libwpa2.a` must + * be patched to call `z2EapFree` instead of `vPortFree`. This limits extending + * the execution time of vPortFree to that module only. Not impacting other + * modules. + * + */ +#include +#include +#include +#include "coredecls.h" + +#if defined(NONOSDK22x_190703) || \ + defined(NONOSDK22x_191122) || \ + defined(NONOSDK22x_191105) || \ + defined(NONOSDK22x_191024) || \ + defined(NONOSDK22x_190313) || \ + defined(NONOSDK221) || \ + defined(NONOSDK305) + +// eap_peer_config_deinit() - For this list of SDKs there are no significant +// changes in the function. Just the line number reference for when vPortFree +// is called. When vPortFree is called, register a12 continues to hold a pointer +// to the struct StateMachine. Our cleanup routine should continue to work. +#if defined(NONOSDK305) + // At v3.0.5 Espressif moved `.text.eap_peer_config_deinit` to + // `eap_peer_config_deinit` then later in latest git they moved it + // back. For our linker script both are placed in flash. + #define SDK_LEAK_LINE 831 +#else + #define SDK_LEAK_LINE 799 +#endif + +#ifdef DEBUG_WPA2_EAP_PATCH +#include "esp8266_undocumented.h" +#define DEBUG_PRINTF ets_uart_printf +#else +#define DEBUG_PRINTF(...) +#endif + +extern "C" { + +// extern "C" void z2EapFree(void *ptr, const char* file, int line) __attribute__((weak, alias("vPortFree"))); + +/* + * Limited 2-part wrapper for vPortFree calls made in SDK module `eap.o` from + * archive `libwpa2.a`. + * + * vPortFree calls from eap.o are monitored for calls from line 799. This is + * the location of the memory leak. At entry register a12 contains the structure + * address which has the addresses of the allocations that will be leaked. + * + * Part 1 of this wrapper, z2EapFree, appends the value of register a12 as a + * 4th argument to part2 of this wrapper, patch_wpa2_eap_vPortFree_a12(). Which + * in turn checks and frees the additional allocations, that would have been + * lost. + * + * extern "C" z2EapFree(void*); + */ + +/* + * Part 1 of Limited vPortFree Wrapper + */ +asm( + // ".section .iram.text.z2EapFree,\"ax\",@progbits\n\t" + // Since all the possible callers in eap.o are in sections starting with + // .text and not .iram.text we should be safe putting these wrappers in .text. + ".section .text.z2EapFree,\"ax\",@progbits\n\t" + ".literal_position\n\t" + ".literal .patch_wpa2_eap_vPortFree_a12, patch_wpa2_eap_vPortFree_a12\n\t" + ".align 4\n\t" + ".global z2EapFree\n\t" + ".type z2EapFree, @function\n\t" + "\n" +"z2EapFree:\n\t" + "addi a1, a1, -16\n\t" + "s32i a0, a1, 0\n\t" + "mov a5, a12\n\t" + "l32r a0, .patch_wpa2_eap_vPortFree_a12\n\t" + "callx0 a0\n\t" + "l32i a0, a1, 0\n\t" + "addi a1, a1, 16\n\t" + "ret\n\t" + ".size z2EapFree, .-z2EapFree\n\t" +); + +/* + * While some insight can be gained from the ESP32 repo for this structure. + * It does not match exactly. This alternate structure focuses on correct offset + * rather than trying to exactly reconstruct the original labels. + * These offset were found in libwpa2.a:eap.o .text.eap_peer_config_init + */ +struct StateMachine { // size 200 bytes + void* beforeConfig[16]; + void* config[26]; + // 0 - s32i a2, a12, 64 // username / Identity + // 1 - s32i a2, a12, 68 // length + // 2 - s32i a2, a12, 72 // anonymous Identity + // 3 - s32i a2, a12, 76 + // 4 - s32i a2, a12, 80 // password + // 5 - s32i a2, a12, 84 + // + // "new password" - From wifi_station_set_enterprise_new_password(), we see + // global saved value .bss+32 and .bss+36 which are later used to populate + // ".config" in eap_peer_config_init(). I do not have an environment to + // exercise this parameter. In my tests, the "new password" element in the + // ".config" is never initialized. At the moment, I don't see any code that + // would free the allocation. + // allocated via pvPortZalloc from line 0x30f, 783 + // 21 - s32i a2, a12, 148 // new password + // 22 - s32i a2, a12, 152 + + void* afterConfig[8]; +}; + +/* + * Part 2 of Limited vPortFree Wrapper + * + * Presently, all SDKs have the same memory leaks in the same module at the + * same line. + */ +void patch_wpa2_eap_vPortFree_a12(void *ptr, const char* file, int line, void* a12) { + if (SDK_LEAK_LINE == line) { + // This caller is eap_peer_config_deinit() + struct StateMachine* sm = (struct StateMachine*)a12; + if (ptr == sm->config[0]) { + // Fix leaky frunction - eap.o only frees one out of 4 config items + // finish the other 3 first + vPortFree(sm->config[2], file, line); + vPortFree(sm->config[4], file, line); + vPortFree(sm->config[21], file, line); + // ptr is sm->config[0], let fall through handle it + } +#ifdef DEBUG_WPA2_EAP_PATCH + DEBUG_PRINTF("\nz2EapFree/vPortFree patch struct StateMachine * = %8p\n", a12); + DEBUG_PRINTF(" config[0] vPortFree(%8p, file, line);\n", ptr); + DEBUG_PRINTF(" config[2] vPortFree(%8p, file, line);\n", sm->config[2]); + DEBUG_PRINTF(" config[4] vPortFree(%8p, file, line);\n", sm->config[4]); + DEBUG_PRINTF(" config[21] vPortFree(%8p, file, line);\n", sm->config[21]); + if (a12) { + void** pw = (void**)a12; + DEBUG_PRINTF("\nhexdump struct StateMachine:\n"); + for (size_t i=0; i<200/4; i+=4) { + DEBUG_PRINTF("%03u: %8p %8p %8p %8p\n", i*4, pw[i], pw[i+1], pw[i+2], pw[i+3]); + } + } +#endif + } + +#if defined(NONOSDK300) || defined(NONOSDK301) + else if (682 == line) { + // This caller is wpa2_sm_rx_eapol() + // 1st of a double free + // let the 2nd free handle it. + return; + } +#elif defined(NONOSDK302) || defined(NONOSDK303) || defined(NONOSDK304) || defined(NONOSDK305) + // It looks like double free is fixed. WPA2 Enterpise connections work + // without crashing. wpa2_sm_rx_eapol() has a few changes between NONOSDK301 + // and NONOSDK302. However, this set of releases still have memory leaks. +#else + // This is not needed because the call was NO-OPed in the library. + // Keep code snippit for reference. + // else if (672 == line) { + // // This caller is wpa2_sm_rx_eapol() + // // 1st of a double free + // // let the 2nd free handle it. + // return; + // } +#endif + vPortFree(ptr, file, line); +} + +}; + +#else +#error "Internal error: A new SDK has been added. This module must be updated." +#error " Need to test WPA2 Enterpise connectivity." +#endif + +/* + * This will minimize code space for non-wifi enterprise sketches which do not + * need the patch and disable_extra4k_at_link_time(). + */ +void enable_wifi_enterprise_patch(void) { + /* + * Calling this from setup or anywhere ensures that the patch code is + * included in the build. + * + * Also, WiFi Enterprise uses a lot of system stack space and may crash + * unless we: + */ + disable_extra4k_at_link_time(); +} 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/Troubleshooting/stack_dump.rst b/doc/Troubleshooting/stack_dump.rst index c977fce56d..ef320392e7 100644 --- a/doc/Troubleshooting/stack_dump.rst +++ b/doc/Troubleshooting/stack_dump.rst @@ -12,42 +12,42 @@ Example: Exception (0): epc1=0x402103f4 epc2=0x00000000 epc3=0x00000000 excvaddr=0x00000000 depc=0x00000000 - ctx: sys + ctx: sys sp: 3ffffc10 end: 3fffffb0 offset: 01a0 >>>stack>>> - 3ffffdb0: 40223e00 3fff6f50 00000010 60000600 - 3ffffdc0: 00000001 4021f774 3fffc250 4000050c - 3ffffdd0: 400043d5 00000030 00000016 ffffffff - 3ffffde0: 400044ab 3fffc718 3ffffed0 08000000 - 3ffffdf0: 60000200 08000000 00000003 00000000 - 3ffffe00: 0000ffff 00000001 04000002 003fd000 - 3ffffe10: 3fff7188 000003fd 3fff2564 00000030 - 3ffffe20: 40101709 00000008 00000008 00000020 - 3ffffe30: c1948db3 394c5e70 7f2060f2 c6ba0c87 - 3ffffe40: 3fff7058 00000001 40238d41 3fff6ff0 - 3ffffe50: 3fff6f50 00000010 60000600 00000020 - 3ffffe60: 402301a8 3fff7098 3fff7014 40238c77 - 3ffffe70: 4022fb6c 40230ebe 3fff1a5b 3fff6f00 - 3ffffe80: 3ffffec8 00000010 40231061 3fff0f90 - 3ffffe90: 3fff6848 3ffed0c0 60000600 3fff6ae0 - 3ffffea0: 3fff0f90 3fff0f90 3fff6848 3fff6d40 - 3ffffeb0: 3fff28e8 40101233 d634fe1a fffeffff - 3ffffec0: 00000001 00000000 4022d5d6 3fff6848 - 3ffffed0: 00000002 4000410f 3fff2394 3fff6848 - 3ffffee0: 3fffc718 40004a3c 000003fd 3fff7188 - 3ffffef0: 3fffc718 40101510 00000378 3fff1a5b - 3fffff00: 000003fd 4021d2e7 00000378 000003ff - 3fffff10: 00001000 4021d37d 3fff2564 000003ff - 3fffff20: 000003fd 60000600 003fd000 3fff2564 - 3fffff30: ffffff00 55aa55aa 00000312 0000001c - 3fffff40: 0000001c 0000008a 0000006d 000003ff - 3fffff50: 4021d224 3ffecf90 00000000 3ffed0c0 - 3fffff60: 00000001 4021c2e9 00000003 3fff1238 - 3fffff70: 4021c071 3ffecf84 3ffecf30 0026a2b0 - 3fffff80: 4021c0b6 3fffdab0 00000000 3fffdcb0 - 3fffff90: 3ffecf40 3fffdab0 00000000 3fffdcc0 - 3fffffa0: 40000f49 40000f49 3fffdab0 40000f49 + 3ffffdb0: 40223e00 3fff6f50 00000010 60000600 + 3ffffdc0: 00000001 4021f774 3fffc250 4000050c + 3ffffdd0: 400043d5 00000030 00000016 ffffffff + 3ffffde0: 400044ab 3fffc718 3ffffed0 08000000 + 3ffffdf0: 60000200 08000000 00000003 00000000 + 3ffffe00: 0000ffff 00000001 04000002 003fd000 + 3ffffe10: 3fff7188 000003fd 3fff2564 00000030 + 3ffffe20: 40101709 00000008 00000008 00000020 + 3ffffe30: c1948db3 394c5e70 7f2060f2 c6ba0c87 + 3ffffe40: 3fff7058 00000001 40238d41 3fff6ff0 + 3ffffe50: 3fff6f50 00000010 60000600 00000020 + 3ffffe60: 402301a8 3fff7098 3fff7014 40238c77 + 3ffffe70: 4022fb6c 40230ebe 3fff1a5b 3fff6f00 + 3ffffe80: 3ffffec8 00000010 40231061 3fff0f90 + 3ffffe90: 3fff6848 3ffed0c0 60000600 3fff6ae0 + 3ffffea0: 3fff0f90 3fff0f90 3fff6848 3fff6d40 + 3ffffeb0: 3fff28e8 40101233 d634fe1a fffeffff + 3ffffec0: 00000001 00000000 4022d5d6 3fff6848 + 3ffffed0: 00000002 4000410f 3fff2394 3fff6848 + 3ffffee0: 3fffc718 40004a3c 000003fd 3fff7188 + 3ffffef0: 3fffc718 40101510 00000378 3fff1a5b + 3fffff00: 000003fd 4021d2e7 00000378 000003ff + 3fffff10: 00001000 4021d37d 3fff2564 000003ff + 3fffff20: 000003fd 60000600 003fd000 3fff2564 + 3fffff30: ffffff00 55aa55aa 00000312 0000001c + 3fffff40: 0000001c 0000008a 0000006d 000003ff + 3fffff50: 4021d224 3ffecf90 00000000 3ffed0c0 + 3fffff60: 00000001 4021c2e9 00000003 3fff1238 + 3fffff70: 4021c071 3ffecf84 3ffecf30 0026a2b0 + 3fffff80: 4021c0b6 3fffdab0 00000000 3fffdcb0 + 3fffff90: 3ffecf40 3fffdab0 00000000 3fffdcc0 + 3fffffa0: 40000f49 40000f49 3fffdab0 40000f49 <<`__ tool. +It's possible to decode the Stack to readable information. +You can get a copy and read about the `Esp Exception Decoder `__ tool. + +For a troubleshooting example using the Exception Decoder Tool, read `FAQ: My ESP Crashes <../faq/a02-my-esp-crashes.rst#exception-decoder>`__. .. figure:: ESP_Exception_Decoderp.png :alt: ESP Exception Decoder diff --git a/doc/boards.rst b/doc/boards.rst index 5721fab624..99421d86b9 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 1.0 +----------- + +Based on ESP8266, Mercury is board developed by Ralio Technologies. Board supports on motor drivers and direct-connect feature for various endpoints. + +Product page: https://www.raliotech.com + Phoenix 1.0 ----------- @@ -351,6 +367,11 @@ LOLIN(WEMOS) D1 R2 & mini Product page: https://www.wemos.cc/ +LOLIN(WEMOS) D1 ESP-WROOM-02 +---------------------------- + +No real product pages. See: https://www.instructables.com/How-to-Use-Wemos-ESP-Wroom-02-D1-Mini-WiFi-Module-/ or https://www.arduino-tech.com/wemos-esp-wroom-02-mainboard-d1-mini-wifi-module-esp826618650-battery/ + LOLIN(WEMOS) D1 mini (clone) ---------------------------- @@ -408,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..0eef82bc24 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -66,12 +66,12 @@ # # 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 = ['_venv', '_build', 'Thumbs.db', '.DS_Store'] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' @@ -162,11 +162,7 @@ # # 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/bearssl-client-secure-class.rst b/doc/esp8266wifi/bearssl-client-secure-class.rst index 4523759f78..28cf590d6d 100644 --- a/doc/esp8266wifi/bearssl-client-secure-class.rst +++ b/doc/esp8266wifi/bearssl-client-secure-class.rst @@ -48,7 +48,7 @@ As a rule, either keep your objects global, use `new` to create them, or ensure TLS and HTTPS Basics ~~~~~~~~~~~~~~~~~~~~ -The following discussion is only intended to give a rough idea of TLS/HTTPS(which is just HTTP over a TLS connection) and the components an application needs to manage to make a TLS connection. For more detailed information, please check the relevant `RFC 5246 `__ and others. +The following discussion is only intended to give a rough idea of TLS/HTTPS(which is just HTTP over a TLS connection) and the components an application needs to manage to make a TLS connection. For more detailed information, please check the relevant `RFC 5246 `__ and others. TLS can be broken into two stages: verifying the identities of server (and potentially client), and then encrypting blocks of data bidirectionally. Verifying the identity of the other partner is handled via keys encoded in X509 certificates, optionally signed by a series of other entities. diff --git a/doc/esp8266wifi/client-class.rst b/doc/esp8266wifi/client-class.rst index 11e412181e..19bc3e660f 100644 --- a/doc/esp8266wifi/client-class.rst +++ b/doc/esp8266wifi/client-class.rst @@ -29,6 +29,36 @@ Default input value 0 means that effective value is left at the discretion of th ``stop()`` returns ``false`` in case of an issue when closing the client (for instance a timed-out ``flush``). Depending on implementation, its parameter can be passed to ``flush()``. +abort +~~~~~ + +.. code:: cpp + + void abort(); + + +Originally proposed in `#8738 `__ +Unlike ``stop()``, immediately shuts down internal connection object. + +Under usual circumstances, we either enter ``CLOSE_WAIT`` or ``TIME_WAIT`` state. But, the connection object is not freed right away, and requires us to either +* wait until ``malloc()`` returns ``NULL`` when our TCP stack tries to allocate memory for a new connection +* manually call ``tcp_kill_timewait()`` to forcibly stop the 'oldest' connection + +This API frees up resources used by the connection. Consider using it instead of ``stop()`` if your application handles a lot of clients and frequently runs out of available heap memory. + +*Example:* + +.. code:: cpp + + # define MIN_HEAP_FREE 20000 // or whatever min available heap memory convienent for your application + auto client = server.accept(); + // ... do something with the client object ... + if (ESP.getFreeHeap() >= MIN_HEAP_FREE) { + client.stop(); + } else { + client.abort(); + } + setNoDelay ~~~~~~~~~~ diff --git a/doc/esp8266wifi/generic-class.rst b/doc/esp8266wifi/generic-class.rst index f1ed42ea06..f722bff3af 100644 --- a/doc/esp8266wifi/generic-class.rst +++ b/doc/esp8266wifi/generic-class.rst @@ -55,6 +55,10 @@ This change is harmless with standard sketches: Calls to ``WiFi.mode()`` do enable radio as usual. It also smooths current spikes at boot and decreases DHCP stress. +Known side-effects: + +- ``WiFi.mode()`` must be called before changing mac addresses with ``wifi_set_macaddr({SOFTAP,STATION}_IF, ...)``. + Legacy behavior can be restored by calling ``enableWiFiAtBootTime()`` from anywhere in the code (it is a weak void function intended to play with the linker). @@ -223,7 +227,11 @@ Other Function Calls bool enableAP (bool enable) int hostByName (const char *aHostname, IPAddress &aResult) - appeared with SDK pre-V3: + +Also, when using NONOS SDK v3: + +.. code:: cpp + uint8_t getListenInterval (); bool isSleepLevelMax (); 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/scan-class.rst b/doc/esp8266wifi/scan-class.rst index eb94de8aad..4629a97a94 100644 --- a/doc/esp8266wifi/scan-class.rst +++ b/doc/esp8266wifi/scan-class.rst @@ -236,3 +236,29 @@ The ``networkItem`` is a zero based index of network discovered during scan. All 6: UPC Wi-Free, Ch:11 (-79dBm) For code samples please refer to separate section with `examples `__ dedicated specifically to the Scan Class. + +getScanInfoByIndex +^^^^^^^^^^^^^^^^^^ + +Similar to the ``getNetworkInfo``, but instead returns a pointer to the Nth ``bss_info`` structure which is internally used by the NONOS SDK. + +.. code:: cpp + + WiFi.getScanInfoByIndex(networkItem) + +The ``networkItem`` is a zero based index of network discovered during scan. Function will return ``nullptr`` when ``networkItem`` is greater than the number of networks in the scan result or when there are no scan results available. + +.. code:: cpp + + auto n = WiFi.scanNetworks(false, true); + if (n <= 0) { + // scan failed or there are no results + return; + } + + for (int i = 0; i < n; i++) + const auto* info = WiFi.getScanInfoByIndex(i) + // ... use the raw data from the bss_info structure ... + } + +See ``tools/sdk/include/user_interface.h`` for all available fields and `examples `__. diff --git a/doc/esp8266wifi/scan-examples.rst b/doc/esp8266wifi/scan-examples.rst index 4ad4777507..d05d258f72 100644 --- a/doc/esp8266wifi/scan-examples.rst +++ b/doc/esp8266wifi/scan-examples.rst @@ -1,5 +1,11 @@ :orphan: +IDE example +^^^^^^^^^^^ + +- For the currently installed Core, see Arduino IDE > *Examples* > *ESP8266WiFi* > *WiFiScan*. +- For the latest development version, see `WiFiScan.ino `__. + Scan ~~~~ diff --git a/doc/esp8266wifi/server-class.rst b/doc/esp8266wifi/server-class.rst index 8b8bc0e6c1..8bcce99944 100644 --- a/doc/esp8266wifi/server-class.rst +++ b/doc/esp8266wifi/server-class.rst @@ -18,6 +18,11 @@ For most use cases the basic WiFiServer class of the ESP8266WiFi library is suit Methods and properties described further down are specific to ESP8266. They are not covered in `Arduino WiFi library `__ documentation. Before they are fully documented please refer to information below. +begin(port) +~~~~~~~~~~~ + +Additionally to ``begin()`` without parameter and a constructor with parameter ``port``, ESP8266WiFi library has ``begin(uint16_t port)`` and a constructor without parameters. If port is not specified with constructor and ``begin`` without parameter is used, the server is started on port 23. + accept ~~~~~~ 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 787de49684..06e072adc6 100644 --- a/doc/esp8266wifi/station-class.rst +++ b/doc/esp8266wifi/station-class.rst @@ -7,6 +7,8 @@ The number of features provided by ESP8266 in the station mode is far more exten Description of station class has been broken down into four parts. First discusses methods to establish connection to an access point. Second provides methods to manage connection like e.g. ``reconnect`` or ``isConnected``. Third covers properties to obtain information about connection like MAC or IP address. Finally the fourth section provides alternate methods to connect like e.g. Wi-Fi Protected Setup (WPS). +An effort to unify such network device class accross several Arduino core implementations has been made. Recommandations are located at `Arduino-Networking-API `__ and tested with `NetApiHelpers `__. Esp8266 Arduino core's station class is also following these guidelines. + Table of Contents ----------------- @@ -97,8 +99,12 @@ config Disable `DHCP `__ client (Dynamic Host Configuration Protocol) and set the IP configuration of station interface to user defined arbitrary values. The interface will be a static IP configuration instead of values provided by DHCP. +Note that to reenable DHCP, all three parameters (local_ip, gateway and subnet) as IPv4 ``0U`` (= 0.0.0.0) must be passed back to config() and re-connecting is needed. + .. code:: cpp + WiFi.config(local_ip, gateway, subnet) (for Arduino API portability, discouraged as chosen defaults may not match the local network configuration) + WiFi.config(local_ip, gateway, subnet, dns1) WiFi.config(local_ip, gateway, subnet, dns1, dns2) Function will return ``true`` if configuration change is applied successfully. If configuration can not be applied, because e.g. module is not in station or station + soft access point mode, then ``false`` will be returned. @@ -116,6 +122,21 @@ The following IP configuration may be provided: (like e.g. *www.google.co.uk*) and translate them for us to IP addresses +For Arduino networking API compatibility, the ESP8266WiFi library supports IPv4-only additional versions of the ``config`` function: + +.. code:: cpp + + WiFi.config(local_ip) (for Arduino API portability, discouraged as chosen defaults may not match the local network configuration) + WiFi.config(local_ip, dns) (for Arduino API portability, discouraged as chosen defaults may not match the local network configuration) + WiFi.config(local_ip, dns, gateway) (for Arduino API portability, discouraged as chosen defaults may not match the local network configuration) + WiFi.config(local_ip, dns, gateway, subnet) + +Versions where some of ``dns``, ``gateway`` and ``subnet`` parameters are not specified use a default value. Default ``subnet`` is 255.255.255.0. Default ``gateway`` and ``dns`` are derived from ``local_ip`` by changing the last number to 1. It is discouraged to use these default values as they may not apply to every network configuration. + +Reminder : To reenable DHCP you can use ``WiFi.config(0U, 0U, 0U);``. + +**Warning: The default values for dns, gateway and subnet may not match your router's settings.** Also please note, that ``config(local_ip, gateway)`` is not supported and ``WiFi.config(local_ip, gateway, subnet)`` doesn't set the DNS server IP. + *Example code:* .. code:: cpp @@ -157,8 +178,7 @@ The following IP configuration may be provided: . Connected, IP address: 192.168.1.22 -Please note that station with static IP configuration usually connects to the network faster. In the above example it took about 500ms (one dot `.` displayed). This is because obtaining of IP configuration by DHCP client takes time and in this case this step is skipped. If you pass all three parameter as 0.0.0.0 (local_ip, gateway and subnet), it will re enable DHCP. You need to re-connect the device to get new IPs. - +Please note that station with static IP configuration usually connects to the network faster. In the above example it took about 500ms (one dot `.` displayed). This is because obtaining of IP configuration by DHCP client takes time and in this case this step is skipped. Reminder: If you pass all three parameters as 0.0.0.0 (local_ip, gateway and subnet), it will re enable DHCP. You need to re-connect the device to get new IPs. Manage Connection ~~~~~~~~~~~~~~~~~ @@ -453,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:* @@ -491,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/esp8266wifi/udp-class.rst b/doc/esp8266wifi/udp-class.rst index 5e02f7b3da..73a9086021 100644 --- a/doc/esp8266wifi/udp-class.rst +++ b/doc/esp8266wifi/udp-class.rst @@ -26,11 +26,11 @@ Multicast UDP .. code:: cpp - uint8_t beginMulticast (IPAddress interfaceAddr, IPAddress multicast, uint16_t port) + uint8_t beginMulticast (IPAddress multicast, uint16_t port) virtual int beginPacketMulticast (IPAddress multicastAddress, uint16_t port, IPAddress interfaceAddress, int ttl=1) IPAddress destinationIP () uint16_t localPort () -The ``WiFiUDP`` class supports sending and receiving multicast packets on STA interface. When sending a multicast packet, replace ``udp.beginPacket(addr, port)`` with ``udp.beginPacketMulticast(addr, port, WiFi.localIP())``. When listening to multicast packets, replace ``udp.begin(port)`` with ``udp.beginMulticast(WiFi.localIP(), multicast_ip_addr, port)``. You can use ``udp.destinationIP()`` to tell whether the packet received was sent to the multicast or unicast address. +The ``WiFiUDP`` class supports sending and receiving multicast packets on STA interface. When sending a multicast packet, replace ``udp.beginPacket(addr, port)`` with ``udp.beginPacketMulticast(addr, port, WiFi.localIP())``. When listening to multicast packets, replace ``udp.begin(port)`` with ``udp.beginMulticast(multicast_ip_addr, port)``. You can use ``udp.destinationIP()`` to tell whether the packet received was sent to the multicast or unicast address. For code samples please refer to separate section with `examples `__ dedicated specifically to the UDP Class. diff --git a/doc/faq/a01-espcomm_sync-failed.rst b/doc/faq/a01-espcomm_sync-failed.rst index df4d0aa177..d47a5d2eeb 100644 --- a/doc/faq/a01-espcomm_sync-failed.rst +++ b/doc/faq/a01-espcomm_sync-failed.rst @@ -9,7 +9,7 @@ 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 diff --git a/doc/faq/a02-my-esp-crashes.rst b/doc/faq/a02-my-esp-crashes.rst index 1fcc2cbaa8..ec6340e8c7 100644 --- a/doc/faq/a02-my-esp-crashes.rst +++ b/doc/faq/a02-my-esp-crashes.rst @@ -5,12 +5,13 @@ 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>`__ - `Watchdog <#watchdog>`__ - `Exception Decoder <#exception-decoder>`__ +- `Improving Exception Decoder Results <#improving-exception-decoder-results>`__ - `Other Common Causes for Crashes <#other-causes-for-crashes>`__ - `If at the Wall, Enter an Issue Report <#if-at-the-wall-enter-an-issue-report>`__ @@ -147,8 +148,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 @@ -236,6 +237,7 @@ If you don't have any code for troubleshooting, use the example below: void loop(){} + Enable the Out-Of-Memory (*OOM*) debug option (in the *Tools > Debug Level* menu), compile/flash/upload this code to your ESP (Ctrl+U) and start Serial Monitor (Ctrl+Shift+M). You should shortly see ESP restarting every couple @@ -270,31 +272,92 @@ Decoder `__ you can track down where the module is crashing whenever you see the stack trace dropped. The same procedure applies to crashes caused by exceptions. - Note: To decode the exact line of code where the application + Note, to decode the exact line of code where the application crashed, you need to use ESP Exception Decoder in context of sketch you have just loaded to the module for diagnosis. Decoder is not able to correctly decode the stack trace dropped by some other application not compiled and loaded from your Arduino IDE. +Improving Exception Decoder Results +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Due to the limited resources on the device, our default compiler optimizations +focus on creating the smallest code size (``.bin`` file). The GCC compiler's +option ``-Os`` contains the base set of optimizations used. This set is fine for +release but not ideal for debugging. + +Our view of a crash is often the `Stack Dump <../Troubleshooting/stack_dump.rst>`__ +which gets copy/pasted into an Exception Decoder. +For some situations, the optimizer doesn't write caller return addresses to the +stack. When we crash, the list of functions called is missing. And when the +crash occurs in a leaf function, there is seldom if ever any evidence of who +called. + +With the ``-Os`` option, functions called once are inlined into the calling +function. A chain of these functions can optimize down to the calling function. +When the crash occurs in one of these chain functions, the actual location in +the source code is no longer available. + +When you select ``Debug Optimization: Lite`` on the Arduino IDE Tools menu, it +turns off ``optimize-sibling-calls``. Turning off this optimization allows more +caller addresses to be written to the stack, improving the results from the +Exception Decoder. Without this option, the callers involved in the crash may be +missing from the Decoder results. Because of the limited stack space, there is +the remote possibility that removing this optimization could lead to more +frequent stack overflows. You only want to do this in a debug setting. This +option does not help the chained function issue. + +When you select ``Debug Optimization: Optimum``, you get an even more complete +stack trace. For example, chained function calls may show up. This selection +uses the compiler option ``-Og``. GCC considers this the ideal optimization for +the "edit-compile-debug cycle" ... "producing debuggable code." You can read the +specifics at `GCC's Optimize Options `__ + +When global optimization creates build size issues or stack overflow issues, +select ``Debug Optimization: None``, and use a targeted approach with +``#pragma GCC optimize("Og")`` at the module level. Or, if you want to use a +different set of optimizations, you can set optimizations through build options. +Read more at `Global Build Options `__. + +For non-Arduino IDE build platforms, you may need to research how to add build +options. Some build platforms already use ``-Og`` for debug builds. + +A crash in a leaf function may not leave the caller's address on the stack. +The return address can stay in a register for the duration of the call. +Resulting in a crash report identifying the crashing function without a +trace of who called. You can encourage the compiler to save the caller's +return address by adding an inline assembly trick +``__asm__ __volatile__("" ::: "a0", "memory");`` at the beginning of the +function's body. Or instead, for a debug build conditional option, use the +macro ``DEBUG_LEAF_FUNCTION()`` from ``#include ``. For compiler +toolchain 3.2.0 and above, the ``-Og`` option is an alternative solution. + +In some cases, adding ``#pragma GCC optimize("Og,no-ipa-pure-const")`` to a +module as well as using ``DEBUG_LEAF_FUNCTION()`` in a leaf function were +needed to display a complete call chain. Or use +``#pragma GCC optimize("Os,no-inline,no-optimize-sibling-calls,no-ipa-pure-const")`` +if you require optimization ``-Os``. + + Other Causes for Crashes ~~~~~~~~~~~~~~~~~~~~~~~~ Interrupt Service Routines - By default, all functions are compiled into flash, which means that the - cache may kick in for that code. However, the cache currently can't be used - during hardware interrupts. That means that, if you use a hardware ISR, such as - attachInterrupt(gpio, myISR, CHANGE) for a GPIO change, the ISR must have the - IRAM_ATTR attribute declared. Not only that, but the entire function tree + By default, all functions are compiled into flash, which means that the + cache may kick in for that code. However, the cache currently can't be used + during hardware interrupts. That means that, if you use a hardware ISR, such as + attachInterrupt(gpio, myISR, CHANGE) for a GPIO change, the ISR must have the + IRAM_ATTR attribute declared. Not only that, but the entire function tree called from the ISR must also have the IRAM_ATTR declared. Be aware that every function that has this attribute reduces available memory. - In addition, it is not possible to execute delay() or yield() from an ISR, + In addition, it is not possible to execute delay() or yield() from an ISR, or do blocking operations, or operations that disable the interrupts, e.g.: read a DHT. Finally, an ISR has very high restrictions on timing for the executed code, meaning - that executed code should not take longer than a very few microseconds. It is + that executed code should not take longer than a very few microseconds. It is considered best practice to set a flag within the ISR, and then from within the loop() check and clear that flag, and execute code. @@ -303,7 +366,7 @@ Asynchronous Callbacks than ISRs, but some restrictions still apply. It is not possible to execute delay() or yield() from an asynchronous callback. Timing is not as tight as an ISR, but it should remain below a few milliseconds. This - is a guideline. The hard timing requirements depend on the WiFi configuration and + is a guideline. The hard timing requirements depend on the WiFi configuration and amount of traffic. In general, the CPU must not be hogged by the user code, as the longer it is away from servicing the WiFi stack, the more likely that memory corruption can happen. @@ -311,8 +374,8 @@ Asynchronous Callbacks Memory, memory, memory Running out of heap is the **most common cause for crashes**. Because the build process for the ESP leaves out exceptions (they use memory), memory allocations that fail will do - so silently. A typical example is when setting or concatenating a large String. If - allocation has failed internally, then the internal string copy can corrupt data, and + so silently. A typical example is when setting or concatenating a large String. If + allocation has failed internally, then the internal string copy can corrupt data, and the ESP will crash. In addition, doing many String concatenations in sequence, e.g.: using operator+() @@ -348,9 +411,9 @@ Memory, memory, memory * If you use std libs like std::vector, make sure to call its ::reserve() method before filling it. This allows allocating only once, which reduces mem fragmentation, and makes sure that there are no empty unused slots left over in the container at the end. Stack -   The amount of stack in the ESP is tiny at only 4KB. For normal development in large systems, it +   The amount of stack in the ESP is tiny at only 4KB. For normal development in large systems, it is good practice to use and abuse the stack, because it is faster for allocation/deallocation, the scope of the object is well defined, and deallocation automatically happens in reverse order as allocation, which means no mem fragmentation. However, with the tiny amount of stack available in the ESP, that practice is not really viable, at least not for big objects. - + * Large objects that have internally managed memory, such as String, std::string, std::vector, etc, are ok on the stack, because they internally allocate their buffers on the heap. * Large arrays on the stack, such as uint8_t buffer[2048] should be avoided on the stack and should be dynamically allocated instead (consider smart pointers). * Objects that have large data members, such as large arrays, should also be avoided on the stack, and should be dynamically allocated (consider smart pointers). @@ -392,7 +455,7 @@ or `esp8266 / Arduino `__ core, types and versions of O/S, you need to provide exact information on what your application is about. Only then, people willing to look into your issue may be able to compare it to a configuration they are familiar with. -If you are lucky, they may even attempt to reproduce your issue on their +If you are lucky, they may even attempt to reproduce your issue on their own equipment! This will be far more difficult if you provide only vague details, so somebody would need to ask you to find out what is really happening. diff --git a/doc/faq/a03-library-does-not-work.rst b/doc/faq/a03-library-does-not-work.rst index 8b9b2d6910..6597c1a1dc 100644 --- a/doc/faq/a03-library-does-not-work.rst +++ b/doc/faq/a03-library-does-not-work.rst @@ -7,7 +7,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/a06-global-build-options.rst b/doc/faq/a06-global-build-options.rst new file mode 100644 index 0000000000..3e86b88a58 --- /dev/null +++ b/doc/faq/a06-global-build-options.rst @@ -0,0 +1,326 @@ +:orphan: + +How to specify global build defines and options +=============================================== + +To create globally usable macro definitions for a Sketch, create a file +with a name based on your Sketch’s file name followed by ``.globals.h`` +in the Sketch folder. For example, if the main Sketch file is named +``LowWatermark.ino``, its global ``.h`` file would be +``LowWatermark.ino.globals.h``. This file will be implicitly included +with every module built for your Sketch. Do not directly include it in +any of your sketch files or in any other source files. There is no need +to create empty/dummy files, when not used. + +This global ``.h`` also supports embedding compiler command-line options +in a unique “C” block comment. Compiler options are placed in a “C” +block comment starting with ``/*@create-file:build.opt@``. This +signature line must be alone on a single line. The block comment ending +``*/`` should also be alone on a single line. In between, place your +compiler command-line options just as you would have for the GCC @file +command option. + +Actions taken in processing comment block to create ``build.opt`` + +- for each line, white space is trimmed +- blank lines are skipped +- lines starting with ``*``, ``//``, or ``#`` are skipped +- the remaining results are written to build tree\ ``/core/build.opt`` +- multiple ``/*@create-file:build.opt@`` ``*/`` comment blocks are not + allowed +- ``build.opt`` is finished with a ``-include ...`` command, which + references the global .h its contents were extracted from. + +Example Sketch: ``LowWatermark.ino`` + +.. code:: cpp + + #include // has prototype for umm_free_heap_size_min() + + void setup() { + Serial.begin(115200); + delay(200); + #ifdef MYTITLE1 + Serial.printf("\r\n" MYTITLE1 MYTITLE2 "\r\n"); + #else + Serial.println("ERROR: MYTITLE1 not present"); + #endif + Serial.printf("Heap Low Watermark %u\r\n", umm_free_heap_size_min()); + } + + void loop() {} + +Global ``.h`` file: ``LowWatermark.ino.globals.h`` + +.. code:: cpp + + /*@create-file:build.opt@ + // An embedded build.opt file using a "C" block comment. The starting signature + // must be on a line by itself. The closing block comment pattern should be on a + // line by itself. Each line within the block comment will be space trimmed and + // written to build.opt, skipping blank lines and lines starting with '//', '*' + // or '#'. + + * this line is ignored + # this line is ignored + -DMYTITLE1="\"Running on \"" + -O3 + //-fanalyzer + -DUMM_STATS_FULL=1 + */ + + #ifndef LOWWATERMARK_INO_GLOBALS_H + #define LOWWATERMARK_INO_GLOBALS_H + + #if !defined(__ASSEMBLER__) + // Defines kept away from assembler modules + // i.e. Defines for .cpp, .ino, .c ... modules + #endif + + #if defined(__cplusplus) + // Defines kept private to .cpp and .ino modules + //#pragma message("__cplusplus has been seen") + #define MYTITLE2 "Empty" + #endif + + #if !defined(__cplusplus) && !defined(__ASSEMBLER__) + // Defines kept private to .c modules + #define MYTITLE2 "Full" + #endif + + #if defined(__ASSEMBLER__) + // Defines kept private to assembler modules + #endif + + #endif + +Separate production and debug build options +=========================================== + +If your production and debug build option requirements are different, +adding ``mkbuildoptglobals.extra_flags={build.debug_port}`` to +``platform.local.txt`` will create separate build option groups for +debugging and production. For the production build option group, the “C” +block comment starts with ``/*@create-file:build.opt@``, as previously +defined. For the debugging group, the new “C” block comment starts with +``/*@create-file:build.opt:debug@``. You make your group selection +through “Arduino->Tools->Debug port” by selecting or disabling the +“Debug port.” + +Options common to both debug and production builds must be included in +both groups. Neither of the groups is required. You may also omit either +or both. + +Reminder with this change, any old “sketch” with only a “C” block +comment starting with ``/*@create-file:build.opt@`` would not use a +``build.opt`` file for the debug case. Update old sketches as needed. + +Updated Global ``.h`` file: ``LowWatermark.ino.globals.h`` + +.. code:: cpp + + /*@create-file:build.opt:debug@ + // Debug build options + -DMYTITLE1="\"Running on \"" + -DUMM_STATS_FULL=1 + + //-fanalyzer + + // Removing the optimization for "sibling and tail recursive calls" may fill + // in some gaps in the stack decoder report. Preserves the stack frames + // created at each level as you call down to the next. + -fno-optimize-sibling-calls + */ + + /*@create-file:build.opt@ + // Production build options + -DMYTITLE1="\"Running on \"" + -DUMM_STATS_FULL=1 + -O3 + */ + + #ifndef LOWWATERMARK_INO_GLOBALS_H + #define LOWWATERMARK_INO_GLOBALS_H + + #if defined(__cplusplus) + #define MYTITLE2 "Empty" + #endif + + #if !defined(__cplusplus) && !defined(__ASSEMBLER__) + #define MYTITLE2 "Full" + #endif + + #ifdef DEBUG_ESP_PORT + // Global Debug defines + // ... + #else + // Global Production defines + // ... + #endif + + #endif + +Aggressively cache compiled core +================================ + +This feature appeared with the release of Arduino IDE 1.8.2. The feature +“Aggressively Cache Compiled core” refers to sharing a single copy of +``core.a`` across all Arduino IDE Sketch windows. This feature is on by +default. ``core.a`` is an archive file containing the compiled objects +of ``./core/esp8266/*``. Created after your 1ST successful compilation. +All other open sketch builds use this shared file. When you close all +Arduino IDE windows, the core archive file is deleted. + +This feature is not compatible with using global defines or compiler +command-line options. Without mediation, bad builds could result, when +left enabled. When ``#define`` changes require rebuilding ``core.a`` and +multiple Sketches are open, they can no longer reliably share one cached +``core.a``. In a simple case: The 1st Sketch to be built has its version +of ``core.a`` cached. Other sketches will use this cached version for +their builds. + +There are two solutions to this issue: + +1. Do nothing, and rely on aggressive cache workaround built into the + script. +2. Turn off the “Aggressively Cache Compiled core” feature, by setting + ``compiler.cache_core=false``. + +Using “compiler.cache_core=false” +--------------------------------- + +There are two ways to turn off the “Aggressively Cache Compiled core” +feature: This can be done with the Arduino IDE command-line or a text +editor. + +Using the Arduino IDE command-line from a system command line, enter the +following: + +:: + + arduino --pref compiler.cache_core=false --save-prefs + +For the text editor, you need to find the location of +``preferences.txt``. From the Arduino IDE, go to *File->Preferences*. +Make note of the path to ``prefereces.txt``. You *cannot* edit the file +while the Arduino IDE is running. Close all Arduino IDE windows and edit +the file ``preferences.txt``. Change ``compiler.cache_core=true`` to +``compiler.cache_core=false`` and save. Then each sketch will maintain +its *own* copy of ``core.a`` built with the customization expressed by +their respective ``build.opt`` file. + +The “workaround” +---------------- + +When the “Aggressively Cache Compiled core” feature is enabled and the +global define file is detected, a workaround will turn on and stay on. +When you switch between Sketch windows, core will be recompiled and the +cache updated. The workaround logic is reset when Arduino IDE is +completely shutdown and restarted. + +The workaround is not perfect. These issues may be of concern: + +1. Dirty temp space. Arduino build cache files left over from a previous + run or boot. +2. Arduino command-line options: + + - override default preferences.txt file. + - override a preference, specifically ``compiler.cache_core``. + +3. Multiple versions of the Arduino IDE running + +**Dirty temp space** + +A minor concern, the workaround is always on. Not an issue for build +accuracy, but ``core.a`` maybe rebuild more often than necessary. + +Some operating systems are better at cleaning up their temp space than +others at reboot after a crash. At least for Windows®, you may need to +manually delete the Arduino temp files and directories after a crash. +Otherwise, the workaround logic may be left on. There is no harm in the +workaround being stuck on, the build will be correct; however, the core +files will occasionally be recompiled when not needed. + +For some Windows® systems the temp directory can be found near +``C:\Users\\AppData\Local\Temp\arduino*``. Note ``AppData`` is +a hidden directory. For help with this do an Internet search on +``windows disk cleanup``. Or, type ``disk cleanup`` in the Windows® +taskbar search box. + +With Linux, this problem could occur after an Arduino IDE crash. The +problem would be cleared after a reboot. Or you can manually cleanup the +``/tmp/`` directory before restarting the Arduino IDE. + +**Arduino command-line option overrides** + +If you are building with ``compiler.cache_core=true`` no action is +needed. If ``false`` the script would benefit by knowing that. + +When using either of these two command-line options: + +:: + + ./arduino --preferences-file other-preferences.txt + ./arduino --pref compiler.cache_core=false + +Hints for discovering the value of ``compiler.cache_core``, can be +provided by specifying ``mkbuildoptglobals.extra_flags=...`` in +``platform.local.txt``. + +Examples of hints: + +:: + + mkbuildoptglobals.extra_flags=--preferences_sketch # assume file preferences.txt in the sketch folder + mkbuildoptglobals.extra_flags=--preferences_sketch "pref.txt" # is relative to the sketch folder + mkbuildoptglobals.extra_flags=--no_cache_core + mkbuildoptglobals.extra_flags=--cache_core + mkbuildoptglobals.extra_flags=--preferences_file "other-preferences.txt" # relative to IDE or full path + +If required, remember to quote file or file paths. + +**Multiple versions of the Arduino IDE running** + +You can run multiple Arduino IDE windows as long as you run one version +of the Arduino IDE at a time. When testing different versions, +completely exit one before starting the next version. For example, +Arduino IDE 1.8.19 and Arduino IDE 2.0 work with different temp and +build paths. With this combination, the workaround logic sometimes fails +to enable. + +At the time of this writing, when Arduino IDE 2.0 rc5 exits, it leaves +the temp space dirty. This keeps the workaround active the next time the +IDE is started. If this is an issue, manually delete the temp files. + +Custom build environments +========================= + +Some custom build environments may have already addressed this issue by +other means. If you have a custom build environment that does not +require this feature and would like to turn it off, you can add the +following lines to the ``platform.local.txt`` used in your build +environment: + +:: + + recipe.hooks.prebuild.2.pattern= + build.opt.flags= + +Other build confusion +===================== + +1. Renaming a file does not change the last modified timestamp, possibly + causing issues when adding a file by renaming and rebuilding. A good + example of this problem would be to have then fixed a typo in file + name ``LowWatermark.ino.globals.h``. You need to touch (update + timestamp) the file so a “rebuild all” is performed. + +2. When a ``.h`` file is renamed in the sketch folder, a copy of the old + file remains in the build sketch folder. This can create confusion if + you missed an edit in updating an ``#include`` in one or more of your + modules. That module will continue to use the stale version of the + ``.h`` until you restart the IDE or other major changes that would + cause the IDE to delete and recopy the contents from the source + Sketch directory. Changes on the IDE Tools board settings may cause a + complete rebuild, clearing the problem. This may be the culprit for + “What! It built fine last night!” diff --git a/doc/faq/readme.rst b/doc/faq/readme.rst index 53e830358b..cfd65eca90 100644 --- a/doc/faq/readme.rst +++ b/doc/faq/readme.rst @@ -187,7 +187,16 @@ regular API. Read more at `former WiFi persistent mode <../esp8266wifi/generic-class.rst#persistent>`__. -How to resolve "undefined reference to ``flashinit`'" error ? +How to resolve "undefined reference to ``flashinit``" error ? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Please read `flash layout <../filesystem.rst>`__ documentation entry. + +How to specify global build defines and options? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +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 `__. diff --git a/doc/filesystem.rst b/doc/filesystem.rst index a9038fe3b4..1dca7dc707 100644 --- a/doc/filesystem.rst +++ b/doc/filesystem.rst @@ -93,7 +93,7 @@ directory support, but has higher filesystem and per-file overhead (4K minimum vs. SPIFFS' 256 byte minimum file allocation unit). They share a compatible API but have incompatible on-flash -implementations, so it is important to choose one or the per project +implementations, so it is important to choose one or the other per project as attempting to mount a SPIFFS volume under LittleFS may result in a format operation and definitely will not preserve any files, and vice-versa. @@ -228,10 +228,16 @@ use esptool.py. *ESP8266LittleFS* is the equivalent tool for LittleFS. -- Download the 2.6.0 or later version of the tool: https://github.com/earlephilhower/arduino-esp8266littlefs-plugin/releases +For Arduino IDE 1.x: +- Download the latest plugin from: https://github.com/earlephilhower/arduino-esp8266littlefs-plugin/releases - Install as above - To upload a LittleFS filesystem use Tools > ESP8266 LittleFS Data Upload +For Arduino IDE 2.x: +- Download the latest plugin from: https://github.com/earlephilhower/arduino-littlefs-upload/releases +- Follow the manual installation instructions in: https://github.com/earlephilhower/arduino-littlefs-upload/blob/main/README.md +- To upload a LittleFS filesystem use `Ctrl`+`Shift`+`P` and then select the `Upload LittleFS to Pico/ESP8266` item + File system object (SPIFFS/LittleFS/SD/SDFS) -------------------------------------------- diff --git a/doc/gdb.rst b/doc/gdb.rst index 53e9f07fbb..79e221e844 100644 --- a/doc/gdb.rst +++ b/doc/gdb.rst @@ -380,4 +380,33 @@ breakpoint) command in GDB while debugging instead of the more common ``break`` command, since ``thb`` will remove the breakpoint once it is reached automatically and save you some trouble. +Because of the single hardware breakpoint limitation, you must pay careful +attention to the output from ``gdb`` when you set a breakpoint. If your +breakpoint expression matches multiple locations, as in this example: +.. code:: bash + + (gdb) break loop + Breakpoint 1 at 0x40202c84: loop. (2 locations) + +Then you will be unable to ``continue``: + +.. code:: bash + + (gdb) cont + Continuing. + Note: automatically using hardware breakpoints for read-only addresses. + Warning: + Cannot insert hardware breakpoint 1. + Could not insert hardware breakpoints: + You may have requested too many hardware breakpoints/watchpoints. + +You can resolve this situation by deleting the previous breakpoint and +using a more specific breakpoint expression: + +.. code:: bash + + (gdb) delete + Delete all breakpoints? (y or n) y + (gdb) break mysketch.ino:loop + Breakpoint 2 at 0x40202c84: file .../mysketch.ino, line 113. diff --git a/doc/ideoptions.rst b/doc/ideoptions.rst index f790d02fc0..25fd08defa 100644 --- a/doc/ideoptions.rst +++ b/doc/ideoptions.rst @@ -127,7 +127,31 @@ There are a number of options. - The last (``NoAssert - NDEBUG``) is even quieter than the first (some internal guards are skipped to save more flash). - The other ones may be used when asked by a maintainer or if you are a - developper trying to debug some issues. + developer trying to debug some issues. + +Debug Optimization +~~~~~~~~~~~~~~~~~~ + +Due to the limited resources on the device, our default compiler optimizations +focus on creating the smallest code size (``.bin`` file). That is fine for +release but not ideal for debugging. + +``Debug Optimization`` use to improve Exception Decoder results. + +- ``Lite`` impact on code size uses ``-fno-optimize-sibling-calls`` to alter + the ``-Os`` compiler option to place more caller addresses on the Stack. +- ``Optimum`` offers better quality stack content for the Exception Decoder at + the expense of a larger code size. It uses the ``-Og`` compiler option, which + turns off optimizations that can make debugging difficult while keeping + others. +- ``None`` no changes for debugging continue using ``-Os``. + +Take note some sketches may start working after changing the optimization. Or +fail less often. And it is also possible (not likely) that source code that +was working with ``-Os`` may break with ``-Og``. + +For more topic depth, read `Improving Exception Decoder Results `__ + lwIP variant ~~~~~~~~~~~~ @@ -219,13 +243,31 @@ Erase Flash - ``All Flash``: WiFi settings and Filesystems are erased. -Espressif Firmware +NONOS SDK Version ~~~~~~~~~~~~~~~~~~ -There are a number of available espressif firmwares. The first / default -choice is fine. Only try with others after reading on the issue tracker -that something has to be tried with them. Note that Espressif obsoleted -all of them at the time of writing. +Our Core is based on [Espressif NONOS SDK](https://github.com/espressif/ESP8266_NONOS_SDK). + +- **2.2.1+100 (190703)** (default) +- 2.2.1+119 (191122) +- 2.2.1+113 (191105) +- 2.2.1+111 (191024) +- 2.2.1+61 (190313) +- 2.2.1 (legacy) +- 3.0.5 (experimental) + +See our issue tracker in regards to default version selection. + +* `#6724 (comment) `__ +* `#6826 `__ + +Notice that 3.x.x is provided **as-is** and remains **experimental**. + +Floating Point operations +~~~~~~~~~~~~~~~~~~~~~~~~~ + +- ``in IROM``: This provides more free space in IRAM but disallows using floating operations inside ISRs. +- ``allowed in ISR``: Floats can be used in ISRs, cost is ~1KB IRAM when floats are used. SSL Support ~~~~~~~~~~~ diff --git a/doc/installing.rst b/doc/installing.rst index 8f04b90f05..34f76d7365 100644 --- a/doc/installing.rst +++ b/doc/installing.rst @@ -9,10 +9,9 @@ This is the suggested installation method for end users. Prerequisites ~~~~~~~~~~~~~ -- Arduino 1.6.8, get it from `Arduino - website `__. - Internet connection -- Python 3 interpreter (Mac/Linux only, Windows installation supplies its own) +- Arduino IDE 1.x or 2.x (https://www.arduino.cc/en/software) +- (macOS/Linux only) Python ≥3.7 (https://python.org) Instructions ~~~~~~~~~~~~ @@ -33,6 +32,7 @@ For more information on the Arduino Board Manager, see: - https://www.arduino.cc/en/guide/cores + Using git version ----------------- @@ -42,12 +42,12 @@ developers. Prerequisites ~~~~~~~~~~~~~ -- Arduino 1.6.8 (or newer, current working version is 1.8.5) -- git -- Python 3.x (https://python.org) -- terminal, console, or command prompt (depending on your OS) - Internet connection -- Uninstalling any core version installed via Board Manager +- Arduino IDE 1.x or 2.x (https://www.arduino.cc/en/software) +- git (https://git-scm.com) +- Python ≥3.7 (https://python.org) +- terminal, console, or command prompt (depending on your OS) +- **Uninstalling any core version installed via Board Manager** Instructions - Windows 10 ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -59,7 +59,7 @@ Instructions - Windows 10 - Install git for Windows (if not already; see https://git-scm.com/download/win) - Open a command prompt (cmd) and go to Arduino default directory. This is typically the - *sketchbook* directory (usually ``C:\users\{username}\Documents\Arduino`` where the environment variable ``%USERPROFILE%`` usually contains ``C:\users\{username}``) + *sketchbook* directory (usually ``C:\Users\{username}\Documents\Arduino`` where the environment variable ``%USERPROFILE%`` usually contains ``C:\Users\{username}``) - Clone this repository into hardware/esp8266com/esp8266 directory. @@ -101,14 +101,15 @@ Instructions - Windows 10 --- boards.txt --- LICENSE -- Initialize the submodules +- Initialize submodules to fetch external libraries .. code:: bash cd %USERPROFILE%\Documents\Arduino\hardware\esp8266com\esp8266 git submodule update --init - If error messages about missing files related to ``SoftwareSerial`` are encountered during the build process, it should be because this step was missed and is required. + Not doing this step would cause build failure when attempting to include ``SoftwareSerial.h``, ``Ethernet.h``, etc. + See our `.gitmodules file `__ for the full list. - Download binary tools @@ -181,14 +182,16 @@ Instructions - Other OS --- boards.txt --- LICENSE -- Initialize the submodules +- Initialize submodules to fetch external libraries .. code:: bash cd esp8266 git submodule update --init - - If error messages about missing files related to ``SoftwareSerial`` are encountered during the build process, it should be because this step was missed and is required. + + + Not doing this step would cause build failure when attempting to include ``SoftwareSerial.h``, ``Ethernet.h``, etc. + See our `.gitmodules file `__ for the full list. - Download binary tools @@ -197,9 +200,10 @@ Instructions - Other OS cd tools python3 get.py - If you get an error message stating that python3 is not found, you will need to install it (most modern UNIX-like OSes provide Python 3 as - part of the default install). To install you will need to use ``sudo yum install python3``, ``sudo apt install python3``, or ``brew install python3`` - as appropriate. On the Mac you may get an error message like: + + If you get an error message stating that python3 is not found, you will need to install it (most modern UNIX-like OSes provide Python 3 as + part of the default install). To install you will need to use ``sudo yum install python3``, ``sudo apt install python3``, or ``brew install python3`` + as appropriate. On the Mac you may get an error message like: .. code:: bash @@ -214,7 +218,8 @@ Instructions - Other OS self._sslobj.do_handshake() ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1056) - This is because Homebrew on the Mac does not always install the required SSL certificates by default. Install them manually (adjust the Python 3.7 as needed) with: + + This is because Homebrew on the Mac does not always install the required SSL certificates by default. Install them manually (adjust the Python 3.7 as needed) with: .. code:: bash @@ -231,6 +236,44 @@ Instructions - Other OS git status git pull +Maintaining +~~~~~~~~~~~ + +To keep up with the development branch + +.. code:: bash + + git switch --recurse-submodules --discard-changes master + git pull --recurse-submodules + cd tools + python3 get.py + +Pull requests +~~~~~~~~~~~~~ + +To test not yet merged Pull Request, first you have to find its ID number. This is the sequence of digits right after the pull request title. + +Open terminal and cd into the directory where the repository was previously cloned. For example, 12345 is the Pull Request ID + +.. code:: bash + + git fetch origin pull/12345/head + git switch --detach --recurse-submodules --discard-changes FETCH_HEAD + +When Pull Request updates packaged tools, make sure to also fetch their latest versions. + +.. code:: bash + + cd tools + python3 get.py + +To go back to using the development branch + +.. code:: bash + + git switch --recurse-submodules --discard-changes master + git pull --recurse-submodules + Using PlatformIO ---------------- @@ -245,5 +288,6 @@ BeagleBone, CubieBoard). - `PlatformIO IDE `__ - `PlatformIO Core `__ (command line tool) - `Advanced usage `__ - custom settings, uploading to LittleFS, Over-the-Air (OTA), staging version +- `Using Arduino Framework Staging Version `__ - install development version of the Core - `Integration with Cloud and Standalone IDEs `__ - Cloud9, Codeanywhere, Eclipse Che (Codenvy), Atom, CLion, Eclipse, Emacs, NetBeans, Qt Creator, Sublime Text, VIM, Visual Studio, and VSCode - `Project Examples `__ diff --git a/doc/mmu.rst b/doc/mmu.rst index 9c3ec48acb..27ea4d4d7d 100644 --- a/doc/mmu.rst +++ b/doc/mmu.rst @@ -76,19 +76,28 @@ The Arduino IDE Tools menu option, ``MMU`` has the following selections: MMU related build defines and possible values. These values change as indicated with the menu options above: -+-------------------------+--------------+--------------+------------------------------------+------------------------------+ -| ``#define`` | balanced | IRAM | shared (IRAM and Heap) | not shared (IRAM and Heap) | -+=========================+==============+==============+====================================+==============================+ -| ``MMU_IRAM_SIZE`` | ``0x8000`` | ``0xC000`` | ``0xC000`` | ``0x8000`` | -+-------------------------+--------------+--------------+------------------------------------+------------------------------+ -| ``MMU_ICACHE_SIZE`` | ``0x8000`` | ``0x4000`` | ``0x4000`` | ``0x4000`` | -+-------------------------+--------------+--------------+------------------------------------+------------------------------+ -| ``MMU_IRAM_HEAP`` | -- | -- | defined, enables\ ``umm_malloc`` | -- | -+-------------------------+--------------+--------------+------------------------------------+------------------------------+ -| ``MMU_SEC_HEAP`` | -- | \*\* | \*\* | ``0x40108000`` | -+-------------------------+--------------+--------------+------------------------------------+------------------------------+ -| ``MMU_SEC_HEAP_SIZE`` | -- | \*\* | \*\* | ``0x4000`` | -+-------------------------+--------------+--------------+------------------------------------+------------------------------+ ++-------------+------------+------------+-------------+-------------+ +| ``#define`` | balanced | IRAM | shared | not shared | +| | | | (IRAM and | (IRAM and | +| | | | Heap) | Heap) | ++=============+============+============+=============+=============+ +| ``MMU_ | ``0x8000`` | ``0xC000`` | ``0xC000`` | ``0x8000`` | +| IRAM_SIZE`` | | | | | ++-------------+------------+------------+-------------+-------------+ +| ``MMU_IC | ``0x8000`` | ``0x4000`` | ``0x4000`` | ``0x4000`` | +| ACHE_SIZE`` | | | | | ++-------------+------------+------------+-------------+-------------+ +| ``MMU_ | – | – | defined, | – | +| IRAM_HEAP`` | | | e | | +| | | | nables\ ``u | | +| | | | mm_malloc`` | | ++-------------+------------+------------+-------------+-------------+ +| ``MMU | – | \*\* | \*\* | ``0 | +| _SEC_HEAP`` | | | | x40108000`` | ++-------------+------------+------------+-------------+-------------+ +| ``MMU_SEC_ | – | \*\* | \*\* | ``0x4000`` | +| HEAP_SIZE`` | | | | | ++-------------+------------+------------+-------------+-------------+ \*\* This define is to an inline function that calculates the value, based on unused code space, requires ``#include ``. @@ -112,8 +121,8 @@ The Arduino IDE Tools menu option, ``Non-32-Bit Access`` has the following selec option ``16KB cache + 48KB IRAM and 2nd Heap (shared)``. IRAM, unlike DRAM, must be accessed as aligned full 32-bit words, no -byte or short access. The pgm\_read macros are an option; however, the -store operation remains an issue. For a block copy, ets\_memcpy appears +byte or short access. The pgm_read macros are an option; however, the +store operation remains an issue. For a block copy, ets_memcpy appears to work well as long as the byte count is rounded up to be evenly divided by 4, and source and destination addresses are 4 bytes aligned. @@ -125,6 +134,11 @@ over-optimization. To get a sense of how memory access time is effected, see examples ``MMU48K`` and ``irammem`` in ``ESP8266``. +NON-OS SDK v3.0.0 and above have builtin support for Non-32-Bit Access. +Selecting ``Byte/Word access to IRAM/PROGMEM`` will override the builtin +version with ours. However, there is no known reason to do this other +than debugging. + Miscellaneous ------------- @@ -133,41 +147,43 @@ For calls to ``umm_malloc`` with interrupts disabled. - ``malloc`` will always allocate from the ``DRAM`` heap when called with interrupts disabled. -- ``realloc`` with a NULL pointer will use ``malloc`` and return a - ``DRAM`` heap allocation. Note, calling ``realloc`` with interrupts - disabled is **not** officially supported. You are on your own if you - do this. + + - ``realloc`` with a NULL pointer will use ``malloc`` and return a + ``DRAM`` heap allocation. Note, calling ``realloc`` with + interrupts disabled is **not** officially supported. You are on + your own if you do this. + - If you must use IRAM memory in your ISR, allocate the memory in your init code. To reduce the time spent in the ISR, avoid non32-bit access that would trigger the exception handler. For short or byte access, consider using the inline functions described in section - "Performance Functions" below. + “Performance Functions” below. How to Select Heap ~~~~~~~~~~~~~~~~~~ The ``MMU`` selection ``16KB cache + 48KB IRAM and 2nd Heap (shared)`` allows you to use the standard heap API function calls (``malloc``, -``calloc``, ``free``, ... ). to allocate memory from DRAM or IRAM. This +``calloc``, ``free``, … ). to allocate memory from DRAM or IRAM. This selection can be made by instantiating the class ``HeapSelectIram`` or ``HeapSelectDram``. The usage is similar to that of the ``InterruptLock`` class. The default/initial heap source is DRAM. The class is in ``umm_malloc/umm_heap_select.h``. -:: - - ... - char *bufferDram; - bufferDram = (char *)malloc(33); - char *bufferIram; - { - HeapSelectIram ephemeral; - bufferIram = (char *)malloc(33); - } - ... - free(bufferIram); - free(bufferDram); - ... +.. code:: cpp + + ... + char *bufferDram; + bufferDram = (char *)malloc(33); + char *bufferIram; + { + HeapSelectIram ephemeral; + bufferIram = (char *)malloc(33); + } + ... + free(bufferIram); + free(bufferDram); + ... ``free`` will always return memory to the correct heap. There is no need for tracking and selecting before freeing. @@ -182,8 +198,9 @@ Classes: - ``umm_get_current_heap_id()`` - ``umm_set_heap_by_id( ID value )`` - Possible ID values -- ``UMM_HEAP_DRAM`` -- ``UMM_HEAP_IRAM`` + + - ``UMM_HEAP_DRAM`` + - ``UMM_HEAP_IRAM`` Also, an alternate stack select method API is available. This is not as easy as the class method; however, for some small set of cases, it may @@ -201,9 +218,9 @@ a pointer: .. code:: cpp - bool mmu_is_iram(const void *addr); - bool mmu_is_dram(const void *addr); - bool mmu_is_icache(const void *addr); + bool mmu_is_iram(const void *addr); + bool mmu_is_dram(const void *addr); + bool mmu_is_icache(const void *addr); Performance Functions ~~~~~~~~~~~~~~~~~~~~~ @@ -213,23 +230,23 @@ exception handler reducing execution time and stack use, it comes at the cost of increased code size. These are an alternative to the ``pgm_read`` macros for reading from -IRAM. When compiled with 'Debug Level: core' range checks are performed +IRAM. When compiled with ‘Debug Level: core’ range checks are performed on the pointer value to make sure you are reading from the address range of IRAM, DRAM, or ICACHE. .. code:: cpp - uint8_t mmu_get_uint8(const void *p8); - uint16_t mmu_get_uint16(const uint16_t *p16); - int16_t mmu_get_int16(const int16_t *p16); + uint8_t mmu_get_uint8(const void *p8); + uint16_t mmu_get_uint16(const uint16_t *p16); + int16_t mmu_get_int16(const int16_t *p16); While these functions are intended for writing to IRAM, they will work -with DRAM. When compiled with 'Debug Level: core', range checks are +with DRAM. When compiled with ‘Debug Level: core’, range checks are performed on the pointer value to make sure you are writing to the address range of IRAM or DRAM. .. code:: cpp - 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); + 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 53ce8c2707..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: @@ -501,7 +501,7 @@ In case OTA update fails dead after entering modifications in your sketch, you c HTTP Server ----------- -``ESPhttpUpdate`` class can check for updates and download a binary file from HTTP web server. It is possible to download updates from every IP or domain address on the network or Internet. +``ESP8266HTTPUpdate`` class can check for updates and download a binary file from HTTP web server. It is possible to download updates from every IP or domain address on the network or Internet. Note that by default this class closes all other connections except the one used by the update, this is because the update method blocks. This means that if there's another application receiving data then TCP packets will build up in the buffer leading to out of memory errors causing the OTA update to fail. There's also a limited number of receive buffers available and all may be used up by other applications. @@ -523,6 +523,8 @@ Simple updater downloads the file every time the function is called. .. code:: cpp + #include + WiFiClient client; ESPhttpUpdate.update(client, "192.168.0.2", 80, "/arduino.bin"); @@ -535,6 +537,8 @@ The server-side script can respond as follows: - response code 200, and send the .. code:: cpp + #include + WiFiClient client; t_httpUpdate_return ret = ESPhttpUpdate.update(client, "192.168.0.2", 80, "/esp/update/arduino.php", "optional current version string here"); switch(ret) { @@ -588,34 +592,42 @@ With this information the script now can check if an update is needed. It is als "DOOR-7-g14f53a19", - "18:FE:AA:AA:AA:BB" => "TEMP-1.0.0" - ); - - if(!isset($db[$_SERVER['x-ESP8266-STA-MAC']])) { - header($_SERVER["SERVER_PROTOCOL"].' 500 ESP MAC not configured for updates', true, 500); + + $db_string = '{ + "18:FE:AA:AA:AA:AA": {"file": "DOOR-7-g14f53a19.bin", "version": 1}, + "18:FE:AA:AA:AA:BB": {"file": "TEMP-1.0.0".bin", "version": 1}}'; + // $db_string = file_get_contents("arduino-db.json"); + $db = json_decode($db_string, true); + $mode = $headers['x-ESP8266-mode']; + $mac = $headers['x-ESP8266-STA-MAC']; + + if (!isset($db[$mac])) { + header($_SERVER["SERVER_PROTOCOL"].' 404 ESP MAC not configured for updates', true, 404); + echo "MAC ".$mac." not configured for updates\n"; + exit(); } - - $localBinary = "./bin/".$db[$_SERVER['x-ESP8266-STA-MAC']].".bin"; - - // Check if version has been set and does not match, if not, check if - // MD5 hash between local binary and ESP8266 binary do not match if not. - // then no update has been found. - if((!check_header('x-ESP8266-sdk-version') && $db[$_SERVER['x-ESP8266-STA-MAC']] != $_SERVER['x-ESP8266-version']) - || $_SERVER["x-ESP8266-sketch-md5"] != md5_file($localBinary)) { - sendFile($localBinary); + + $localBinary = $db[$mac]['file']; + $localVersion = $db[$mac]['version']; + + if (!is_readable($localBinary)) { + header($_SERVER["SERVER_PROTOCOL"].' 404 File not found', true, 404); + echo "File ".$localBinary." not found\n"; + exit(); + } + + if ($mode == 'sketch') { + // Check if version has been set and does not match, if not, check if + // MD5 hash between local binary and ESP8266 binary do not match if not. + // then no update has been found. + if ((check_header('x-ESP8266-version') && $headers['x-ESP8266-version'] != $localVersion)) { + // || $headers["x-ESP8266-sketch-md5"] != md5_file($localBinary)) { + sendFile($localBinary, $localVersion); + } else { + header($_SERVER["SERVER_PROTOCOL"].' 304 Not Modified', true, 304); + echo "File ".$localBinary." not modified\n"; + } + } else if ($mode == 'version') { + header($_SERVER["SERVER_PROTOCOL"].' 200 OK', true, 200); + header('x-MD5: '.md5_file($localBinary), true); + header('x-version: '.$localVersion, true); } else { - header($_SERVER["SERVER_PROTOCOL"].' 304 Not Modified', true, 304); + header($_SERVER["SERVER_PROTOCOL"].' 404 Mode not supported', true, 404); + echo "mode: ".$mode." not supported\n"; + exit(); } - - header($_SERVER["SERVER_PROTOCOL"].' 500 no version for ESP MAC', true, 500); + ?> Stream Interface ---------------- @@ -668,9 +702,29 @@ Updater class Updater is in the Core and deals with writing the firmware to the flash, checking its integrity and telling the bootloader (eboot) to load the new firmware on the next boot. -**Note:** The bootloader command will be stored into the first 128 bytes of user RTC memory, then it will be retrieved by eboot on boot. That means that user data present there will be lost `(per discussion in #5330) `__. +The following `Updater ; + void onProgress(THandlerFunction_Progress); // current and total number of bytes + + using THandlerFunction_Error = std::function; + void onStart(THandlerFunction_Error); // error code + + using THandlerFunction = std::function; + void onEnd(THandlerFunction); + void onError(THandlerFunction); + +Using RTC memory +~~~~~~~~~~~~~~~~ + +The bootloader command will be stored into the first 128 bytes of user RTC memory, then it will be retrieved by eboot on boot. That means that user data present there will be lost `(per discussion in #5330) `__. + +Flash mode and size +~~~~~~~~~~~~~~~~~~~ -**Note:** For uncompressed firmware images, the Updater will change the flash mode bits if they differ from the flash mode the device is currently running at. This ensures that the flash mode is not changed to an incompatible mode when the device is in a remote or hard to access area. Compressed images are not modified, thus changing the flash mode in this instance could result in damage to the ESP8266 and/or flash memory chip or your device no longer be accessible via OTA, and requiring re-flashing via a serial connection `(per discussion in #7307) `__. +For uncompressed firmware images, the Updater will change the flash mode bits if they differ from the flash mode the device is currently running at. This ensures that the flash mode is not changed to an incompatible mode when the device is in a remote or hard to access area. Compressed images are not modified, thus changing the flash mode in this instance could result in damage to the ESP8266 and/or flash memory chip or your device no longer be accessible via OTA, and requiring re-flashing via a serial connection `(per discussion in #7307) `__. Update process - memory view ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/reference.rst b/doc/reference.rst index 792e867a47..e06dfd8a3d 100644 --- a/doc/reference.rst +++ b/doc/reference.rst @@ -27,15 +27,22 @@ and have several limitations: or use a scheduled function (which will be called outside of the interrupt context when it is safe) to do long-running work. -* Memory operations can be dangerous and should be avoided in interrupts. - Calls to ``new`` or ``malloc`` should be minimized because they may require - a long running time if memory is fragmented. Calls to ``realloc`` and - ``free`` must NEVER be called. Using any routines or objects which call - ``free`` or ``realloc`` themselves is also forbidden for the same reason. - This means that ``String``, ``std::string``, ``std::vector`` and other - classes which use contiguous memory that may be resized must be used with - extreme care (ensuring strings aren't changed, vector elements aren't - added, etc.). +* Heap API operations can be dangerous and should be avoided in interrupts. + Calls to ``malloc`` should be minimized because they may require a long + running time if memory is fragmented. Calls to ``realloc`` and ``free`` + must NEVER be called. Using any routines or objects which call ``free`` or + ``realloc`` themselves is also forbidden for the same reason. This means + that ``String``, ``std::string``, ``std::vector`` and other classes which + use contiguous memory that may be resized must be used with extreme care + (ensuring strings aren't changed, vector elements aren't added, etc.). + The underlying problem, an allocation address could be actively in use at + the instant of an interrupt. Upon return, the address actively in use may + be invalid after an ISR uses ``realloc`` or ``free`` against the same + allocation. + +* The C++ ``new`` and ``delete`` operators must NEVER be used in an ISR. Their + call path is not in IRAM. Using any routines or objects that use the ``new`` + or ``delete`` operator is also forbidden. Digital IO ---------- 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/ArduinoOTA/ArduinoOTA.cpp b/libraries/ArduinoOTA/ArduinoOTA.cpp index 025c10d2be..4ee31e3751 100644 --- a/libraries/ArduinoOTA/ArduinoOTA.cpp +++ b/libraries/ArduinoOTA/ArduinoOTA.cpp @@ -3,6 +3,7 @@ #endif #include #include +#include #include "ArduinoOTA.h" #include "MD5Builder.h" #include "StreamString.h" @@ -24,10 +25,11 @@ extern "C" { #include #endif -#ifdef DEBUG_ESP_OTA -#ifdef DEBUG_ESP_PORT +#if defined(DEBUG_ESP_OTA) && defined(DEBUG_ESP_PORT) #define OTA_DEBUG DEBUG_ESP_PORT -#endif +#define OTA_DEBUG_PRINTF(fmt, ...) OTA_DEBUG.printf_P(PSTR(fmt), ##__VA_ARGS__) +#else +#define OTA_DEBUG_PRINTF(...) #endif ArduinoOTAClass::ArduinoOTAClass() @@ -93,6 +95,10 @@ void ArduinoOTAClass::setRebootOnSuccess(bool reboot){ _rebootOnSuccess = reboot; } +void ArduinoOTAClass::setEraseConfig(ota_erase_cfg_t eraseConfig){ + _eraseConfig = eraseConfig; +} + void ArduinoOTAClass::begin(bool useMDNS) { if (_initialized) return; @@ -119,7 +125,7 @@ void ArduinoOTAClass::begin(bool useMDNS) { if(!_udp_ota->listen(IP_ADDR_ANY, _port)) return; _udp_ota->onRx(std::bind(&ArduinoOTAClass::_onRx, this)); - + #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS) if(_useMDNS) { MDNS.begin(_hostname.c_str()); @@ -133,9 +139,7 @@ void ArduinoOTAClass::begin(bool useMDNS) { #endif _initialized = true; _state = OTA_IDLE; -#ifdef OTA_DEBUG - OTA_DEBUG.printf("OTA server at: %s.local:%u\n", _hostname.c_str(), _port); -#endif + OTA_DEBUG_PRINTF("OTA server at: %s.local:%u\n", _hostname.c_str(), _port); } int ArduinoOTAClass::parseInt(){ @@ -243,13 +247,11 @@ void ArduinoOTAClass::_runUpdate() { IPAddress ota_ip = _ota_ip; if (!Update.begin(_size, _cmd)) { -#ifdef OTA_DEBUG - OTA_DEBUG.println("Update Begin Error"); -#endif + OTA_DEBUG_PRINTF("Update Begin Error\n"); if (_error_callback) { _error_callback(OTA_BEGIN_ERROR); } - + StreamString ss; Update.printError(ss); _udp_ota->append("ERR: ", 5); @@ -265,8 +267,6 @@ void ArduinoOTAClass::_runUpdate() { delay(100); Update.setMD5(_md5.c_str()); - WiFiUDP::stopAll(); - WiFiClient::stopAll(); if (_start_callback) { _start_callback(); @@ -277,9 +277,7 @@ void ArduinoOTAClass::_runUpdate() { WiFiClient client; if (!client.connect(_ota_ip, _ota_port)) { -#ifdef OTA_DEBUG - OTA_DEBUG.printf("Connect Failed\n"); -#endif + OTA_DEBUG_PRINTF("Connect Failed\n"); _udp_ota->listen(IP_ADDR_ANY, _port); if (_error_callback) { _error_callback(OTA_CONNECT_ERROR); @@ -295,9 +293,7 @@ void ArduinoOTAClass::_runUpdate() { while (!client.available() && waited--) delay(1); if (!waited){ -#ifdef OTA_DEBUG - OTA_DEBUG.printf("Receive Failed\n"); -#endif + OTA_DEBUG_PRINTF("Receive Failed\n"); _udp_ota->listen(IP_ADDR_ANY, _port); if (_error_callback) { _error_callback(OTA_RECEIVE_ERROR); @@ -322,18 +318,31 @@ void ArduinoOTAClass::_runUpdate() { client.flush(); delay(1000); client.stop(); -#ifdef OTA_DEBUG - OTA_DEBUG.printf("Update Success\n"); -#endif + OTA_DEBUG_PRINTF("Update Success\n"); if (_end_callback) { _end_callback(); } if(_rebootOnSuccess){ -#ifdef OTA_DEBUG - OTA_DEBUG.printf("Rebooting...\n"); -#endif + OTA_DEBUG_PRINTF("Rebooting...\n"); //let serial/network finish tasks that might be given in _end_callback delay(100); + if (OTA_ERASE_CFG_NO != _eraseConfig) { + eraseConfigAndReset(); // returns on failure + if (_error_callback) { + _error_callback(OTA_ERASE_SETTINGS_ERROR); + } + if (OTA_ERASE_CFG_ABORT_ON_ERROR == _eraseConfig) { + eboot_command_clear(); + return; + } +#ifdef OTA_DEBUG + else if (OTA_ERASE_CFG_IGNORE_ERROR == _eraseConfig) { + // Fallthrough and restart + } else { + panic(); + } +#endif + } ESP.restart(); } } else { @@ -350,19 +359,33 @@ void ArduinoOTAClass::_runUpdate() { } void ArduinoOTAClass::end() { + if (!_initialized) + return; + _initialized = false; - _udp_ota->unref(); - _udp_ota = 0; + if(_udp_ota){ + _udp_ota->unref(); + _udp_ota = 0; + } #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS) if(_useMDNS){ MDNS.end(); } #endif _state = OTA_IDLE; - #ifdef OTA_DEBUG - OTA_DEBUG.printf("OTA server stopped.\n"); - #endif + OTA_DEBUG_PRINTF("OTA server stopped.\n"); } + +void ArduinoOTAClass::eraseConfigAndReset() { + OTA_DEBUG_PRINTF("Erase Config and Hard Reset ...\n"); + if (WiFi.mode(WIFI_OFF)) { + ESP.eraseConfigAndReset(); // No return testing - Only returns on failure + OTA_DEBUG_PRINTF(" ESP.eraseConfigAndReset() failed!\n"); + } else { + OTA_DEBUG_PRINTF(" WiFi.mode(WIFI_OFF) Timeout!\n"); + } +} + //this needs to be called in the loop() void ArduinoOTAClass::handle() { if (_state == OTA_RUNUPDATE) { diff --git a/libraries/ArduinoOTA/ArduinoOTA.h b/libraries/ArduinoOTA/ArduinoOTA.h index d1a81a316e..d3d93b9b36 100644 --- a/libraries/ArduinoOTA/ArduinoOTA.h +++ b/libraries/ArduinoOTA/ArduinoOTA.h @@ -18,9 +18,16 @@ typedef enum { OTA_BEGIN_ERROR, OTA_CONNECT_ERROR, OTA_RECEIVE_ERROR, - OTA_END_ERROR + OTA_END_ERROR, + OTA_ERASE_SETTINGS_ERROR } ota_error_t; +typedef enum { + OTA_ERASE_CFG_NO = 0, + OTA_ERASE_CFG_IGNORE_ERROR, + OTA_ERASE_CFG_ABORT_ON_ERROR +} ota_erase_cfg_t; + class ArduinoOTAClass { public: @@ -47,6 +54,10 @@ class ArduinoOTAClass //Sets if the device should be rebooted after successful update. Default true void setRebootOnSuccess(bool reboot); + //Sets flag to erase WiFi Settings at reboot/reset. "eraseConfig" selects to + //abort erase on failure or ignore error and erase. + void setEraseConfig(ota_erase_cfg_t eraseConfig = OTA_ERASE_CFG_ABORT_ON_ERROR); + //This callback will be called when OTA connection has begun void onStart(THandlerFunction fn); @@ -64,6 +75,11 @@ class ArduinoOTAClass //Ends the ArduinoOTA service void end(); + + //Has the effect of the "+ WiFi Settings" in the Arduino IDE Tools "Erase + //Flash" selection. Only returns on erase flash failure. + void eraseConfigAndReset(); + //Call this in loop() to run the service. Also calls MDNS.update() when begin() or begin(true) is used. void handle(); @@ -84,6 +100,7 @@ class ArduinoOTAClass bool _initialized = false; bool _rebootOnSuccess = true; bool _useMDNS = true; + ota_erase_cfg_t _eraseConfig = OTA_ERASE_CFG_NO; ota_state_t _state = OTA_IDLE; int _size = 0; int _cmd = 0; diff --git a/libraries/ArduinoOTA/examples/OTAEraseConfig/OTAEraseConfig.ino b/libraries/ArduinoOTA/examples/OTAEraseConfig/OTAEraseConfig.ino new file mode 100644 index 0000000000..85a0797d47 --- /dev/null +++ b/libraries/ArduinoOTA/examples/OTAEraseConfig/OTAEraseConfig.ino @@ -0,0 +1,106 @@ +/* + This example is a variation on BasicOTA. + + As is, this example will "always" erase WiFi Settings and reset after a + successful update. You can make this conditional. +*/ +#include +#include +#include +#include + +#ifndef STASSID +#define STASSID "your-ssid" +#define STAPSK "your-password" +#endif + +const char* ssid = STASSID; +const char* password = STAPSK; + +void setup() { + Serial.begin(115200); + Serial.println("Booting"); + Serial.println(String("Reset Reason: ") + ESP.getResetReason()); + + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + while (WiFi.waitForConnectResult() != WL_CONNECTED) { + Serial.println("Connection Failed! Rebooting..."); + delay(5000); + ESP.restart(); + } + + // Port defaults to 8266 + // ArduinoOTA.setPort(8266); + + // Hostname defaults to esp8266-[ChipID] + // ArduinoOTA.setHostname("myesp8266"); + + // No authentication by default + // ArduinoOTA.setPassword("admin"); + + // Password can be set with it's md5 value as well + // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3 + // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3"); + + ArduinoOTA.onStart([]() { + String type; + if (ArduinoOTA.getCommand() == U_FLASH) { + type = "sketch"; + } else { // U_FS + type = "filesystem"; + } + + // NOTE: if updating FS this would be the place to unmount FS using FS.end() + Serial.println("Start updating " + type); + }); + ArduinoOTA.onEnd([]() { + Serial.println("\nEnd"); + /* + By calling "ArduinoOTA.setEraseConfig(ArduinoOTA::OTA_ERASE_CFG_ABORT_ON_ERROR)," + this example will erase the "WiFi Settings" as part of an OTA update. When + erasing WiFi Settings fails, the OTA Update aborts, and eboot will not + copy the new ".bin" in place. + + Without the call to "ArduinoOTA.setEraseConfig" legacy behavior, the + system restarts without touching the WiFi Settings. + + Options for "setEraseConfig" to handle eraseConfig failures: + OTA_ERASE_CFG_NO - Do not erase WiFi Settings + OTA_ERASE_CFG_IGNORE_ERROR - Ignore the error and continue with update ".bin" copy + OTA_ERASE_CFG_ABORT_ON_ERROR - Cancel flash update copy at reboot + + To meet unique requirements, you can make the call below conditional. + Also, this call could be enabled before ArduinoOTA.onEnd() and canceled + here with "ArduinoOTA.setEraseConfig(OTA_ERASE_CFG_NO)." + */ + ArduinoOTA.setEraseConfig(OTA_ERASE_CFG_ABORT_ON_ERROR); + }); + ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { + Serial.printf("Progress: %u%%\r", (progress / (total / 100))); + }); + ArduinoOTA.onError([](ota_error_t error) { + Serial.printf("Error[%u]: ", error); + if (error == OTA_AUTH_ERROR) { + Serial.println("Auth Failed"); + } else if (error == OTA_BEGIN_ERROR) { + Serial.println("Begin Failed"); + } else if (error == OTA_CONNECT_ERROR) { + Serial.println("Connect Failed"); + } else if (error == OTA_RECEIVE_ERROR) { + Serial.println("Receive Failed"); + } else if (error == OTA_END_ERROR) { + Serial.println("End Failed"); + } else if (error == OTA_ERASE_SETTINGS_ERROR) { + Serial.println("Erase WiFi Settings Failed"); + } + }); + ArduinoOTA.begin(); + Serial.println("Ready"); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); +} + +void loop() { + ArduinoOTA.handle(); +} diff --git a/libraries/ArduinoOTA/examples/OTASdkCheck/OTASdkCheck.ino b/libraries/ArduinoOTA/examples/OTASdkCheck/OTASdkCheck.ino new file mode 100644 index 0000000000..1965321bda --- /dev/null +++ b/libraries/ArduinoOTA/examples/OTASdkCheck/OTASdkCheck.ino @@ -0,0 +1,162 @@ +/* + This example is a variation on BasicOTA. + + Logic added to look for a change in SDK Version. If changed, erase the WiFi + Settings and Reset the system. + + Added extra debug printing to aid in cutting through the confusion of the + multiple reboots. +*/ + +#include +#include +#include +#include +#include + +// You can control the extra debug printing here. To turn off, change 1 to 0. +#if 1 +#ifdef DEBUG_ESP_PORT +#define CONSOLE DEBUG_ESP_PORT +#else +#define CONSOLE Serial +#endif +#define DEBUG_PRINTF(fmt, ...) CONSOLE.printf_P(PSTR(fmt), ##__VA_ARGS__) +#else +#define DEBUG_PRINTF(...) +#endif + +#ifndef STASSID +#define STASSID "your-ssid" +#define STAPSK "your-password" +#endif + +const char* ssid = STASSID; +const char* password = STAPSK; + +struct YourEEPROMData { + // list of parameters you need to keep + // ... + + // To efficiently save and compare SDK version strings, we use their computed + // CRC32 value. + uint32_t sdkCrc; +}; + +bool checkSdkCrc() { + auto reason = ESP.getResetInfoPtr()->reason; + // In this example, the OTA update does a software restart. As coded, SDK + // version checks are only performed after a hard reset. Change the lines + // below at your discretion. + // + // Boot loop guard + // Limit crash loops erasing flash. Only run at Power On or Hardware Reset. + if (REASON_DEFAULT_RST != reason && REASON_EXT_SYS_RST != reason) { + DEBUG_PRINTF(" Boot loop guard - SDK version not checked. To perform check, do a hardware reset.\r\n"); + return true; + } + + const char* sdkVerStr = ESP.getSdkVersion(); + uint32_t sdkVersionCrc = crc32(sdkVerStr, strlen(sdkVerStr)); + + uint32_t savedSdkVersionCrc; + EEPROM.begin((sizeof(struct YourEEPROMData) + 3) & ~3); + EEPROM.get(offsetof(struct YourEEPROMData, sdkCrc), savedSdkVersionCrc); + + DEBUG_PRINTF(" Current SDK Verison: %s CRC(0x%08X)\r\n", sdkVerStr, sdkVersionCrc); + DEBUG_PRINTF(" Previous saved SDK CRC(0x%08X)\r\n", savedSdkVersionCrc); + if (sdkVersionCrc == savedSdkVersionCrc) { + return EEPROM.end(); + } + + DEBUG_PRINTF(" Handle wew SDK Version\r\n"); + // Remember new SDK CRC + EEPROM.put(offsetof(struct YourEEPROMData, sdkCrc), sdkVersionCrc); + if (EEPROM.commit() && EEPROM.end()) { + // Erase WiFi Settings and Reset + DEBUG_PRINTF(" EEPROM update successful. New SDK CRC saved.\r\n"); + DEBUG_PRINTF(" Erase config and reset: ...\r\n"); + ArduinoOTA.eraseConfigAndReset(); // Only returns on fail + DEBUG_PRINTF(" ArduinoOTA.eraseConfigAndReset() failed!\r\n"); + + } else { + DEBUG_PRINTF(" EEPROM.commit() or EEPROM.end() failed!\r\n"); + } + + return false; +} + +void setup() { + Serial.begin(115200); + Serial.println("Booting"); + // It is normal for resets generated by "ArduinoOTA.eraseConfigAndReset()" + // to be reported as "External System". + Serial.println(String("Reset Reason: ") + ESP.getResetReason()); + Serial.println("Check for changes in SDK Version:"); + if (checkSdkCrc()) { + Serial.println(" SDK version has not changed."); + } else { + Serial.println(" SDK version changed and update to saved details failed."); + } + + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + while (WiFi.waitForConnectResult() != WL_CONNECTED) { + Serial.println("Connection Failed! Rebooting..."); + delay(5000); + ESP.restart(); + } + + // Port defaults to 8266 + // ArduinoOTA.setPort(8266); + + // Hostname defaults to esp8266-[ChipID] + // ArduinoOTA.setHostname("myesp8266"); + + // No authentication by default + // ArduinoOTA.setPassword("admin"); + + // Password can be set with it's md5 value as well + // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3 + // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3"); + + ArduinoOTA.onStart([]() { + String type; + if (ArduinoOTA.getCommand() == U_FLASH) { + type = "sketch"; + } else { // U_FS + type = "filesystem"; + } + + // NOTE: if updating FS this would be the place to unmount FS using FS.end() + Serial.println("Start updating " + type); + }); + ArduinoOTA.onEnd([]() { + Serial.println("\nEnd"); + }); + ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { + Serial.printf("Progress: %u%%\r", (progress / (total / 100))); + }); + ArduinoOTA.onError([](ota_error_t error) { + Serial.printf("Error[%u]: ", error); + if (error == OTA_AUTH_ERROR) { + Serial.println("Auth Failed"); + } else if (error == OTA_BEGIN_ERROR) { + Serial.println("Begin Failed"); + } else if (error == OTA_CONNECT_ERROR) { + Serial.println("Connect Failed"); + } else if (error == OTA_RECEIVE_ERROR) { + Serial.println("Receive Failed"); + } else if (error == OTA_END_ERROR) { + Serial.println("End Failed"); + } + }); + ArduinoOTA.begin(); + Serial.println("Ready"); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); +} + +void loop() { + ArduinoOTA.handle(); +} diff --git a/libraries/DNSServer/examples/CaptivePortalAdvanced/CaptivePortalAdvanced.ino b/libraries/DNSServer/examples/CaptivePortalAdvanced/CaptivePortalAdvanced.ino index 55222fbc7b..1aef1afd20 100644 --- a/libraries/DNSServer/examples/CaptivePortalAdvanced/CaptivePortalAdvanced.ino +++ b/libraries/DNSServer/examples/CaptivePortalAdvanced/CaptivePortalAdvanced.ino @@ -4,6 +4,7 @@ #include #include #include +#include /* This example serves a "hello world" on a WLAN and a SoftAP at the same time. @@ -74,8 +75,8 @@ void setup() { server.on("/", handleRoot); server.on("/wifi", handleWifi); server.on("/wifisave", handleWifiSave); - server.on("/generate_204", handleRoot); // Android captive portal. Maybe not needed. Might be handled by notFound handler. - server.on("/fwlink", handleRoot); // Microsoft captive portal. Maybe not needed. Might be handled by notFound handler. + server.on(UriGlob("/generate_204*"), handleRoot); // Android captive portal. Handle "/generate_204_"-like requests. Might be handled by notFound handler. + server.on("/fwlink", handleRoot); // Microsoft captive portal. Maybe not needed. Might be handled by notFound handler. server.onNotFound(handleNotFound); server.begin(); // Web server start Serial.println("HTTP server started"); 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/NAPTCaptivePortal.ino b/libraries/DNSServer/examples/NAPTCaptivePortal/NAPTCaptivePortal.ino new file mode 100644 index 0000000000..2f238ee922 --- /dev/null +++ b/libraries/DNSServer/examples/NAPTCaptivePortal/NAPTCaptivePortal.ino @@ -0,0 +1,392 @@ +/* + This example shows the use of the 'DNS forwarder' feature in the DNSServer. + It does so by combining two examples CaptivePortalAdvanced and + RangeExtender-NAPT. Additionally the CaptivePortalAdvanced part has a few + upgrades to the HTML presentation to improve readability and ease of use on + mobile devices. + + Also for an example of using HTML chunked response, see handleWifi() in + handleHttp.ino. + + This example starts up in Captive Portal mode by default. + It starts the SoftAP and NAPT w/o connecting the WLAN side. + + You connect your computer or mobile device to the WiFi Network 'MagicPortal' + password 'ShowTime'. Your device should shortly notify you of a Captive + Portal and the need to login. If it fails to do so in a timely maner, + navigate to http://172.217.28.1/wifi and configure it there. + + Note, until a successful WLAN connection is made all DNS lookups will point + back to the SoftAP at 172.217.28.1. This is the Captive Portal element of + this example. + + Once the WLAN is connected, your device should notify you that you are + connected. This, of course, assumes your WLAN connection has a path to the + Internet. + + At this stage we are no longer running as a Captive Portal, but a regular + NAPT. The DNSServer will be running with the DNS forwarder enabled. The + DNSServer will resolve lookups for 'margicportal' to point to 172.217.28.1 + and all other lookup request will be forwarded to the 1st DNS server that was + in the DHCP response for the WLAN interface. + + You should now be able to access things on the Internet. The ease of access + to devices on your home Network may vary. By IP address it should work. + Access by a hostname - maybe. Some home routers will use the hostname + supplied during DHCP to support a local DNS table; some do not. + + There is an additional possible complication for using the local DNS, the DNS + suffix list, this subject is seldom discussed. It is normally handled + automaticly by the host computers DNS lookup code. For the DHCP case, the + DHCP server will supply a suffix list, if there is one. Then when a name + lookup fails and the name does not have a trailing (.)dot the host computer + will append a suffix from the list and try again, until successful or the + list is exhaused. This topic I fear can become a TL;DR. A quick wrapup by way + of an example. On an Ubuntu system run `nmcli dev show eth0 | grep + IP4\.DOMAIN` that may show you a suffix list. (replace eth0 with your wlan + interface name) Try adding them to the local name you are failing to connect + to. For example, assume 'myhost' fails. You see that 'lan' is in the suffix + list. Try connecting to 'myhost.lan'. + + mDNS names also will not work. We do not have a way to pass those request + back and forth through the NAPT. + + Note if hostnames are going to work for an ESP8266 device on your home + Network, you have to have the call to WiFi.hostname(...) before you call + WiFi.begin(). + + In this example the SoftAP in 'Captive Portal' uses the same public address + that was used in the CaptivePortalAdvanced example. Depending on your devices + you may or may not be successful in using a private address. A previous + PR-author discovered a fix that made the CaptivePortalAdvanced example work + better with Android devices. That fix was to use that public address. At this + time, this PR-author with a different Android device running the latest + version of Android has seen no problems in using either. At least not yet :) + FWIW: My device also works with the original CaptivePortalAdvanced example + when using a private address. I would suggest keeping the public address + for a while. At lest until you are confident everything is working well + before experimenting with a private address. +*/ + + +#if LWIP_FEATURES && !LWIP_IPV6 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "WifiHttp.h" + +#define NAPT 1000 +#define NAPT_PORT 10 + +/* + Some defines for debugging +*/ +#ifdef DEBUG_ESP_PORT +#define CONSOLE DEBUG_ESP_PORT +#else +#define CONSOLE Serial +#endif + +#define _PRINTF(a, ...) printf_P(PSTR(a), ##__VA_ARGS__) +#define _PRINT(a) print(String(F(a))) +#define _PRINTLN(a) println(String(F(a))) +#define _PRINTLN2(a, b) println(String(F(a)) + b) + +#define CONSOLE_PRINTF CONSOLE._PRINTF +#define CONSOLE_PRINT CONSOLE._PRINT +#define CONSOLE_PRINTLN CONSOLE._PRINTLN +#define CONSOLE_PRINTLN2 CONSOLE._PRINTLN2 + +#ifdef DEBUG_SKETCH +#define DEBUG_PRINTF CONSOLE_PRINTF +#define DEBUG_PRINT CONSOLE_PRINT +#define DEBUG_PRINTLN CONSOLE_PRINTLN +#define DEBUG_PRINTLN2 CONSOLE_PRINTLN2 + +#else +#define DEBUG_PRINTF(...) \ + do { \ + } while (false) +#define DEBUG_PRINT(...) \ + do { \ + } while (false) +#define DEBUG_PRINTLN(...) \ + do { \ + } while (false) +#define DEBUG_PRINTLN2(...) \ + do { \ + } while (false) +#endif + + + +/* Set these to your desired softAP credentials. They are not configurable at runtime */ +#ifndef APSSID +#define APSSID "MagicPortal" +#define APPSK "ShowTime" +#endif + +const char *softAP_ssid = APSSID; +const char *softAP_password = APPSK; + +/* hostname for mDNS. Should work at least on windows. Try http://esp8266.local */ +const char *myHostname = "magicportal"; + +/* Don't set this wifi credentials. They are configurated at runtime and stored on EEPROM */ +char ssid[33] = ""; +char password[65] = ""; +uint8_t bssid[6]; +WiFiEventHandler staModeConnectedHandler; +WiFiEventHandler staModeDisconnectedHandler; + +// DNS server +DNSServer dnsServer; + +// Web server +ESP8266WebServer server(80); + +/* Soft AP network parameters */ +IPAddress apIP(172, 217, 28, 1); +IPAddress netMsk(255, 255, 255, 0); + + +/** Should I connect to WLAN asap? */ +bool connect = false; + +/** Set to true to start WiFi STA at setup time when credentials loaded successfuly from EEPROM */ +/** Set to false to defer WiFi STA until configured through web interface. */ +bool staReady = false; // Don't connect right away + +/** Last time I tried to connect to WLAN */ +unsigned long lastConnectTry = 0; + +/** Current WLAN status */ +unsigned int status = WL_IDLE_STATUS; + +void setup() { + WiFi.persistent(false); // w/o this a flash write occurs at every boot + WiFi.mode(WIFI_OFF); // Prevent use of SDK stored credentials + CONSOLE.begin(115200); + CONSOLE_PRINTLN("\r\n\r\nNAPT with Configuration Portal ..."); + + staModeConnectedHandler = WiFi.onStationModeConnected( + [](const WiFiEventStationModeConnected &data) { + // Keep a copy of the BSSID for the AP that WLAN connects to. + // This is used in the WLAN report on WiFi Details page. + memcpy(bssid, data.bssid, sizeof(bssid)); + }); + + staModeDisconnectedHandler = WiFi.onStationModeDisconnected( + [](const WiFiEventStationModeDisconnected &) { + if (dnsServer.isForwarding()) { + dnsServer.disableForwarder("*"); + dnsServer.setTTL(0); + // Reminder, Serial.println() will not work from these callbacks. + // For debug printf use ets_uart_printf(). + } + }); + + /* + While you can remove the password parameter to make the AP open. + You will be operating with less security and allowing snoopers to see + the credentials you use for your WiFi. + */ + WiFi.softAPConfig(apIP, apIP, netMsk); + WiFi.softAP(softAP_ssid, softAP_password); + // The following comment for delay(500) was committed Aug 19, 2015; is it + // still true? Commented out for verification. - APR 2020 + // delay(500); // Without delay I've seen the IP address blank + CONSOLE_PRINTF("SoftAP '%s' started\r\n", softAP_ssid); + CONSOLE_PRINTLN2(" IP address: ", WiFi.softAPIP().toString()); + + /* Captive portals will usually use a TTL of 0 to avoid DNS cache poisoning. */ + dnsServer.setTTL(0); + + /* Setup the DNS server redirecting all the domains to the apIP */ + dnsServer.start(IANA_DNS_PORT, "*", apIP); + CONSOLE_PRINTLN("DNSServer started:"); + CONSOLE_PRINTF(" DNS Forwarding is %s\r\n", dnsServer.isForwarding() ? "on" : "off"); + CONSOLE_PRINTF(" Resolve all domain lookups, '%s', to this AP's IP address, '%s' %s.\r\n", + dnsServer.getDomainName().c_str(), + softAP_ssid, + WiFi.softAPIP().toString().c_str()); + CONSOLE_PRINTF(" TTL set to %u\r\n", dnsServer.getTTL()); + + /* + Do some NAPT startup stuff + */ + CONSOLE_PRINTLN("Begin NAPT initialization:"); + CONSOLE_PRINTF(" Heap before NAPT init: %d\r\n", ESP.getFreeHeap()); + + err_t ret = ip_napt_init(NAPT, NAPT_PORT); + CONSOLE_PRINTF(" ip_napt_init(%d,%d): ret=%d (OK=%d)\r\n", NAPT, NAPT_PORT, (int)ret, (int)ERR_OK); + if (ret == ERR_OK) { + ret = ip_napt_enable_no(SOFTAP_IF, 1); + CONSOLE_PRINTF(" ip_napt_enable_no(SOFTAP_IF): ret=%d (OK=%d)\r\n", (int)ret, (int)ERR_OK); + if (ret == ERR_OK) { + CONSOLE_PRINTF(" NAPT AP '%s' started.\r\n", softAP_ssid); + if (WiFi.localIP().isSet()) { + CONSOLE_PRINTF(" It is an extension of '%s' made through WLAN interface.\r\n", ssid); + CONSOLE_PRINTF(" Remote WLAN IP Address: %s.\r\n", WiFi.localIP().toString().c_str()); + } + } + } + CONSOLE_PRINTF(" Heap after NAPT init: %d\r\n", ESP.getFreeHeap()); + if (ret != ERR_OK) { + CONSOLE_PRINTF(" NAPT initialization failed!!!\r\n"); + } + + /* Setup web pages: root, wifi config pages, SO captive portal detectors and not found. */ + server.on("/", handleRoot); + server.on("/wifi", handleWifi); + server.on("/wifisave", handleWifiSave); + server.on("/generate_204", handleRoot); // Android captive portal. Maybe not needed. Might be handled by notFound handler. + server.on("/fwlink", handleRoot); // Microsoft captive portal. Maybe not needed. Might be handled by notFound handler. + server.onNotFound(handleNotFound); + server.begin(); // Web server start + CONSOLE_PRINTLN("HTTP server started"); + loadCredentials(); // Load WLAN credentials from network + connect = (strlen(ssid) > 0 && staReady); // Request WLAN connect if there is a SSID and we want to connect at startup +} + +void connectWifi() { + CONSOLE_PRINTF("Connecting as wifi client, WLAN, to '%s' ...\r\n", ssid); + WiFi.disconnect(); + /* + A call to set hostname, must be set before the call to WiFi.begin, otherwise + the name may be missing from the routers DNS lookup results. Note, not all + routers will import registered DHCP host names from clients into the active + local DNS resolver. For those that do, it is best to set hostname before + calling WiFi.begin(). + */ + WiFi.hostname(myHostname); + WiFi.begin(ssid, password); + int connRes = WiFi.waitForConnectResult(); + if (-1 == connRes) { + CONSOLE_PRINTLN(" WiFi.waitForConnectResult() timed out."); + } else { + CONSOLE_PRINTF(" Connection status: %s, %d\r\n", getWiFiStatusString(connRes).c_str(), connRes); + } +} + +void loop() { + if (connect) { + connect = false; + connectWifi(); + lastConnectTry = millis(); + } + { + unsigned int s = WiFi.status(); + if (s == 0 && millis() > (lastConnectTry + 60000) && ssid[0] && staReady) { + /* When all of the following conditions are true, try to connect */ + /* 1) If WLAN disconnected */ + /* 2) Required idle time between connect attempts has passed. */ + /* 3) We have an ssid configured */ + /* 4) We are ready for the STA to come up */ + /* Don't set retry time too low as retry interfere the softAP operation */ + connect = true; + } + if (status != s) { // WLAN status change + CONSOLE_PRINTF("WLAN Status changed:\r\n"); + CONSOLE_PRINTF(" new status: %s, %d\r\n", getWiFiStatusString(s).c_str(), s); + CONSOLE_PRINTF(" previous status: %s, %d\r\n", getWiFiStatusString(status).c_str(), status); + status = s; + if (s == WL_CONNECTED) { + /* Just connected to WLAN */ + CONSOLE.println(); + if (WiFi.localIP().isSet() && WiFi.softAPIP().isSet()) { + CONSOLE_PRINTF("NAPT AP '%s' status:\r\n", softAP_ssid); + if (WiFi.localIP().isSet()) { + CONSOLE_PRINTF(" It is an extension of '%s' made through WLAN interface.\r\n", ssid); + CONSOLE_PRINTF(" WLAN connected with IP Address: %s.\r\n", WiFi.localIP().toString().c_str()); + } + } else { + CONSOLE_PRINT("WLAN connected to "); + CONSOLE.println(ssid); + CONSOLE_PRINT(" IP address: "); + CONSOLE.println(WiFi.localIP()); + } + // Setup MDNS responder + if (!MDNS.begin(myHostname, WiFi.localIP())) { + CONSOLE_PRINTLN(" Error setting up MDNS responder!"); + } else { + CONSOLE_PRINTLN(" mDNS responder started"); + // Add service to MDNS-SD + MDNS.addService("http", "tcp", 80); + } + /* + Setup the DNSServer to respond only to request for our hostname and + forward other name request to the DNS configured to the WLAN. + */ + dnsServer.setTTL(600); // 10 minutes + dnsServer.enableForwarder(myHostname, WiFi.dnsIP(0)); + CONSOLE_PRINTF("DNSServer changes/status:\r\n"); + CONSOLE_PRINTF(" DNS Forwarding is %s\r\n", dnsServer.isForwarding() ? "on" : "off"); + CONSOLE_PRINTF(" Resolve '%s' to this AP's IP address, '%s' %s.\r\n", + dnsServer.getDomainName().c_str(), + softAP_ssid, + WiFi.softAPIP().toString().c_str()); + if (dnsServer.isDNSSet()) { + CONSOLE_PRINTF(" Forward other lookups to DNS: %s\r\n", dnsServer.getDNS().toString().c_str()); + } + CONSOLE_PRINTF(" TTL set to %u\r\n", dnsServer.getTTL()); + + } else { + /* Captive portals will usually use a TTL of 0 to avoid DNS cache poisoning. */ + dnsServer.setTTL(0); + /* Setup the DNSServer to redirect all the domain lookups to the apIP */ + dnsServer.disableForwarder("*"); + CONSOLE_PRINTF("DNSServer changes/status:\r\n"); + CONSOLE_PRINTF(" DNS Forwarding is %s\r\n", dnsServer.isForwarding() ? "on" : "off"); + CONSOLE_PRINTF(" Resolve all domain lookups, '%s', to this AP's IP address, '%s' %s.\r\n", + dnsServer.getDomainName().c_str(), + softAP_ssid, + WiFi.softAPIP().toString().c_str()); + CONSOLE_PRINTF(" TTL set to %u\r\n", dnsServer.getTTL()); + + // Note, it is not necessary to clear the DNS forwarder address. This + // is being done here, to test that methods isDNSSet() and setDNS() work. + dnsServer.setDNS(0U); + if (dnsServer.isDNSSet()) { + CONSOLE_PRINTF(" DNS forwarder address: %s\r\n", dnsServer.getDNS().toString().c_str()); + } else { + CONSOLE_PRINTF(" DNS forwarder address not set.\r\n"); + } + + if (s == WL_NO_SSID_AVAIL) { + WiFi.disconnect(); + } + } + } + if (s == WL_CONNECTED) { + MDNS.update(); + } + } + // Do work: + // DNS + dnsServer.processNextRequest(); + // HTTP + server.handleClient(); +} + +#else // LWIP_FEATURES && !LWIP_IPV6 + +#include +void setup() { + WiFi.persistent(false); + WiFi.mode(WIFI_OFF); + Serial.begin(115200); + Serial.printf("\n\nNAPT not supported in this configuration\n"); +} + +void loop() { +} + +#endif // LWIP_FEATURES && !LWIP_IPV6 diff --git a/libraries/DNSServer/examples/NAPTCaptivePortal/PortalRedirectHttp.ino b/libraries/DNSServer/examples/NAPTCaptivePortal/PortalRedirectHttp.ino new file mode 100644 index 0000000000..5de12229a5 --- /dev/null +++ b/libraries/DNSServer/examples/NAPTCaptivePortal/PortalRedirectHttp.ino @@ -0,0 +1,46 @@ + +#if LWIP_FEATURES && !LWIP_IPV6 + +// Substitution list: +// {t} - target name +// {1} - The target to redirect to, in absolute URL form. +#ifdef DEBUG_VIEW +static const char portalRedirectHTML[] PROGMEM = R"EOF( + + + + + +Redirecting + + +

Captive Portal Redirect

+

Redirecting to {t}

+

If you do not see the menu in 5 seconds, please click on the above link!

+ + +)EOF"; + +#else +static const char portalRedirectHTML[] PROGMEM = R"EOF( +Redirecting

Captive Portal Redirect

Redirecting to {t}

If you do not see the menu in 5 seconds, please click on the above link!

+)EOF"; +#endif + +void sendPortalRedirect(String path, String targetName) { + CONSOLE_PRINTLN2("Request redirected to captive portal, original request was for ", server.hostHeader()); + /* There are 3 points of redirection here: + 1) "Location" element in the header + 2) HTML meta element to redirect + 3) Click on link to redirect + If the "Location" header element works the HTML stuff is never seen. + */ + // https://tools.ietf.org/html/rfc7231#section-6.4.3 + String reply = FPSTR(portalRedirectHTML); + reply.reserve(reply.length() + 2 * path.length() + 80); + reply.replace("{t}", targetName); + reply.replace("{1}", path); + server.redirect(path, reply); +} + +#endif // LWIP_FEATURES && !LWIP_IPV6 diff --git a/libraries/DNSServer/examples/NAPTCaptivePortal/WifiHttp.h b/libraries/DNSServer/examples/NAPTCaptivePortal/WifiHttp.h new file mode 100644 index 0000000000..c3a222a492 --- /dev/null +++ b/libraries/DNSServer/examples/NAPTCaptivePortal/WifiHttp.h @@ -0,0 +1,329 @@ + +// #define DEBUG_VIEW +// The idea here is to debug HTML with DEBUG_VIEW defined then, when finished, +// use one of the minify web applications to strip the comments and nice +// formating spaces. Then update the minified version below. +// +// Also there are comment sections at the top and bottom of each block of HTML +// code. The purpose is to move the lines of C code near by into the blocked +// comment sections. Then you have a large block of continguious HTML that can +// be copy/pasted into one of the online web HTML checkers. You can adjust the +// code there till it is correct then copy/paste back here. Then, you can move +// comment boarders around until the C code is back in place. + +#pragma once + +#include + +#ifdef DEBUG_VIEW +static const char configHead[] PROGMEM = R"EOF( + + + + + + +WiFi + + + + + + +)EOF"; +#else +static const char configHead[] PROGMEM = R"EOF(WiFi )EOF"; +#endif + +#ifdef DEBUG_VIEW +static const char configPresetInput[] PROGMEM = R"EOF( + + + +)EOF"; +#else +static const char configPresetInput[] PROGMEM = R"EOF()EOF"; +#endif + +#ifdef DEBUG_VIEW +static const char configConnection[] PROGMEM = R"EOF( + +
+
+

WiFi Details and Config

+

You are connected through the {w}

+ +)EOF"; +#else +static const char configConnection[] PROGMEM = R"EOF(

WiFi Details and Config

You are connected through the {w}

)EOF"; +#endif + +#ifdef DEBUG_VIEW +static const char configAPInfo[] PROGMEM = R"EOF( + +
+

SoftAP Details

+ + + + + +
SSI{s}
BSSID {b}
IP{i}
STACount: {a}
+ +)EOF"; +#else +static const char configAPInfo[] PROGMEM = R"EOF(

SoftAP Details

SSI{s}
BSSID {b}
IP{i}
STACount: {a}
)EOF"; +#endif + +#ifdef DEBUG_VIEW +static const char configWLANInfo[] PROGMEM = R"EOF( + +
+

WLAN Details

+ + + + + + + + + + + +
SSID{s}
BSSID {b}
CH{c}
PHY{p}
RSSI{r}
IP{i}
GW{g}
Mask{m}
DNS1{1}
DNS2{2}
+ +)EOF"; +#else +static const char configWLANInfo[] PROGMEM = R"EOF(

WLAN Details

SSID{s}
BSSID {b}
CH{c}
PHY{p}
RSSI{r}
IP{i}
GW{g}
Mask{m}
DNS1{1}
DNS2{2}
)EOF"; +#endif + + +#ifdef DEBUG_VIEW +static const char configWLANOffline[] PROGMEM = R"EOF( + +
+

WLAN - offline

+ +)EOF"; +#else +static const char configWLANOffline[] PROGMEM = R"EOF(

WLAN - offline

)EOF"; +#endif + + +#ifdef DEBUG_VIEW +static const char configList[] PROGMEM = R"EOF( + +
+

WLAN Network List

+

(refresh if any are missing)

+ + + + + + +)EOF"; +#else +static const char configList[] PROGMEM = R"EOF(

WLAN Network List

(refresh if any are missing)

Network Name/SSIDCHRSSI
)EOF"; +#endif + +#ifdef DEBUG_VIEW +static const char configItem[] PROGMEM = R"EOF( + + + + + + +)EOF"; +#else +static const char configItem[] PROGMEM = R"EOF()EOF"; +#endif + +#ifdef DEBUG_VIEW +static const char configNoAPs[] PROGMEM = R"EOF( + + + + + + +)EOF"; +#else +static const char configNoAPs[] PROGMEM = R"EOF()EOF"; +#endif + +#ifdef DEBUG_VIEW +static const char configEnd[] PROGMEM = R"EOF( + +
Network Name/SSIDCHRSSI
{s}{c}{l}{r}
{s}{c}{l}{r}
No WLAN found
No WLAN found
+

Connect to Network:

+ +

+ + +)EOF"; +#else +#endif + +#ifdef DEBUG_VIEW +static const char configEnd2[] PROGMEM = R"EOF( + +  👁 +

+
+
+

You may want to return to the home page.

+

+
+
+ + + +)EOF"; +#else +static const char configEnd[] PROGMEM = R"EOF(

Connect to Network:



  👁


You may want to return to the home page.

)EOF"; +#endif diff --git a/libraries/DNSServer/examples/NAPTCaptivePortal/credentials.ino b/libraries/DNSServer/examples/NAPTCaptivePortal/credentials.ino new file mode 100644 index 0000000000..2d0827d0c2 --- /dev/null +++ b/libraries/DNSServer/examples/NAPTCaptivePortal/credentials.ino @@ -0,0 +1,33 @@ + +#if LWIP_FEATURES && !LWIP_IPV6 + +/** Load WLAN credentials from EEPROM */ +void loadCredentials() { + EEPROM.begin(512); + EEPROM.get(0, ssid); + EEPROM.get(0 + sizeof(ssid), password); + char ok[2 + 1]; + EEPROM.get(0 + sizeof(ssid) + sizeof(password), ok); + EEPROM.end(); + if (String(ok) != String("OK")) { + ssid[0] = 0; + password[0] = 0; + } + + CONSOLE_PRINTLN("Recovered credentials:"); + CONSOLE_PRINTF(" %s\r\n", ssid); + CONSOLE_PRINTF(" %s\r\n", strlen(password) > 0 ? "********" : ""); +} + +/** Store WLAN credentials to EEPROM */ +void saveCredentials() { + EEPROM.begin(512); + EEPROM.put(0, ssid); + EEPROM.put(0 + sizeof(ssid), password); + char ok[2 + 1] = "OK"; + EEPROM.put(0 + sizeof(ssid) + sizeof(password), ok); + EEPROM.commit(); + EEPROM.end(); +} + +#endif // LWIP_FEATURES && !LWIP_IPV6 diff --git a/libraries/DNSServer/examples/NAPTCaptivePortal/handleHttp.ino b/libraries/DNSServer/examples/NAPTCaptivePortal/handleHttp.ino new file mode 100644 index 0000000000..d8c2a48433 --- /dev/null +++ b/libraries/DNSServer/examples/NAPTCaptivePortal/handleHttp.ino @@ -0,0 +1,250 @@ +#if LWIP_FEATURES && !LWIP_IPV6 + +#ifndef TCP_MSS +#define TCP_MSS 1460 +#endif +/* + Use kMaxChunkSize to limit size of chuncks +*/ +constexpr inline size_t kMaxChunkSize = TCP_MSS; +String& sendIfOver(String& str, size_t threshold = kMaxChunkSize / 2); +size_t sendAsChunks_P(PGM_P content, size_t chunkSize = kMaxChunkSize); + +size_t maxPage = 0; + +void addNoCacheHeader() { + server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + server.sendHeader("Pragma", "no-cache"); + server.sendHeader("Expires", "-1"); +} + + +String& sendIfOver(String& str, size_t threshold) { + size_t len = str.length(); + if (len > threshold) { + // Use later to determine if we reserved enough room in page to avoid realloc + maxPage = std::max(maxPage, len); + server.sendContent(str); + str = ""; + } + return str; +} + +/* + The idea here is to avoid a large allocation by sendContent_P to copy a + big PROGMEM string. Slice PROGMEM string into chuncks and send. +*/ +size_t sendAsChunks_P(PGM_P content, size_t chunkSize) { + size_t len = strlen_P(content); + for (size_t pos = 0; pos < len; pos += chunkSize) { + server.sendContent_P(&content[pos], ((len - pos) >= chunkSize) ? chunkSize : len - pos); + } + return len; +} + +/** Handle root or redirect to captive portal */ +void handleRoot() { + if (captivePortal()) { + // If captive portal is needed, redirect instead of displaying the page. + return; + } + addNoCacheHeader(); + + String Page; + Page += F( + "" + "" + "ADV CAP Portal Example" + "" + "

HELLO WORLD!!

"); + if (server.client().localIP() == apIP) { + Page += String(F("

You are connected through the soft AP: ")) + softAP_ssid + F("

"); + } else { + Page += String(F("

You are connected through the wifi network: ")) + ssid + F("

"); + } + Page += F( + "

You may want to config the wifi connection.

" + ""); + + server.send(200, F("text/html"), Page); +} + +/* + Redirect to the captive portal if we got a request for another domain. + Return true in that case, so the page handler does not try to handle + the request again. +*/ +boolean captivePortal() { + IPAddress hAddr, cAddr; + + cAddr = server.client().localIP(); + if (!cAddr.isSet()) { + // The connection closed prematurely on us. + // Return true, so no further action is taken. + return true; + } + + if (hAddr.fromString(server.hostHeader()) && hAddr == cAddr) { + return false; + } + + if (hAddr.isSet() || (server.hostHeader() != (String(myHostname) + ".local") && // arrived here by mDNS + server.hostHeader() != String(myHostname))) { // arrived here by local router DNS + String whereTo = String("http://") + server.client().localIP().toString(); + sendPortalRedirect(whereTo, F("Captive Portal Example")); + return true; + } + + return false; +} + + +/** Wifi Details and Config page handler */ +void handleWifi() { + addNoCacheHeader(); + + // use HTTP/1.1 Chunked response to avoid building a huge temporary string + if (!server.chunkedResponseModeStart(200, F("text/html"))) { + server.send(505, F("text/plain"), F("HTTP1.1 required")); + return; + } + + // Send a few chunks of the HTML that don't need to change. + sendAsChunks_P(configHead); + + String page; + + CONSOLE_PRINTLN2("sizeof(configHead): ", (sizeof(configHead))); + CONSOLE_PRINTLN2("sizeof(configWLANInfo): ", (sizeof(configWLANInfo))); + CONSOLE_PRINTLN2("sizeof(configList): ", (sizeof(configList))); + CONSOLE_PRINTLN2("sizeof(configEnd): ", (sizeof(configEnd))); + // Just do max on some of the visually larger HTML chunks that will be loaded + // into page and add a little for growth when substituting values in. + size_t thisMany = std::max(sizeof(configWLANInfo), sizeof(configList)) + 200; + CONSOLE_PRINTLN2("Estimate Minimum page reserve size: ", (thisMany)); + page.reserve(std::max(kMaxChunkSize, thisMany)); + + page = FPSTR(configPresetInput); + /* + Set previously used/entered credentials as a default entries. + This allows an opportunity to correct them and try again. + */ + page.replace("{s}", String(ssid)); + page.replace("{p}", String(password)); + sendIfOver(page); + + page += FPSTR(configConnection); + if (server.client().localIP() == apIP) { + page.replace("{w}", String(F("SoftAP: ")) + softAP_ssid); + } else { + page.replace("{w}", String(F("WiFi Network: ")) + ssid); + } + + /* + To avoid sending lots of small packets. We call this function frequently, + to check if the 'page' has gone over 512 bytes and if so send. + */ + sendIfOver(page); + + page += FPSTR(configAPInfo); + { + uint8_t sta_cnt = wifi_softap_get_station_num(); + page.replace("{s}", String(softAP_ssid)); + page.replace("{b}", String(WiFi.softAPmacAddress())); + page.replace("{i}", WiFi.softAPIP().toString()); + page.replace("{a}", String(sta_cnt)); + sendIfOver(page); + if (sta_cnt) { + page += String(F("\r\n
\r\n"));
+      struct station_info* info = wifi_softap_get_station_info();
+      IPAddress addr;
+      while (info != NULL) {
+        addr = info->ip;
+        page += macToString(info->bssid) + F("  ") + addr.toString() + F("\r\n");
+        info = STAILQ_NEXT(info, next);
+        sendIfOver(page);
+      }
+      page += F("
\r\n"); + } + } + + /* + Before we prepare a large block for sending, we call 'sendIfOver' with a + threshold of 0 to force the sending of the current 'page' content. + */ + + if (WiFi.localIP().isSet()) { + sendIfOver(page, 0); + page += FPSTR(configWLANInfo); + page.replace("{s}", String(ssid)); + page.replace("{b}", macToString(bssid)); + page.replace("{c}", String(WiFi.channel())); + page.replace("{p}", String(F("802.11")) + (getPhyModeChar(WiFi.getPhyMode()))); + page.replace("{r}", String(WiFi.RSSI())); + page.replace("{i}", WiFi.localIP().toString()); + page.replace("{g}", WiFi.gatewayIP().toString()); + page.replace("{m}", WiFi.subnetMask().toString()); + page.replace("{1}", WiFi.dnsIP(0).toString()); + page.replace("{2}", WiFi.dnsIP(1).toString()); + } else { + page += FPSTR(configWLANOffline); + } + + sendIfOver(page, 0); + sendAsChunks_P(configList); + + CONSOLE_PRINTLN("scan start"); + int n = WiFi.scanNetworks(); + CONSOLE_PRINTLN("scan done"); + + if (n > 0) { + for (size_t i = 0; i < (size_t)n; i++) { + page += FPSTR(configItem); + page.replace("{s}", WiFi.SSID(i)); + page.replace("{t}", WiFi.BSSIDstr(i)); + page.replace("{c}", String(WiFi.channel(i))); + page.replace("{l}", (WiFi.encryptionType(i) == ENC_TYPE_NONE) ? F("") : F("🔒")); + page.replace("{r}", String(WiFi.RSSI(i))); + sendIfOver(page); + } + } else { + page += FPSTR(configNoAPs); + } + sendIfOver(page, 0); // send what we have buffered before next direct send. + sendAsChunks_P(configEnd); + + CONSOLE_PRINTLN2("MAX String memory used: ", (maxPage)); + server.chunkedResponseFinalize(); +} + +/** Handle the WLAN save form and redirect to WLAN config page again */ +void handleWifiSave() { + CONSOLE_PRINTLN("wifi save"); + server.arg("n").toCharArray(ssid, sizeof(ssid) - 1); + server.arg("p").toCharArray(password, sizeof(password) - 1); + sendPortalRedirect(F("wifi"), F("Wifi Config")); + saveCredentials(); + connect = strlen(ssid) > 0; // Request WLAN connect with new credentials if there is a SSID +} + +void handleNotFound() { + if (captivePortal()) { // If captive portal redirect instead of displaying the error page. + return; + } + String message = F("File Not Found\r\n\r\n"); + message += F("URI: "); + message += server.uri(); + message += F("\r\nMethod: "); + message += (server.method() == HTTP_GET) ? "GET" : "POST"; + message += F("\r\nArguments: "); + message += server.args(); + message += F("\r\n"); + + for (uint8_t i = 0; i < server.args(); i++) { + message += String(F(" ")) + server.argName(i) + F(": ") + server.arg(i) + F("\r\n"); + } + addNoCacheHeader(); + server.send(404, F("text/plain"), message); +} + +#endif // LWIP_FEATURES && !LWIP_IPV6 diff --git a/libraries/DNSServer/examples/NAPTCaptivePortal/tools.ino b/libraries/DNSServer/examples/NAPTCaptivePortal/tools.ino new file mode 100644 index 0000000000..976ecc9fb4 --- /dev/null +++ b/libraries/DNSServer/examples/NAPTCaptivePortal/tools.ino @@ -0,0 +1,84 @@ +/* + These functions may exist in other projects +*/ + +#if LWIP_FEATURES && !LWIP_IPV6 + +/* + Returns a descriptive string for WiFi.status() value +*/ +String getWiFiStatusString(uint32_t status) { + const __FlashStringHelper* r; + switch (status) { + case WL_IDLE_STATUS: + r = F("WL_IDLE_STATUS"); + break; + + case WL_NO_SSID_AVAIL: + r = F("WL_NO_SSID_AVAIL"); + break; + + case WL_SCAN_COMPLETED: + r = F("WL_SCAN_COMPLETED"); + break; + + case WL_CONNECTED: + r = F("WL_CONNECTED"); + break; + + case WL_CONNECT_FAILED: + r = F("WL_CONNECT_FAILED"); + break; + + case WL_CONNECTION_LOST: + r = F("WL_CONNECTION_LOST"); + break; + + case WL_DISCONNECTED: + r = F("WL_DISCONNECTED"); + break; + + case WL_NO_SHIELD: + r = F("WL_NO_SHIELD"); + break; + + default: + return String(F("Unknown: 0x")) + String(status, HEX); + } + return String(r); +} + +/* + Returns a single charcter to append to a "802.11" string to describe the PHY + mode of a WiFi device. Can be used with the value returned by WiFi.getPhyMode(). +*/ +char getPhyModeChar(WiFiPhyMode_t i) { + switch (i) { + case WIFI_PHY_MODE_11B: + return 'b'; // = 1 + case WIFI_PHY_MODE_11G: + return 'g'; // = 2, + case WIFI_PHY_MODE_11N: + return 'n'; // = 3, + default: + break; + } + return '?'; +} + +/* + Return a String of 6 colon separated hex bytes. + This format is commonly used when printing 6 byte MAC addresses. +*/ +String macToString(const unsigned char* mac) { + char buf[20]; + int rc = snprintf(buf, sizeof(buf), PSTR("%02X:%02X:%02X:%02X:%02X:%02X"), + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + if (rc < 0 || rc >= (int)sizeof(buf)) { + return emptyString; + } + return String(buf); +} + +#endif // LWIP_FEATURES && !LWIP_IPV6 diff --git a/libraries/DNSServer/src/DNSServer.cpp b/libraries/DNSServer/src/DNSServer.cpp index 8d7b3dfb06..4ffeb88a37 100644 --- a/libraries/DNSServer/src/DNSServer.cpp +++ b/libraries/DNSServer/src/DNSServer.cpp @@ -1,33 +1,139 @@ +#include #include "DNSServer.h" #include #include -#include + +extern struct rst_info resetInfo; #ifdef DEBUG_ESP_PORT -#define DEBUG_OUTPUT DEBUG_ESP_PORT +#define CONSOLE DEBUG_ESP_PORT +#else +#define CONSOLE Serial +#endif + +#define _ETS_PRINTF(a, ...) ets_uart_printf(a, ##__VA_ARGS__) +#define _ETS_PRINTFNL(a, ...) ets_uart_printf(a "\n", ##__VA_ARGS__) +#define _PRINTF(a, ...) printf_P(PSTR(a), ##__VA_ARGS__) +#define _PRINT(a) print(String(F(a))) +#define _PRINTLN(a) println(String(F(a))) +#define _PRINTLN2(a, b) println(String(F(a)) + b ) + +#define ETS_PRINTF _ETS_PRINTF +#define ETS_PRINTFNL _ETS_PRINTFNL +#define CONSOLE_PRINTF CONSOLE._PRINTF +#define CONSOLE_PRINT CONSOLE._PRINT +#define CONSOLE_PRINTLN CONSOLE._PRINTLN +#define CONSOLE_PRINTLN2 CONSOLE._PRINTLN2 + + +#ifdef DEBUG_DNSSERVER +#define DEBUG_PRINTF CONSOLE_PRINTF +#define DEBUG_PRINT CONSOLE_PRINT +#define DEBUG_PRINTLN CONSOLE_PRINTLN +#define DEBUG_PRINTLN2 CONSOLE_PRINTLN2 +#define DBGLOG_FAIL LOG_FAIL + +#define DEBUG_(...) do { (__VA_ARGS__); } while(false) +#define DEBUG__(...) __VA_ARGS__ +#define LOG_FAIL(a, fmt, ...) do { if (!(a)) { CONSOLE.printf_P( PSTR(fmt " line: %d, function: %s\r\n"), ##__VA_ARGS__, __LINE__, __FUNCTION__ ); } } while(false); + #else -#define DEBUG_OUTPUT Serial +#define DEBUG_PRINTF(...) do { } while(false) +#define DEBUG_PRINT(...) do { } while(false) +#define DEBUG_PRINTLN(...) do { } while(false) +#define DEBUG_PRINTLN2(...) do { } while(false) +#define DEBUG_(...) do { } while(false) +#define DEBUG__(...) do { } while(false) +#define LOG_FAIL(a, ...) do { a; } while(false) +#define DBGLOG_FAIL(...) do { } while(false) #endif #define DNS_HEADER_SIZE sizeof(DNSHeader) +// Want to keep IDs unique across restarts and continquious +static uint32_t _ids __attribute__((section(".noinit"))); + DNSServer::DNSServer() { + // I have observed that using 0 for captive and non-zero (600) when + // forwarding, will help Android devices recognize the change in connectivity. + // They will then report connected. _ttl = lwip_htonl(60); + + if (REASON_DEFAULT_RST == resetInfo.reason || + REASON_DEEP_SLEEP_AWAKE <= resetInfo.reason) { + _ids = random(0, BIT(16) - 1); + } + _ids += kDNSSQueSize; // for the case of restart, ignore any inflight responses + _errorReplyCode = DNSReplyCode::NonExistentDomain; } +void DNSServer::disableForwarder(const String &domainName, bool freeResources) +{ + _forwarder = false; + if (!domainName.isEmpty()) { + _domainName = domainName; + downcaseAndRemoveWwwPrefix(_domainName); + } + if (freeResources) { + _dns = (uint32_t)0; + if (_que) { + _que = nullptr; + DEBUG_PRINTF("from stop, deleted _que\r\n"); + DEBUG_(({ + if (_que_ov) { + DEBUG_PRINTLN2("DNS forwarder que overflow or no reply to request: ", (_que_ov)); + } + if (_que_drop) { + DEBUG_PRINTLN2("DNS forwarder que wrapped, reply dropped: ", (_que_drop)); + } + })); + } + } +} + +bool DNSServer::enableForwarder(const String &domainName, const IPAddress &dns) +{ + disableForwarder(domainName, false); // Just happens to have the same logic needed here. + + if (dns.isSet()) { + _dns = dns; + } + + if (_dns.isSet()) { + if (!_que) { + _que = std::unique_ptr (new (std::nothrow) DNSS_REQUESTER[kDNSSQueSize]); + DEBUG_PRINTF("Created new _que\r\n"); + if (_que) { + for (size_t i = 0; i < kDNSSQueSize; i++) { + _que[i].ip = 0; + } + DEBUG_((_que_ov = 0)); + DEBUG_((_que_drop = 0)); + } + } + if (_que) { + _forwarder = true; + } + } + return _forwarder; +} + bool DNSServer::start(const uint16_t &port, const String &domainName, - const IPAddress &resolvedIP) + const IPAddress &resolvedIP, const IPAddress &dns) { - _port = port; - - _domainName = domainName; + _port = (port) ? port : IANA_DNS_PORT; + _resolvedIP[0] = resolvedIP[0]; _resolvedIP[1] = resolvedIP[1]; _resolvedIP[2] = resolvedIP[2]; _resolvedIP[3] = resolvedIP[3]; - downcaseAndRemoveWwwPrefix(_domainName); + + if (!enableForwarder(domainName, dns) && (dns.isSet() || _dns.isSet())) { + return false; + } + return _udp.begin(_port) == 1; } @@ -41,9 +147,15 @@ void DNSServer::setTTL(const uint32_t &ttl) _ttl = lwip_htonl(ttl); } +uint32_t DNSServer::getTTL() +{ + return lwip_ntohl(_ttl); +} + void DNSServer::stop() { _udp.stop(); + disableForwarder(emptyString, true); } void DNSServer::downcaseAndRemoveWwwPrefix(String &domainName) @@ -53,7 +165,58 @@ void DNSServer::downcaseAndRemoveWwwPrefix(String &domainName) domainName.remove(0, 4); } -void DNSServer::respondToRequest(uint8_t *buffer, size_t length) +void DNSServer::forwardReply(uint8_t *buffer, size_t length) +{ + if (!_forwarder || !_que) { + return; + } + DNSHeader *dnsHeader = (DNSHeader *)buffer; + uint16_t id = dnsHeader->ID; + // if (kDNSSQueSize <= (uint16_t)((uint16_t)_ids - id)) { + if ((uint16_t)kDNSSQueSize <= (uint16_t)_ids - id) { + DEBUG_((++_que_drop)); + DEBUG_PRINTLN2("Forward reply ID: 0x", (String(id, HEX) + F(" dropped!"))); + return; + } + size_t i = id & (kDNSSQueSize - 1); + + // Drop duplicate packets + if (0 == _que[i].ip) { + DEBUG_PRINTLN2("Duplicate reply dropped ID: 0x", String(id, HEX)); + return; + } + dnsHeader->ID = _que[i].id; + _udp.beginPacket(_que[i].ip, _que[i].port); + _udp.write(buffer, length); + _udp.endPacket(); + DEBUG_PRINTLN2("Forward reply ID: 0x", (String(id, HEX) + F(" to ") + IPAddress(_que[i].ip).toString())); + _que[i].ip = 0; // This gets used to detect duplicate packets and overflow +} + +void DNSServer::forwardRequest(uint8_t *buffer, size_t length) +{ + if (!_forwarder || !_dns.isSet() || !_que) { + return; + } + DNSHeader *dnsHeader = (DNSHeader *)buffer; + ++_ids; + size_t i = _ids & (kDNSSQueSize - 1); + DEBUG_(({ + if (0 != _que[i].ip) { + ++_que_ov; + } + })); + _que[i].ip = _udp.remoteIP(); + _que[i].port = _udp.remotePort(); + _que[i].id = dnsHeader->ID; + dnsHeader->ID = (uint16_t)_ids; + _udp.beginPacket(_dns, IANA_DNS_PORT); + _udp.write(buffer, length); + _udp.endPacket(); + DEBUG_PRINTLN2("Forward request ID: 0x", (String(dnsHeader->ID, HEX) + F(" to ") + _dns.toString())); +} + +bool DNSServer::respondToRequest(uint8_t *buffer, size_t length) { DNSHeader *dnsHeader; uint8_t *query, *start; @@ -64,23 +227,30 @@ void DNSServer::respondToRequest(uint8_t *buffer, size_t length) dnsHeader = (DNSHeader *)buffer; // Must be a query for us to do anything with it - if (dnsHeader->QR != DNS_QR_QUERY) - return; + if (dnsHeader->QR != DNS_QR_QUERY) { + return false; + } // If operation is anything other than query, we don't do it - if (dnsHeader->OPCode != DNS_OPCODE_QUERY) - return replyWithError(dnsHeader, DNSReplyCode::NotImplemented); + if (dnsHeader->OPCode != DNS_OPCODE_QUERY) { + replyWithError(dnsHeader, DNSReplyCode::NotImplemented); + return false; + } // Only support requests containing single queries - everything else // is badly defined - if (dnsHeader->QDCount != lwip_htons(1)) - return replyWithError(dnsHeader, DNSReplyCode::FormError); + if (dnsHeader->QDCount != lwip_htons(1)) { + replyWithError(dnsHeader, DNSReplyCode::FormError); + return false; + } // We must return a FormError in the case of a non-zero ARCount to // be minimally compatible with EDNS resolvers if (dnsHeader->ANCount != 0 || dnsHeader->NSCount != 0 - || dnsHeader->ARCount != 0) - return replyWithError(dnsHeader, DNSReplyCode::FormError); + || dnsHeader->ARCount != 0) { + replyWithError(dnsHeader, DNSReplyCode::FormError); + return false; + } // Even if we're not going to use the query, we need to parse it // so we can check the address type that's being queried @@ -89,15 +259,19 @@ void DNSServer::respondToRequest(uint8_t *buffer, size_t length) remaining = length - DNS_HEADER_SIZE; while (remaining != 0 && *start != 0) { labelLength = *start; - if (labelLength + 1 > remaining) - return replyWithError(dnsHeader, DNSReplyCode::FormError); + if (labelLength + 1 > remaining) { + replyWithError(dnsHeader, DNSReplyCode::FormError); + return false; + } remaining -= (labelLength + 1); start += (labelLength + 1); } // 1 octet labelLength, 2 octet qtype, 2 octet qclass - if (remaining < 5) - return replyWithError(dnsHeader, DNSReplyCode::FormError); + if (remaining < 5) { + replyWithError(dnsHeader, DNSReplyCode::FormError); + return false; + } start += 1; // Skip the 0 length label that we found above @@ -109,23 +283,33 @@ void DNSServer::respondToRequest(uint8_t *buffer, size_t length) queryLength = start - query; if (qclass != lwip_htons(DNS_QCLASS_ANY) - && qclass != lwip_htons(DNS_QCLASS_IN)) - return replyWithError(dnsHeader, DNSReplyCode::NonExistentDomain, - query, queryLength); + && qclass != lwip_htons(DNS_QCLASS_IN)) { + replyWithError(dnsHeader, DNSReplyCode::NonExistentDomain, query, queryLength); + return false; + } if (qtype != lwip_htons(DNS_QTYPE_A) - && qtype != lwip_htons(DNS_QTYPE_ANY)) - return replyWithError(dnsHeader, DNSReplyCode::NonExistentDomain, - query, queryLength); + && qtype != lwip_htons(DNS_QTYPE_ANY)) { + replyWithError(dnsHeader, DNSReplyCode::NonExistentDomain, query, queryLength); + return false; + } // If we have no domain name configured, just return an error - if (_domainName.isEmpty()) - return replyWithError(dnsHeader, _errorReplyCode, - query, queryLength); + if (_domainName.isEmpty()) { + if (_forwarder) { + return true; + } else { + replyWithError(dnsHeader, _errorReplyCode, query, queryLength); + return false; + } + } // If we're running with a wildcard we can just return a result now - if (_domainName == "*") - return replyWithIP(dnsHeader, query, queryLength); + if (_domainName == "*") { + DEBUG_PRINTF("dnsServer - replyWithIP\r\n"); + replyWithIP(dnsHeader, query, queryLength); + return false; + } matchString = _domainName.c_str(); @@ -139,24 +323,32 @@ void DNSServer::respondToRequest(uint8_t *buffer, size_t length) labelLength = *start; start += 1; while (labelLength > 0) { - if (tolower(*start) != *matchString) - return replyWithError(dnsHeader, _errorReplyCode, - query, queryLength); + if (tolower(*start) != *matchString) { + if (_forwarder) { + return true; + } else { + replyWithError(dnsHeader, _errorReplyCode, query, queryLength); + return false; + } + } ++start; ++matchString; --labelLength; } - if (*start == 0 && *matchString == '\0') - return replyWithIP(dnsHeader, query, queryLength); + if (*start == 0 && *matchString == '\0') { + replyWithIP(dnsHeader, query, queryLength); + return false; + } - if (*matchString != '.') - return replyWithError(dnsHeader, _errorReplyCode, - query, queryLength); + if (*matchString != '.') { + replyWithError(dnsHeader, _errorReplyCode, query, queryLength); + return false; + } ++matchString; } - return replyWithError(dnsHeader, _errorReplyCode, - query, queryLength); + replyWithError(dnsHeader, _errorReplyCode, query, queryLength); + return false; } void DNSServer::processNextRequest() @@ -182,7 +374,14 @@ void DNSServer::processNextRequest() return; _udp.read(buffer.get(), currentPacketSize); - respondToRequest(buffer.get(), currentPacketSize); + if (_dns.isSet() && _udp.remoteIP() == _dns) { + // _forwarder may have been set to false; however, for now allow inflight + // replys to finish. //?? + forwardReply(buffer.get(), currentPacketSize); + } else + if (respondToRequest(buffer.get(), currentPacketSize)) { + forwardRequest(buffer.get(), currentPacketSize); + } } void DNSServer::writeNBOShort(uint16_t value) diff --git a/libraries/DNSServer/src/DNSServer.h b/libraries/DNSServer/src/DNSServer.h index ad50516c81..44af7e4d90 100644 --- a/libraries/DNSServer/src/DNSServer.h +++ b/libraries/DNSServer/src/DNSServer.h @@ -1,7 +1,17 @@ #ifndef DNSServer_h #define DNSServer_h + +#include #include +// #define DEBUG_DNSSERVER + +// https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.txt +#ifndef IANA_DNS_PORT +#define IANA_DNS_PORT 53 // AKA domain +constexpr inline uint16_t kIanaDnsPort = IANA_DNS_PORT; +#endif + #define DNS_QR_QUERY 0 #define DNS_QR_RESPONSE 1 #define DNS_OPCODE_QUERY 0 @@ -45,6 +55,15 @@ struct DNSHeader uint16_t ARCount; // number of resource entries }; +constexpr inline size_t kDNSSQueSizeAddrBits = 3; // The number of bits used to address que entries +constexpr inline size_t kDNSSQueSize = BIT(kDNSSQueSizeAddrBits); + +struct DNSS_REQUESTER { + uint32_t ip; + uint16_t port; + uint16_t id; +}; + class DNSServer { public: @@ -52,24 +71,60 @@ class DNSServer ~DNSServer() { stop(); }; + /* + If specified, `enableForwarder` will update the `domainName` that is used + to match DNS request to this AP's IP Address. A non-matching request will + be forwarded to the DNS server specified by `dns`. + + Returns `true` on success. + + Returns `false`, + * when forwarding `dns` is not set, or + * unable to allocate resources for managing the DNS forward function. + */ + bool enableForwarder(const String &domainName = emptyString, const IPAddress &dns = (uint32_t)0); + /* + `disableForwarder` will stop forwarding DNS requests. If specified, + updates the `domainName` that is matched for returning this AP's IP Address. + Optionally, resources used for the DNS forward function can be freed. + */ + void disableForwarder(const String &domainName = emptyString, bool freeResources = false); + bool isForwarding() { return _forwarder && _dns.isSet(); } + void setDNS(const IPAddress& dns) { _dns = dns; } + IPAddress getDNS() { return _dns; } + bool isDNSSet() { return _dns.isSet(); } + void processNextRequest(); void setErrorReplyCode(const DNSReplyCode &replyCode); void setTTL(const uint32_t &ttl); + uint32_t getTTL(); + String getDomainName() { return _domainName; } // Returns true if successful, false if there are no sockets available bool start(const uint16_t &port, const String &domainName, - const IPAddress &resolvedIP); + const IPAddress &resolvedIP, + const IPAddress &dns = (uint32_t)0); // stops the DNS server void stop(); private: WiFiUDP _udp; - uint16_t _port; String _domainName; - unsigned char _resolvedIP[4]; + IPAddress _dns; + std::unique_ptr _que; uint32_t _ttl; +#ifdef DEBUG_DNSSERVER + // There are 2 possiblities for OverFlow: + // 1) we have more than kDNSSQueSize request already outstanding. + // 2) we have request that never received a reply. + uint32_t _que_ov; + uint32_t _que_drop; +#endif DNSReplyCode _errorReplyCode; + bool _forwarder; + unsigned char _resolvedIP[4]; + uint16_t _port; void downcaseAndRemoveWwwPrefix(String &domainName); void replyWithIP(DNSHeader *dnsHeader, @@ -81,7 +136,9 @@ class DNSServer size_t queryLength); void replyWithError(DNSHeader *dnsHeader, DNSReplyCode rcode); - void respondToRequest(uint8_t *buffer, size_t length); + bool respondToRequest(uint8_t *buffer, size_t length); + void forwardRequest(uint8_t *buffer, size_t length); + void forwardReply(uint8_t *buffer, size_t length); void writeNBOShort(uint16_t value); }; #endif diff --git a/libraries/ESP8266HTTPClient/examples/BasicHttpClient/BasicHttpClient.ino b/libraries/ESP8266HTTPClient/examples/BasicHttpClient/BasicHttpClient.ino index 8d4f9c3015..4dc96622ba 100644 --- a/libraries/ESP8266HTTPClient/examples/BasicHttpClient/BasicHttpClient.ino +++ b/libraries/ESP8266HTTPClient/examples/BasicHttpClient/BasicHttpClient.ino @@ -67,7 +67,7 @@ void loop() { http.end(); } else { - Serial.printf("[HTTP} Unable to connect\n"); + Serial.println("[HTTP] Unable to connect"); } } diff --git a/libraries/ESP8266HTTPClient/examples/BasicHttpsClient/BasicHttpsClient.ino b/libraries/ESP8266HTTPClient/examples/BasicHttpsClient/BasicHttpsClient.ino index b8c4c5b328..0e174c2790 100644 --- a/libraries/ESP8266HTTPClient/examples/BasicHttpsClient/BasicHttpsClient.ino +++ b/libraries/ESP8266HTTPClient/examples/BasicHttpsClient/BasicHttpsClient.ino @@ -9,12 +9,15 @@ #include #include - #include - #include -// Fingerprint for demo URL, expires on June 2, 2021, needs to be updated well before this date -const uint8_t fingerprint[20] = { 0x40, 0xaf, 0x00, 0x6b, 0xec, 0x90, 0x22, 0x41, 0x8e, 0xa3, 0xad, 0xfa, 0x1a, 0xe8, 0x25, 0x41, 0x1d, 0x1a, 0x54, 0xb3 }; + +#include "certs.h" + +#ifndef STASSID +#define STASSID "your-ssid" +#define STAPSK "your-password" +#endif ESP8266WiFiMulti WiFiMulti; @@ -27,30 +30,30 @@ void setup() { Serial.println(); Serial.println(); - for (uint8_t t = 4; t > 0; t--) { - Serial.printf("[SETUP] WAIT %d...\n", t); - Serial.flush(); - delay(1000); - } - WiFi.mode(WIFI_STA); - WiFiMulti.addAP("SSID", "PASSWORD"); + WiFiMulti.addAP(STASSID, STAPSK); + Serial.println("setup() done connecting to ssid '" STASSID "'"); } 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->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 - client->setFingerprint(fingerprint); - // Or, if you happy to ignore the SSL certificate, then use the following line instead: + // Or, if you are *absolutely* sure it is ok to ignore the SSL certificate: // client->setInsecure(); HTTPClient https; Serial.print("[HTTPS] begin...\n"); - if (https.begin(*client, "https://jigsaw.w3.org/HTTP/connection.html")) { // HTTPS + if (https.begin(*client, jigsaw_host, jigsaw_port)) { // HTTPS Serial.print("[HTTPS] GET...\n"); // start connection and send HTTP header @@ -64,6 +67,7 @@ void loop() { // file found at server if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) { String payload = https.getString(); + // String payload = https.getString(1024); // optionally pre-reserve string to avoid reallocations in chunk mode Serial.println(payload); } } else { diff --git a/libraries/ESP8266HTTPClient/examples/BasicHttpsClient/certUpdate b/libraries/ESP8266HTTPClient/examples/BasicHttpsClient/certUpdate new file mode 100755 index 0000000000..71b036dc85 --- /dev/null +++ b/libraries/ESP8266HTTPClient/examples/BasicHttpsClient/certUpdate @@ -0,0 +1,2 @@ +cd ${0%/*} 2>/dev/null +python3 ../../../../tools/cert.py -s jigsaw.w3.org -n jigsaw > certs.h diff --git a/libraries/ESP8266HTTPClient/examples/BasicHttpsClient/certs.h b/libraries/ESP8266HTTPClient/examples/BasicHttpsClient/certs.h new file mode 100644 index 0000000000..76451ff76a --- /dev/null +++ b/libraries/ESP8266HTTPClient/examples/BasicHttpsClient/certs.h @@ -0,0 +1,58 @@ + +// 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:21 +// by ['../../../../tools/cert.py', '-s', 'jigsaw.w3.org', '-n', 'jigsaw'] + +#pragma once + +//////////////////////////////////////////////////////////// +// certificate chain for jigsaw.w3.org:443 + +const char* jigsaw_host = "jigsaw.w3.org"; +const uint16_t jigsaw_port = 443; + +// 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----- +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+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 +MQswCQYDVQQGEwJJRTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJl +clRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTIw +MDEyNzEyNDgwOFoXDTI0MTIzMTIzNTk1OVowSjELMAkGA1UEBhMCVVMxGTAXBgNV +BAoTEENsb3VkZmxhcmUsIEluYy4xIDAeBgNVBAMTF0Nsb3VkZmxhcmUgSW5jIEVD +QyBDQS0zMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEua1NZpkUC0bsH4HRKlAe +nQMVLzQSfS2WuIg4m4Vfj7+7Te9hRsTJc9QkT+DuHM5ss1FxL2ruTAUJd9NyYqSb +16OCAWgwggFkMB0GA1UdDgQWBBSlzjfq67B1DpRniLRF+tkkEIeWHzAfBgNVHSME +GDAWgBTlnVkwgkdYzKz6CFQ2hns6tQRN8DAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0l +BBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBIGA1UdEwEB/wQIMAYBAf8CAQAwNAYI +KwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5j +b20wOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL09t +bmlyb290MjAyNS5jcmwwbQYDVR0gBGYwZDA3BglghkgBhv1sAQEwKjAoBggrBgEF +BQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzALBglghkgBhv1sAQIw +CAYGZ4EMAQIBMAgGBmeBDAECAjAIBgZngQwBAgMwDQYJKoZIhvcNAQELBQADggEB +AAUkHd0bsCrrmNaF4zlNXmtXnYJX/OvoMaJXkGUFvhZEOFp3ArnPEELG4ZKk40Un ++ABHLGioVplTVI+tnkDB0A+21w0LOEhsUCxJkAZbZB2LzEgwLt4I4ptJIsCSDBFe +lpKU1fwg3FZs5ZKTv3ocwDfjhUkV+ivhdDkYD7fa86JXWGBPzI6UAPxGezQxPk1H +goE6y/SJXQ7vTQ1unBuCJN0yJV0ReFEQPaA1IwQvZW+cwdFD19Ae8zFnWSfda9J1 +CZMRJCQUzym+5iPDuI9yP+kHyCREU3qzuWFloUwOxkgAyXVjBYdwRVKD05WdRerw +6DEdfgkfCv4+3ao8XnTSrLE= +-----END CERTIFICATE----- +)CERT"; + +// end of certificate chain for jigsaw.w3.org:443 +//////////////////////////////////////////////////////////// + diff --git a/libraries/ESP8266HTTPClient/examples/PostHttpClient/PostHttpClient.ino b/libraries/ESP8266HTTPClient/examples/PostHttpClient/PostHttpClient.ino index 72d51142c7..bb248d35ed 100644 --- a/libraries/ESP8266HTTPClient/examples/PostHttpClient/PostHttpClient.ino +++ b/libraries/ESP8266HTTPClient/examples/PostHttpClient/PostHttpClient.ino @@ -15,7 +15,7 @@ bin/PostServer/PostServer then put your PC's IP address in SERVER_IP below, port 9080 (instead of default 80): */ -//#define SERVER_IP "10.0.1.7:9080" // PC address with emulation on host +// #define SERVER_IP "10.0.1.7:9080" // PC address with emulation on host #define SERVER_IP "192.168.1.42" #ifndef STASSID 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/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp index 89da711f32..480699e453 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp @@ -39,7 +39,7 @@ static_assert(std::is_move_assignable_v, ""); static const char defaultUserAgentPstr[] PROGMEM = "ESP8266HTTPClient"; const String HTTPClient::defaultUserAgent = defaultUserAgentPstr; -static int StreamReportToHttpClientReport (Stream::Report streamSendError) +int HTTPClient::StreamReportToHttpClientReport (Stream::Report streamSendError) { switch (streamSendError) { @@ -107,7 +107,7 @@ bool HTTPClient::begin(WiFiClient &client, const String& host, uint16_t port, co _canReuse = false; disconnect(true); } - + _client = client.clone(); clear(); @@ -483,7 +483,7 @@ int HTTPClient::sendRequest(const char * type, const uint8_t * payload, size_t s // redirect = false; if ( - _followRedirects != HTTPC_DISABLE_FOLLOW_REDIRECTS && + _followRedirects != HTTPC_DISABLE_FOLLOW_REDIRECTS && redirectCount < _redirectLimit && _location.length() > 0 ) { @@ -496,7 +496,7 @@ int HTTPClient::sendRequest(const char * type, const uint8_t * payload, size_t s // (the RFC require user to accept the redirection) _followRedirects == HTTPC_FORCE_FOLLOW_REDIRECTS || // allow GET and HEAD methods without force - !strcmp(type, "GET") || + !strcmp(type, "GET") || !strcmp(type, "HEAD") ) { redirectCount += 1; @@ -627,110 +627,11 @@ WiFiClient* HTTPClient::getStreamPtr(void) return nullptr; } -/** - * write all message body / payload to Stream - * @param stream Stream * - * @return bytes written ( negative values are error codes ) - */ -int HTTPClient::writeToStream(Stream * stream) -{ - return writeToPrint(stream); -} - -/** - * write all message body / payload to Print - * @param print Print * - * @return bytes written ( negative values are error codes ) - */ -int HTTPClient::writeToPrint(Print * print) -{ - - if(!print) { - return returnError(HTTPC_ERROR_NO_STREAM); - } - - // Only return error if not connected and no data available, because otherwise ::getString() will return an error instead of an empty - // string when the server returned a http code 204 (no content) - if(!connected() && _transferEncoding != HTTPC_TE_IDENTITY && _size > 0) { - return returnError(HTTPC_ERROR_NOT_CONNECTED); - } - - // get length of document (is -1 when Server sends no Content-Length header) - int len = _size; - int ret = 0; - - if(_transferEncoding == HTTPC_TE_IDENTITY) { - // len < 0: transfer all of it, with timeout - // len >= 0: max:len, with timeout - ret = _client->sendSize(print, len); - - // do we have an error? - if(_client->getLastSendReport() != Stream::Report::Success) { - return returnError(StreamReportToHttpClientReport(_client->getLastSendReport())); - } - } else if(_transferEncoding == HTTPC_TE_CHUNKED) { - int size = 0; - while(1) { - if(!connected()) { - return returnError(HTTPC_ERROR_CONNECTION_LOST); - } - String chunkHeader = _client->readStringUntil('\n'); - - if(chunkHeader.length() <= 0) { - return returnError(HTTPC_ERROR_READ_TIMEOUT); - } - - chunkHeader.trim(); // remove \r - - // read size of chunk - len = (uint32_t) strtol((const char *) chunkHeader.c_str(), NULL, 16); - size += len; - DEBUG_HTTPCLIENT("[HTTP-Client] read chunk len: %d\n", len); - - // data left? - if(len > 0) { - // read len bytes with timeout - int r = _client->sendSize(print, len); - if (_client->getLastSendReport() != Stream::Report::Success) - // not all data transferred - return returnError(StreamReportToHttpClientReport(_client->getLastSendReport())); - ret += r; - } else { - - // if no length Header use global chunk size - if(_size <= 0) { - _size = size; - } - - // check if we have write all data out - if(ret != _size) { - return returnError(HTTPC_ERROR_STREAM_WRITE); - } - break; - } - - // read trailing \r\n at the end of the chunk - char buf[2]; - auto trailing_seq_len = _client->readBytes((uint8_t*)buf, 2); - if (trailing_seq_len != 2 || buf[0] != '\r' || buf[1] != '\n') { - return returnError(HTTPC_ERROR_READ_TIMEOUT); - } - - esp_yield(); - } - } else { - return returnError(HTTPC_ERROR_ENCODING); - } - - disconnect(true); - return ret; -} - /** * return all payload as String (may need lot of ram or trigger out of memory!) * @return String */ -const String& HTTPClient::getString(void) +const String& HTTPClient::getString(int reserve) { if (_payload) { return *_payload; @@ -738,12 +639,12 @@ const String& HTTPClient::getString(void) _payload.reset(new StreamString()); - if(_size > 0) { - // try to reserve needed memory - if(!_payload->reserve((_size + 1))) { - DEBUG_HTTPCLIENT("[HTTP-Client][getString] not enough memory to reserve a string! need: %d\n", (_size + 1)); - return *_payload; - } + if (_size > 0 && _size > reserve) + reserve = _size; + + if (reserve > 0 && !_payload->reserve(reserve)) { + DEBUG_HTTPCLIENT("[HTTP-Client][getString] not enough memory to reserve a string! need: %d\n", reserve); + return *_payload; } writeToStream(_payload.get()); @@ -831,30 +732,30 @@ void HTTPClient::collectHeaders(const char* headerKeys[], const size_t headerKey } } -String HTTPClient::header(const char* name) +const String& HTTPClient::header(const char* name) { for(size_t i = 0; i < _headerKeysCount; ++i) { if(_currentHeaders[i].key == name) { return _currentHeaders[i].value; } } - return String(); + return emptyString; } -String HTTPClient::header(size_t i) +const String& HTTPClient::header(size_t i) { if(i < _headerKeysCount) { return _currentHeaders[i].value; } - return String(); + return emptyString; } -String HTTPClient::headerName(size_t i) +const String& HTTPClient::headerName(size_t i) { if(i < _headerKeysCount) { return _currentHeaders[i].key; } - return String(); + return emptyString; } int HTTPClient::headers() diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h index 9e3aef9ecd..bc8a42d33e 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h @@ -203,9 +203,9 @@ class HTTPClient /// Response handling void collectHeaders(const char* headerKeys[], const size_t headerKeysCount); - String header(const char* name); // get request header value by name - String header(size_t i); // get request header value by number - String headerName(size_t i); // get request header name by number + const String& header(const char* name); // get request header value by name + const String& header(size_t i); // get request header value by number + const String& headerName(size_t i); // get request header name by number int headers(); // get header count bool hasHeader(const char* name); // check if header exists @@ -215,9 +215,15 @@ class HTTPClient WiFiClient& getStream(void); WiFiClient* getStreamPtr(void); - int writeToPrint(Print* print); - int writeToStream(Stream* stream); - const String& getString(void); + template int writeToPrint(S* print) [[deprecated]] { return writeToStream(print); } + template int writeToStream(S* output); + + // In case of chunks = when size cannot be known in advance + // by the library, it might be useful to pre-reserve enough + // space instead of offending memory with a growing String + const String& getString() { return getString(0); } + const String& getString(int reserve); + static String errorToString(int error); protected: @@ -234,6 +240,7 @@ class HTTPClient bool sendHeader(const char * type); int handleHeaderResponse(); int writeToStreamDataBlock(Stream * stream, int len); + static int StreamReportToHttpClientReport (Stream::Report streamSendError); // The common pattern to use the class is to // { @@ -274,4 +281,93 @@ class HTTPClient std::unique_ptr _payload; }; +/** + * write all message body / payload to Stream + * @param output Print*(obsolete) / Stream* + * @return bytes written ( negative values are error codes ) + */ +template +int HTTPClient::writeToStream(S * output) +{ + if(!output) { + return returnError(HTTPC_ERROR_NO_STREAM); + } + + // Only return error if not connected and no data available, because otherwise ::getString() will return an error instead of an empty + // string when the server returned a http code 204 (no content) + if(!connected() && _transferEncoding != HTTPC_TE_IDENTITY && _size > 0) { + return returnError(HTTPC_ERROR_NOT_CONNECTED); + } + + // get length of document (is -1 when Server sends no Content-Length header) + int len = _size; + int ret = 0; + + if(_transferEncoding == HTTPC_TE_IDENTITY) { + // len < 0: transfer all of it, with timeout + // len >= 0: max:len, with timeout + ret = _client->sendSize(output, len); + + // do we have an error? + if(_client->getLastSendReport() != Stream::Report::Success) { + return returnError(StreamReportToHttpClientReport(_client->getLastSendReport())); + } + } else if(_transferEncoding == HTTPC_TE_CHUNKED) { + int size = 0; + while(1) { + if(!connected()) { + return returnError(HTTPC_ERROR_CONNECTION_LOST); + } + String chunkHeader = _client->readStringUntil('\n'); + + if(chunkHeader.length() <= 0) { + return returnError(HTTPC_ERROR_READ_TIMEOUT); + } + + chunkHeader.trim(); // remove \r + + // read size of chunk + len = (uint32_t) strtol((const char *) chunkHeader.c_str(), NULL, 16); + size += len; + DEBUG_HTTPCLIENT("[HTTP-Client] read chunk len: %d\n", len); + + // data left? + if(len > 0) { + // read len bytes with timeout + int r = _client->sendSize(output, len); + if (_client->getLastSendReport() != Stream::Report::Success) + // not all data transferred + return returnError(StreamReportToHttpClientReport(_client->getLastSendReport())); + ret += r; + } else { + + // if no length Header use global chunk size + if(_size <= 0) { + _size = size; + } + + // check if we have write all data out + if(ret != _size) { + return returnError(HTTPC_ERROR_STREAM_WRITE); + } + break; + } + + // read trailing \r\n at the end of the chunk + char buf[2]; + auto trailing_seq_len = _client->readBytes((uint8_t*)buf, 2); + if (trailing_seq_len != 2 || buf[0] != '\r' || buf[1] != '\n') { + return returnError(HTTPC_ERROR_READ_TIMEOUT); + } + + esp_yield(); + } + } else { + return returnError(HTTPC_ERROR_ENCODING); + } + + disconnect(true); + return ret; +} + #endif /* ESP8266HTTPClient_H_ */ diff --git a/libraries/ESP8266HTTPUpdateServer/src/ESP8266HTTPUpdateServer-impl.h b/libraries/ESP8266HTTPUpdateServer/src/ESP8266HTTPUpdateServer-impl.h index 49bb87f28c..570ecfbb13 100644 --- a/libraries/ESP8266HTTPUpdateServer/src/ESP8266HTTPUpdateServer-impl.h +++ b/libraries/ESP8266HTTPUpdateServer/src/ESP8266HTTPUpdateServer-impl.h @@ -59,8 +59,24 @@ void ESP8266HTTPUpdateServerTemplate::setup(ESP8266WebServerTemplate _server->send_P(200, PSTR("text/html"), serverIndex); }); + // handler for the /update form page - preflight options + _server->on(path.c_str(), HTTP_OPTIONS, [&](){ + _server->sendHeader("Access-Control-Allow-Headers", "*"); + _server->sendHeader("Access-Control-Allow-Origin", "*"); + _server->send(200, F("text/html"), String(F("y"))); + },[&](){ + _authenticated = (_username == emptyString || _password == emptyString || _server->authenticate(_username.c_str(), _password.c_str())); + if(!_authenticated){ + if (_serial_output) + Serial.printf("Unauthenticated Update\n"); + return; + } + }); + // handler for the /update form POST (once file upload finishes) _server->on(path.c_str(), HTTP_POST, [&](){ + _server->sendHeader("Access-Control-Allow-Headers", "*"); + _server->sendHeader("Access-Control-Allow-Origin", "*"); if(!_authenticated) return _server->requestAuthentication(); if (Update.hasError()) { @@ -89,7 +105,6 @@ void ESP8266HTTPUpdateServerTemplate::setup(ESP8266WebServerTemplate return; } - WiFiUDP::stopAll(); if (_serial_output) Serial.printf("Update: %s\n", upload.filename.c_str()); if (upload.name == "filesystem") { diff --git a/libraries/ESP8266NetBIOS/ESP8266NetBIOS.cpp b/libraries/ESP8266NetBIOS/ESP8266NetBIOS.cpp index 1219cfb32a..3b4b8c6afc 100644 --- a/libraries/ESP8266NetBIOS/ESP8266NetBIOS.cpp +++ b/libraries/ESP8266NetBIOS/ESP8266NetBIOS.cpp @@ -212,8 +212,7 @@ void ESP8266NetBIOS::_recv(udp_pcb *upcb, pbuf *pb, const ip_addr_t *addr, uint1 nbnsa.NBNSA_TIMETOLIVE = LWIP_PLATFORM_HTONL(300000UL);// Time to live (30000 sekund) nbnsa.NBNSA_LENGTH = LWIP_PLATFORM_HTONS(6); nbnsa.NBNSA_NODEFLAGS = LWIP_PLATFORM_HTONS(0); - nbnsa.NBNSA_NODEADDRESS = WiFi.localIP(); // ulozime nasi IP adresu - + nbnsa.NBNSA_NODEADDRESS = ip_addr_get_ip4_u32(&ip_current_netif()->ip_addr); pbuf* pbt = pbuf_alloc(PBUF_TRANSPORT, sizeof(nbnsa), PBUF_RAM); if(pbt != NULL) { uint8_t* dst = reinterpret_cast(pbt->payload); diff --git a/libraries/ESP8266SdFat b/libraries/ESP8266SdFat index 43a65b42d5..eaab1369d5 160000 --- a/libraries/ESP8266SdFat +++ b/libraries/ESP8266SdFat @@ -1 +1 @@ -Subproject commit 43a65b42d5a827092188cfe11fb27237da252692 +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/AdvancedWebServer/AdvancedWebServer.ino b/libraries/ESP8266WebServer/examples/AdvancedWebServer/AdvancedWebServer.ino index 36511917db..b96f977373 100644 --- a/libraries/ESP8266WebServer/examples/AdvancedWebServer/AdvancedWebServer.ino +++ b/libraries/ESP8266WebServer/examples/AdvancedWebServer/AdvancedWebServer.ino @@ -32,6 +32,7 @@ #include #include #include +#include #ifndef STASSID #define STASSID "your-ssid" @@ -47,14 +48,14 @@ const int led = 13; void handleRoot() { digitalWrite(led, 1); - char temp[400]; int sec = millis() / 1000; int min = sec / 60; int hr = min / 60; - snprintf(temp, 400, - - "\ + StreamString temp; + temp.reserve(500); // Preallocate a large chunk to avoid memory fragmentation + temp.printf("\ +\ \ \ ESP8266 Demo\ @@ -68,9 +69,8 @@ void handleRoot() { \ \ ", - - hr, min % 60, sec % 60); - server.send(200, "text/html", temp); + hr, min % 60, sec % 60); + server.send(200, "text/html", temp.c_str()); digitalWrite(led, 0); } diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index 5a0937df0c..5469f48184 100644 --- a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino +++ b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino @@ -25,15 +25,15 @@ // Select the FileSystem by uncommenting one of the lines below -//#define USE_SPIFFS +// #define USE_SPIFFS #define USE_LITTLEFS -//#define USE_SDFS +// #define USE_SDFS // Uncomment the following line to embed a version of the web page in the code // (program code will be larger, but no file will have to be written to the filesystem). // Note: the source file "extras/index_htm.h" must have been generated by "extras/reduce_index.sh" -//#define INCLUDE_FALLBACK_INDEX_HTM +// #define INCLUDE_FALLBACK_INDEX_HTM //////////////////////////////// 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/Graph/Graph.ino b/libraries/ESP8266WebServer/examples/Graph/Graph.ino index 0d6f724bae..224eccf0fb 100644 --- a/libraries/ESP8266WebServer/examples/Graph/Graph.ino +++ b/libraries/ESP8266WebServer/examples/Graph/Graph.ino @@ -24,9 +24,9 @@ // Select the FileSystem by uncommenting one of the lines below -//#define USE_SPIFFS +// #define USE_SPIFFS #define USE_LITTLEFS -//#define USE_SDFS +// #define USE_SDFS //////////////////////////////// diff --git a/libraries/ESP8266WebServer/examples/HelloServer/HelloServer.ino b/libraries/ESP8266WebServer/examples/HelloServer/HelloServer.ino index 16b6ac20f0..105ce6e947 100644 --- a/libraries/ESP8266WebServer/examples/HelloServer/HelloServer.ino +++ b/libraries/ESP8266WebServer/examples/HelloServer/HelloServer.ino @@ -13,6 +13,8 @@ const char* password = STAPSK; ESP8266WebServer server(80); +String bigChunk; + const int led = 13; void handleRoot() { @@ -36,6 +38,16 @@ void handleNotFound() { digitalWrite(led, 0); } +void handleChunked() { + server.chunkedResponseModeStart(200, F("text/html")); + + server.sendContent(bigChunk); + server.sendContent(F("chunk 2")); + server.sendContent(bigChunk); + + server.chunkedResponseFinalize(); +} + void setup(void) { pinMode(led, OUTPUT); digitalWrite(led, 0); @@ -80,6 +92,8 @@ void setup(void) { server.send(200, "image/gif", gif_colored, sizeof(gif_colored)); }); + server.on("/chunks", handleChunked); + server.onNotFound(handleNotFound); ///////////////////////////////////////////////////////// @@ -142,6 +156,15 @@ void setup(void) { // Hook examples ///////////////////////////////////////////////////////// + // prepare chunk in ram for sending + constexpr int chunkLen = 4000; // ~4KB chunk + bigChunk.reserve(chunkLen); + bigChunk = F("chunk of len "); + bigChunk += chunkLen; + String piece = F("-blah"); + while (bigChunk.length() < chunkLen - piece.length()) + bigChunk += piece; + server.begin(); Serial.println("HTTP server started"); } diff --git a/libraries/ESP8266WebServer/examples/HelloServerBearSSL/HelloServerBearSSL.ino b/libraries/ESP8266WebServer/examples/HelloServerBearSSL/HelloServerBearSSL.ino index 14dc1d3417..db93a80021 100644 --- a/libraries/ESP8266WebServer/examples/HelloServerBearSSL/HelloServerBearSSL.ino +++ b/libraries/ESP8266WebServer/examples/HelloServerBearSSL/HelloServerBearSSL.ino @@ -27,59 +27,10 @@ const char* password = STAPSK; BearSSL::ESP8266WebServerSecure server(443); BearSSL::ServerSessions serverCache(5); -static const char serverCert[] PROGMEM = R"EOF( ------BEGIN CERTIFICATE----- -MIIDSzCCAjMCCQD2ahcfZAwXxDANBgkqhkiG9w0BAQsFADCBiTELMAkGA1UEBhMC -VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU9yYW5nZSBDb3VudHkx -EDAOBgNVBAoMB1ByaXZhZG8xGjAYBgNVBAMMEXNlcnZlci56bGFiZWwuY29tMR8w -HQYJKoZIhvcNAQkBFhBlYXJsZUB6bGFiZWwuY29tMB4XDTE4MDMwNjA1NDg0NFoX -DTE5MDMwNjA1NDg0NFowRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3Rh -dGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBAPVKBwbZ+KDSl40YCDkP6y8Sv4iNGvEOZg8Y -X7sGvf/xZH7UiCBWPFIRpNmDSaZ3yjsmFqm6sLiYSGSdrBCFqdt9NTp2r7hga6Sj -oASSZY4B9pf+GblDy5m10KDx90BFKXdPMCLT+o76Nx9PpCvw13A848wHNG3bpBgI -t+w/vJCX3bkRn8yEYAU6GdMbYe7v446hX3kY5UmgeJFr9xz1kq6AzYrMt/UHhNzO -S+QckJaY0OGWvmTNspY3xCbbFtIDkCdBS8CZAw+itnofvnWWKQEXlt6otPh5njwy -+O1t/Q+Z7OMDYQaH02IQx3188/kW3FzOY32knER1uzjmRO+jhA8CAwEAATANBgkq -hkiG9w0BAQsFAAOCAQEAnDrROGRETB0woIcI1+acY1yRq4yAcH2/hdq2MoM+DCyM -E8CJaOznGR9ND0ImWpTZqomHOUkOBpvu7u315blQZcLbL1LfHJGRTCHVhvVrcyEb -fWTnRtAQdlirUm/obwXIitoz64VSbIVzcqqfg9C6ZREB9JbEX98/9Wp2gVY+31oC -JfUvYadSYxh3nblvA4OL+iEZiW8NE3hbW6WPXxvS7Euge0uWMPc4uEcnsE0ZVG3m -+TGimzSdeWDvGBRWZHXczC2zD4aoE5vrl+GD2i++c6yjL/otHfYyUpzUfbI2hMAA -5tAF1D5vAAwA8nfPysumlLsIjohJZo4lgnhB++AlOg== ------END CERTIFICATE----- -)EOF"; - -static const char serverKey[] PROGMEM = R"EOF( ------BEGIN RSA PRIVATE KEY----- -MIIEpQIBAAKCAQEA9UoHBtn4oNKXjRgIOQ/rLxK/iI0a8Q5mDxhfuwa9//FkftSI -IFY8UhGk2YNJpnfKOyYWqbqwuJhIZJ2sEIWp2301OnavuGBrpKOgBJJljgH2l/4Z -uUPLmbXQoPH3QEUpd08wItP6jvo3H0+kK/DXcDzjzAc0bdukGAi37D+8kJfduRGf -zIRgBToZ0xth7u/jjqFfeRjlSaB4kWv3HPWSroDNisy39QeE3M5L5ByQlpjQ4Za+ -ZM2yljfEJtsW0gOQJ0FLwJkDD6K2eh++dZYpAReW3qi0+HmePDL47W39D5ns4wNh -BofTYhDHfXzz+RbcXM5jfaScRHW7OOZE76OEDwIDAQABAoIBAQDKov5NFbNFQNR8 -djcM1O7Is6dRaqiwLeH4ZH1pZ3d9QnFwKanPdQ5eCj9yhfhJMrr5xEyCqT0nMn7T -yEIGYDXjontfsf8WxWkH2TjvrfWBrHOIOx4LJEvFzyLsYxiMmtZXvy6YByD+Dw2M -q2GH/24rRdI2klkozIOyazluTXU8yOsSGxHr/aOa9/sZISgLmaGOOuKI/3Zqjdhr -eHeSqoQFt3xXa8jw01YubQUDw/4cv9rk2ytTdAoQUimiKtgtjsggpP1LTq4xcuqN -d4jWhTcnorWpbD2cVLxrEbnSR3VuBCJEZv5axg5ZPxLEnlcId8vMtvTRb5nzzszn -geYUWDPhAoGBAPyKVNqqwQl44oIeiuRM2FYenMt4voVaz3ExJX2JysrG0jtCPv+Y -84R6Cv3nfITz3EZDWp5sW3OwoGr77lF7Tv9tD6BptEmgBeuca3SHIdhG2MR+tLyx -/tkIAarxQcTGsZaSqra3gXOJCMz9h2P5dxpdU+0yeMmOEnAqgQ8qtNBfAoGBAPim -RAtnrd0WSlCgqVGYFCvDh1kD5QTNbZc+1PcBHbVV45EmJ2fLXnlDeplIZJdYxmzu -DMOxZBYgfeLY9exje00eZJNSj/csjJQqiRftrbvYY7m5njX1kM5K8x4HlynQTDkg -rtKO0YZJxxmjRTbFGMegh1SLlFLRIMtehNhOgipRAoGBAPnEEpJGCS9GGLfaX0HW -YqwiEK8Il12q57mqgsq7ag7NPwWOymHesxHV5mMh/Dw+NyBi4xAGWRh9mtrUmeqK -iyICik773Gxo0RIqnPgd4jJWN3N3YWeynzulOIkJnSNx5BforOCTc3uCD2s2YB5X -jx1LKoNQxLeLRN8cmpIWicf/AoGBANjRSsZTKwV9WWIDJoHyxav/vPb+8WYFp8lZ -zaRxQbGM6nn4NiZI7OF62N3uhWB/1c7IqTK/bVHqFTuJCrCNcsgld3gLZ2QWYaMV -kCPgaj1BjHw4AmB0+EcajfKilcqtSroJ6MfMJ6IclVOizkjbByeTsE4lxDmPCDSt -/9MKanBxAoGAY9xo741Pn9WUxDyRplww606ccdNf/ksHWNc/Y2B5SPwxxSnIq8nO -j01SmsCUYVFAgZVOTiiycakjYLzxlc6p8BxSVqy6LlJqn95N8OXoQ+bkwUux/ekg -gz5JWYhbD6c38khSzJb0pNXCo3EuYAVa36kDM96k1BtWuhRS10Q1VXk= ------END RSA PRIVATE KEY----- -)EOF"; +#define USING_INSECURE_CERTS_AND_KEYS_AND_CAS 1 +#include +String bigChunk; const int led = 13; @@ -104,6 +55,16 @@ void handleNotFound() { digitalWrite(led, 0); } +void handleChunked() { + server.chunkedResponseModeStart(200, F("text/html")); + + server.sendContent(bigChunk); + server.sendContent(F("chunk 2")); + server.sendContent(bigChunk); + + server.chunkedResponseFinalize(); +} + void setup(void) { pinMode(led, OUTPUT); digitalWrite(led, 0); @@ -127,7 +88,7 @@ void setup(void) { if (MDNS.begin("esp8266")) { Serial.println("MDNS responder started"); } - server.getServer().setRSACert(new BearSSL::X509List(serverCert), new BearSSL::PrivateKey(serverKey)); + server.getServer().setRSACert(new BearSSL::X509List(server_cert), new BearSSL::PrivateKey(server_private_key)); // Cache SSL sessions to accelerate the TLS handshake. server.getServer().setCache(&serverCache); @@ -138,8 +99,19 @@ void setup(void) { server.send(200, "text/plain", "this works as well"); }); + server.on("/chunks", handleChunked); + server.onNotFound(handleNotFound); + // prepare chunk in ram for sending + constexpr int chunkLen = 4000; // ~4KB chunk + bigChunk.reserve(chunkLen); + bigChunk = F("chunk of len "); + bigChunk += chunkLen; + String piece = F("-blah"); + while (bigChunk.length() < chunkLen - piece.length()) + bigChunk += piece; + server.begin(); Serial.println("HTTPS server started"); } diff --git a/libraries/ESP8266WebServer/examples/ServerSentEvents/ServerSentEvents.ino b/libraries/ESP8266WebServer/examples/ServerSentEvents/ServerSentEvents.ino index 7c462295cd..ced3548b3c 100644 --- a/libraries/ESP8266WebServer/examples/ServerSentEvents/ServerSentEvents.ino +++ b/libraries/ESP8266WebServer/examples/ServerSentEvents/ServerSentEvents.ino @@ -146,7 +146,9 @@ void updateSensor(sensorType &sensor) { SSEBroadcastState(sensor.name, sensor.value, newVal); // only broadcast if state is different } sensor.value = newVal; - sensor.update.once(rand() % 20 + 10, std::bind(updateSensor, sensor)); // randomly update sensor + sensor.update.once(rand() % 20 + 10, [&]() { + updateSensor(sensor); + }); // randomly update sensor } void handleSubscribe() { 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 6ae7b77938..dbcb251135 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h @@ -102,31 +102,31 @@ bool ESP8266WebServerTemplate::authenticate(const char * username, c if(authReq.startsWith(F("Basic"))){ authReq = authReq.substring(6); authReq.trim(); - char toencodeLen = strlen(username)+strlen(password)+1; - char *toencode = new (std::nothrow) char[toencodeLen + 1]; - if(toencode == NULL){ - authReq = ""; + + const size_t username_len = strlen(username); + const size_t password_len = strlen(password); + + String raw; + raw.reserve(username_len + password_len + 1); + raw.concat(username, username_len); + raw += ':'; + raw.concat(password, password_len); + if(!raw.length()) { return false; } - sprintf(toencode, "%s:%s", username, password); - String encoded = base64::encode((uint8_t *)toencode, toencodeLen, false); - if(!encoded){ - authReq = ""; - delete[] toencode; + + String encoded = base64::encode(raw, false); + if(!encoded.length()){ return false; } if(authReq.equalsConstantTime(encoded)) { - authReq = ""; - delete[] toencode; return true; } - delete[] toencode; } else if(authReq.startsWith(F("Digest"))) { String _realm = _extractParam(authReq, F("realm=\"")); - String _H1 = credentialHash((String)username,_realm,(String)password); - return authenticateDigest((String)username,_H1); + String _H1 = credentialHash(username,_realm,password); + return authenticateDigest(username,_H1); } - authReq = ""; } return false; } @@ -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 +RequestHandler& ESP8266WebServerTemplate::on(const Uri &uri, HTTPMethod method, ESP8266WebServerTemplate::THandlerFunction fn) { + return on(uri, method, fn, _fileUploadHandler); +} + +template +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 -void ESP8266WebServerTemplate::on(const Uri &uri, HTTPMethod method, ESP8266WebServerTemplate::THandlerFunction fn) { - on(uri, method, fn, _fileUploadHandler); +bool ESP8266WebServerTemplate::removeRoute(const char *uri, HTTPMethod method) { + return removeRoute(String(uri), method); } template -void ESP8266WebServerTemplate::on(const Uri &uri, HTTPMethod method, ESP8266WebServerTemplate::THandlerFunction fn, ESP8266WebServerTemplate::THandlerFunction ufn) { - _addRequestHandler(new FunctionRequestHandler(fn, ufn, uri, method)); +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; @@ -281,14 +356,13 @@ void ESP8266WebServerTemplate::serveStatic(const char* uri, FS& fs, template void ESP8266WebServerTemplate::handleClient() { if (_currentStatus == HC_NONE) { - ClientType client = _server.accept(); - if (!client) { + _currentClient = _server.accept(); + if (!_currentClient) { return; } DBGWS("New client\n"); - _currentClient = client; _currentStatus = HC_WAIT_READ; _statusChange = millis(); } @@ -296,12 +370,35 @@ void ESP8266WebServerTemplate::handleClient() { bool keepCurrentClient = false; bool callYield = false; - DBGWS("http-server loop: conn=%d avail=%d status=%s\n", - _currentClient.connected(), _currentClient.available(), - _currentStatus==HC_NONE?"none": - _currentStatus==HC_WAIT_READ?"wait-read": - _currentStatus==HC_WAIT_CLOSE?"wait-close": - "??"); +#ifdef DEBUG_ESP_HTTP_SERVER + + struct compare_s + { + uint8_t connected; + int available; + HTTPClientStatus status; + bool operator != (const compare_s& o) + { + return o.connected != connected + || o.available != available + || o.status != status; + } + }; + static compare_s last { false, 0, HC_NONE }; + compare_s now { _currentClient.connected(), _currentClient.available(), _currentStatus }; + + if (last != now) + { + DBGWS("http-server loop: conn=%d avail=%d status=%s\n", + _currentClient.connected(), _currentClient.available(), + _currentStatus==HC_NONE?"none": + _currentStatus==HC_WAIT_READ?"wait-read": + _currentStatus==HC_WAIT_CLOSE?"wait-close": + "??"); + last = now; + } + +#endif // DEBUG_ESP_HTTP_SERVER if (_currentClient.connected() || _currentClient.available()) { if (_currentClient.available() && _keepAlive) { @@ -568,10 +665,6 @@ const String& ESP8266WebServerTemplate::pathArg(unsigned int i) cons template const String& ESP8266WebServerTemplate::arg(const String& name) const { - for (int j = 0; j < _postArgsLen; ++j) { - if ( _postArgs[j].key == name ) - return _postArgs[j].value; - } for (int i = 0; i < _currentArgCount + _currentArgsHavePlain; ++i) { if ( _currentArgs[i].key == name ) return _currentArgs[i].value; @@ -600,10 +693,6 @@ int ESP8266WebServerTemplate::args() const { template bool ESP8266WebServerTemplate::hasArg(const String& name) const { - for (int j = 0; j < _postArgsLen; ++j) { - if (_postArgs[j].key == name) - return true; - } for (int i = 0; i < _currentArgCount + _currentArgsHavePlain; ++i) { if (_currentArgs[i].key == name) return true; @@ -712,9 +801,11 @@ void ESP8266WebServerTemplate::_handleRequest() { _finalizeResponse(); } _currentUri = ""; + delete[] _currentArgs; + _currentArgs = nullptr; + _currentArgCount = 0; } - template void ESP8266WebServerTemplate::_finalizeResponse() { if (_chunked) { diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.h b/libraries/ESP8266WebServer/src/ESP8266WebServer.h index ae697f4b27..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 @@ -171,7 +177,9 @@ class ESP8266WebServerTemplate void send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength); void send(int code, const char* content_type, Stream* stream, size_t content_length = 0); - void send(int code, const char* content_type, Stream& stream, size_t content_length = 0); + void send(int code, const char* content_type, Stream& stream, size_t content_length = 0) { + send(code, content_type, &stream, content_length); + } void setContentLength(const size_t contentLength); void sendHeader(const String& name, const String& value, bool first = false); @@ -205,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. @@ -280,6 +312,7 @@ class ESP8266WebServerTemplate protected: void _addRequestHandler(RequestHandlerType* handler); + bool _removeRequestHandler(RequestHandlerType *handler); void _handleRequest(); void _finalizeResponse(); ClientFuture _parseRequest(ClientType& client); @@ -321,8 +354,6 @@ class ESP8266WebServerTemplate RequestArgument* _currentArgs = nullptr; int _currentArgsHavePlain = 0; std::unique_ptr _currentUpload; - int _postArgsLen = 0; - RequestArgument* _postArgs = nullptr; int _headerKeysCount = 0; RequestArgument* _currentHeaders = nullptr; @@ -350,4 +381,4 @@ class ESP8266WebServerTemplate using ESP8266WebServer = esp8266webserver::ESP8266WebServerTemplate; using RequestHandler = esp8266webserver::RequestHandler; -#endif //ESP8266WEBSERVER_H \ No newline at end of file +#endif //ESP8266WEBSERVER_H diff --git a/libraries/ESP8266WebServer/src/Parsing-impl.h b/libraries/ESP8266WebServer/src/Parsing-impl.h index 8e4a6d1ae9..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; @@ -182,7 +182,7 @@ typename ESP8266WebServerTemplate::ClientFuture ESP8266WebServerTemp if (!isForm) { if (contentLength) { // add key=value: plain={body} (post json or other data) - RequestArgument& arg = _currentArgs[_currentArgCount++]; + RequestArgument& arg = _currentArgs[_currentArgCount]; arg.key = F("plain"); arg.value = plainBuf; _currentArgsHavePlain = 1; @@ -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; @@ -358,9 +358,8 @@ bool ESP8266WebServerTemplate::_parseForm(ClientType& client, const client.readStringUntil('\n'); //start reading the form if (line == ("--"+boundary)){ - if(_postArgs) delete[] _postArgs; - _postArgs = new RequestArgument[WEBSERVER_MAX_POST_ARGS]; - _postArgsLen = 0; + std::unique_ptr postArgs(new RequestArgument[WEBSERVER_MAX_POST_ARGS]); + int postArgsLen = 0; while(1){ String argName; String argValue; @@ -408,7 +407,7 @@ bool ESP8266WebServerTemplate::_parseForm(ClientType& client, const } DBGWS("PostArg Value: %s\n\n", argValue.c_str()); - RequestArgument& arg = _postArgs[_postArgsLen++]; + RequestArgument& arg = postArgs[postArgsLen++]; arg.key = argName; arg.value = argValue; @@ -426,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; @@ -464,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(), @@ -488,25 +487,20 @@ bool ESP8266WebServerTemplate::_parseForm(ClientType& client, const } int iarg; - int totalArgs = ((WEBSERVER_MAX_POST_ARGS - _postArgsLen) < _currentArgCount)?(WEBSERVER_MAX_POST_ARGS - _postArgsLen):_currentArgCount; + int totalArgs = ((WEBSERVER_MAX_POST_ARGS - postArgsLen) < _currentArgCount)?(WEBSERVER_MAX_POST_ARGS - postArgsLen):_currentArgCount; for (iarg = 0; iarg < totalArgs; iarg++){ - RequestArgument& arg = _postArgs[_postArgsLen++]; + RequestArgument& arg = postArgs[postArgsLen++]; arg.key = _currentArgs[iarg].key; arg.value = _currentArgs[iarg].value; } - if (_currentArgs) delete[] _currentArgs; - _currentArgs = new RequestArgument[_postArgsLen]; - for (iarg = 0; iarg < _postArgsLen; iarg++){ + delete[] _currentArgs; + _currentArgs = new RequestArgument[postArgsLen]; + for (iarg = 0; iarg < postArgsLen; iarg++){ RequestArgument& arg = _currentArgs[iarg]; - arg.key = _postArgs[iarg].key; - arg.value = _postArgs[iarg].value; + arg.key = postArgs[iarg].key; + arg.value = postArgs[iarg].value; } _currentArgCount = iarg; - if (_postArgs) { - delete[] _postArgs; - _postArgs = nullptr; - _postArgsLen = 0; - } return true; } DBGWS("Error: line: %s\n", line.c_str()); @@ -548,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..fb36c5aba4 100644 --- a/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h +++ b/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h @@ -59,9 +59,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 +82,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 +135,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 +149,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 +225,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 +256,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,8 +298,14 @@ 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 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/BearSSL_CertStore.ino b/libraries/ESP8266WiFi/examples/BearSSL_CertStore/BearSSL_CertStore.ino index fd543933b4..f96a4ccf83 100644 --- a/libraries/ESP8266WiFi/examples/BearSSL_CertStore/BearSSL_CertStore.ino +++ b/libraries/ESP8266WiFi/examples/BearSSL_CertStore/BearSSL_CertStore.ino @@ -75,8 +75,7 @@ void fetchURL(BearSSL::WiFiClientSecure *client, const char *host, const uint16_ if (!path) { path = "/"; } Serial.printf("Trying: %s:443...", host); - client->connect(host, port); - if (!client->connected()) { + if (!client->connect(host, port)) { Serial.printf("*** Can't connect. ***\n-------\n"); return; } @@ -88,7 +87,7 @@ void fetchURL(BearSSL::WiFiClientSecure *client, const char *host, const uint16_ client->write("\r\nUser-Agent: ESP8266\r\n"); client->write("\r\n"); uint32_t to = millis() + 5000; - if (client->connected()) { + while (client->available()) { do { char tmp[32]; memset(tmp, 0, 32); diff --git a/libraries/ESP8266WiFi/examples/BearSSL_CertStore/certs-from-mozilla.py b/libraries/ESP8266WiFi/examples/BearSSL_CertStore/certs-from-mozilla.py index 1f35a99850..80dbc48387 100755 --- a/libraries/ESP8266WiFi/examples/BearSSL_CertStore/certs-from-mozilla.py +++ b/libraries/ESP8266WiFi/examples/BearSSL_CertStore/certs-from-mozilla.py @@ -47,7 +47,6 @@ if item.startswith("'-----BEGIN CERTIFICATE-----"): pems.append(item) del names[0] # Remove headers -del pems[0] # Remove headers # Try and make ./data, skip if present try: diff --git a/libraries/ESP8266WiFi/examples/BearSSL_MaxFragmentLength/BearSSL_MaxFragmentLength.ino b/libraries/ESP8266WiFi/examples/BearSSL_MaxFragmentLength/BearSSL_MaxFragmentLength.ino index 03b619f932..84579d549d 100644 --- a/libraries/ESP8266WiFi/examples/BearSSL_MaxFragmentLength/BearSSL_MaxFragmentLength.ino +++ b/libraries/ESP8266WiFi/examples/BearSSL_MaxFragmentLength/BearSSL_MaxFragmentLength.ino @@ -16,7 +16,7 @@ const char *ssid = STASSID; const char *pass = STAPSK; void fetch(BearSSL::WiFiClientSecure *client) { - client->write("GET / HTTP/1.0\r\nHost: tls.mbed.org\r\nUser-Agent: ESP8266\r\n\r\n"); + client->write("GET /ip HTTP/1.0\r\nHost: api.my-ip.io\r\nUser-Agent: ESP8266\r\n\r\n"); client->flush(); using oneShot = esp8266::polledTimeout::oneShot; oneShot timeout(5000); @@ -39,13 +39,12 @@ void fetch(BearSSL::WiFiClientSecure *client) { int fetchNoMaxFragmentLength() { int ret = ESP.getFreeHeap(); - Serial.printf("\nConnecting to https://tls.mbed.org\n"); + Serial.printf("\nConnecting to https://api.my-ip.io\n"); Serial.printf("No MFLN attempted\n"); BearSSL::WiFiClientSecure client; client.setInsecure(); - client.connect("tls.mbed.org", 443); - if (client.connected()) { + if (client.connect("api.my-ip.io", 443)) { Serial.printf("Memory used: %d\n", ret - ESP.getFreeHeap()); ret -= ESP.getFreeHeap(); fetch(&client); @@ -77,12 +76,11 @@ int fetchMaxFragmentLength() { BearSSL::WiFiClientSecure client; client.setInsecure(); - bool mfln = client.probeMaxFragmentLength("tls.mbed.org", 443, 512); - Serial.printf("\nConnecting to https://tls.mbed.org\n"); + bool mfln = client.probeMaxFragmentLength("api.my-ip.io", 443, 512); + Serial.printf("\nConnecting to https://api.my-ip.io\n"); Serial.printf("MFLN supported: %s\n", mfln ? "yes" : "no"); if (mfln) { client.setBufferSizes(512, 512); } - client.connect("tls.mbed.org", 443); - if (client.connected()) { + if (client.connect("api.my-ip.io", 443)) { Serial.printf("MFLN status: %s\n", client.getMFLNStatus() ? "true" : "false"); Serial.printf("Memory used: %d\n", ret - ESP.getFreeHeap()); ret -= ESP.getFreeHeap(); diff --git a/libraries/ESP8266WiFi/examples/BearSSL_Server/BearSSL_Server.ino b/libraries/ESP8266WiFi/examples/BearSSL_Server/BearSSL_Server.ino index 299324af31..598a0adc77 100644 --- a/libraries/ESP8266WiFi/examples/BearSSL_Server/BearSSL_Server.ino +++ b/libraries/ESP8266WiFi/examples/BearSSL_Server/BearSSL_Server.ino @@ -48,102 +48,17 @@ const char *pass = STAPSK; // The HTTPS server BearSSL::WiFiServerSecure server(443); -//#define USE_EC // Enable Elliptic Curve signed cert +// #define USE_EC // Enable Elliptic Curve signed cert -#ifndef USE_EC - -// The server's private key which must be kept secret -const char server_private_key[] PROGMEM = R"EOF( ------BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDJblrg47vF3qlE -NMRM7uG8QwE6v/AKpxOL+CLb/32s+dW9Psgf+oZKJgzGkYUoJdWpLitTmTZeykAs -Sq7Iax5Rq/mGqyAc7oJAUUAupfNRU0KwkD1XqtpQWEFoiqoIqZbOZ4CRX5q8z/MN -BH1aPVBMKaL33uwknkgJBzxwZJ2+uGKxRJt8+koj1CXgUCk5lEAEEG5kqE326MjN -O/c4gBqulBV8AIoq6/trY3apTS7FEOiN47qh1PVzoBm/oGVwXvoZAZOj7+gGGo91 -sBC5oHJy5Y2BOcNB3opTNXQTiK3Z80b5wc3iQS+h83qAfHwhs6tfAW22WkAf+jtt -x8KdRWFNAgMBAAECggEAPd+jFL9/d1lc/zGCNuuN9YlTgFti/bKyo2UWOCOz1AVu -LVJyoLgQtggYFoqur1Vn2y7uaiB+/gD8U16hb7jPuGCuJjq8g4aUBfOvVmTtZ8a+ -joPQA/TcWJ+zf8xQTJbjVwWeDYmje2oZC5+cbbK1zp9fiuoz+U+RawyI+TE+700i -ESCmsKFIHy2Ifruva8HgcPYIPpZ9zLxJj0Dii+WDs7zM9h2dzO4HfImSG/DPmgoV -ydo9IcrUE7KoMLa8Uo7u1b2h6BnTn7GfYiMSUsYcYR3CnpDBknBWjZMwrV0uqv9q -TbVc4QXt+c1q89HDg7BIJaOAzbCvJfgAfXUqZyqwQQKBgQD5ENFjicUzCqPw7fOy -Q5Z8GeUbIJ5urT1MheAq7SPd2kK8TsO3hUjNC0LLNSyKPs6gsYaIiObO3wDGeZZk -xeHBhrUVaz2nIjI7TrnCUpMDOrdxcPr4bc+ifV5YT4W3OFBWQ9chQEx3Nm3DbiX4 -fpno34AiFrJF791JkTPFj9OIUQKBgQDPCgcae1pQr77q+GL5Q2tku3RrE4cWtExf -m8DzAb4Vxe3EhPz8bVr+71rqr/KqNfG1uKE3sT0fhB6VMTkHTOQU13jDrvpPUS3W -Vg8cVr5/+iiyF0xb+W8LQ+GVdR5xnMPSZHUtXyURvtzT4nnTAlAtN7lEytX9BzbX -xhltOOwGPQKBgA/Y/BnDSGLpCGlqGpl7J3YaB7PkLXCJYV8fHZZdpGyXWKu2r0lc -F7fEQanAZmcde/RJl2/UlisPkXMPhXxAAw9XTOph+nhJ+rw/VB6DNot8DvQO5kks -Y4vJQlmIJc/0q1fx1RxuhO8I7Y8D0TKwi4Z/wh1pKEq+6mul649kiWchAoGAWn8B -l9uvIHGRO9eSO23ytTcSrfL9Kzln4KqN7iom0hGP2kRe6F9MVP5+ePKrWSb3Hf0z -ysoX83ymeYPob352e32rda04EA9lv7giJrrrzbikrSNt5w3iMcRcCB4HTpW9Kmtq -pIhgBZ+tmpf1s/vg28LtoloeqtjKagpW9tzYnekCgYAZFZ84EGqS9SHw5LELgGY4 -mQLMwbYZ6wBMA2PlqYi/17hoAVWz37mLDjtWDB4ir78QMoGbesQVtK9W/4vzmez4 -ZLKlffdL5tCtA08Gq9aond1z83Xdnh1UjtwHIJvJPc/AoCFW1r5skv/G6acAk6I2 -Zs0aiirNGTEymRX4rw26Qg== ------END PRIVATE KEY----- -)EOF"; - -// The server's public certificate which must be shared -const char server_cert[] PROGMEM = R"EOF( ------BEGIN CERTIFICATE----- -MIIDUTCCAjmgAwIBAgIJAOcfK7c3JQtnMA0GCSqGSIb3DQEBCwUAMD8xCzAJBgNV -BAYTAkFVMQ0wCwYDVQQIDAROb25lMQ0wCwYDVQQKDAROb25lMRIwEAYDVQQDDAlF -U1BTZXJ2ZXIwHhcNMTgwMzE0MTg1NTQ1WhcNMjkwNTMxMTg1NTQ1WjA/MQswCQYD -VQQGEwJBVTENMAsGA1UECAwETm9uZTENMAsGA1UECgwETm9uZTESMBAGA1UEAwwJ -RVNQU2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyW5a4OO7 -xd6pRDTETO7hvEMBOr/wCqcTi/gi2/99rPnVvT7IH/qGSiYMxpGFKCXVqS4rU5k2 -XspALEquyGseUav5hqsgHO6CQFFALqXzUVNCsJA9V6raUFhBaIqqCKmWzmeAkV+a -vM/zDQR9Wj1QTCmi997sJJ5ICQc8cGSdvrhisUSbfPpKI9Ql4FApOZRABBBuZKhN -9ujIzTv3OIAarpQVfACKKuv7a2N2qU0uxRDojeO6odT1c6AZv6BlcF76GQGTo+/o -BhqPdbAQuaBycuWNgTnDQd6KUzV0E4it2fNG+cHN4kEvofN6gHx8IbOrXwFttlpA -H/o7bcfCnUVhTQIDAQABo1AwTjAdBgNVHQ4EFgQUBEk8LqgV+sMjdl/gpP1OlcNW -14EwHwYDVR0jBBgwFoAUBEk8LqgV+sMjdl/gpP1OlcNW14EwDAYDVR0TBAUwAwEB -/zANBgkqhkiG9w0BAQsFAAOCAQEAO1IrqW21KfzrxKmtuDSHdH5YrC3iOhiF/kaK -xXbigdtw6KHW/pIhGiA3BY5u+d5eVuHTR5YSwIbbRvOjuoNBATAw/8f5mt5Wa+C3 -PDpLNxDys561VbCW45RMQ0x5kybvDYi0D1R/grqZ18veuFSfE6QMJ/mzvr575fje -8r5Ou0IZOYYF8cyqG5rA4U7BYXEnH44VgwlpkF8pitPsnyUWaAYqE0KnZ0qw0Py4 -HCkfGJNlNOOamnr6KakVlocwKY0SdxcLoXSs5ogTQvTSrAOjwcm1RA0hOCXr8f/f -UsQIIGpPVh1plR1vYNndDeBpRJSFkoJTkgAIrlFzSMwNebU0pg== ------END CERTIFICATE----- -)EOF"; - -#else -const char server_cert[] PROGMEM = R"EOF( ------BEGIN CERTIFICATE----- -MIIB0zCCAXqgAwIBAgIJALANi2eTiGD/MAoGCCqGSM49BAMCMEUxCzAJBgNVBAYT -AkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRn -aXRzIFB0eSBMdGQwHhcNMTkwNjExMjIyOTU2WhcNMjAwNjEwMjIyOTU2WjBFMQsw -CQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJu -ZXQgV2lkZ2l0cyBQdHkgTHRkMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAExIkZ -w7zjk6TGcScff1PAehuEGmKZTf8VfnkjyJH0IbBgZibZ+qwYGBEnkz4KpKv7TkHo -W+j7F5EMcLcSrUIpy6NTMFEwHQYDVR0OBBYEFI6A0f+g0HyxUT6xrbVmRU79urbj -MB8GA1UdIwQYMBaAFI6A0f+g0HyxUT6xrbVmRU79urbjMA8GA1UdEwEB/wQFMAMB -Af8wCgYIKoZIzj0EAwIDRwAwRAIgWvy7ofQTGZMNqxUfe4gjtkU+C9AkQtaOMW2U -5xFFSvcCICvcGrQpoi7tRTq8xsXFmr8MYWgQTpVAtj6opXMQct/l ------END CERTIFICATE----- -)EOF"; - -// The server's private key which must be kept secret -const char server_private_key[] PROGMEM = R"EOF( ------BEGIN EC PARAMETERS----- -BggqhkjOPQMBBw== ------END EC PARAMETERS----- ------BEGIN EC PRIVATE KEY----- -MHcCAQEEIKyLR9/NT7ZdWM+2rklehveuk+jyIHJ+P8ZUQ392HOYvoAoGCCqGSM49 -AwEHoUQDQgAExIkZw7zjk6TGcScff1PAehuEGmKZTf8VfnkjyJH0IbBgZibZ+qwY -GBEnkz4KpKv7TkHoW+j7F5EMcLcSrUIpyw== ------END EC PRIVATE KEY----- -)EOF"; - -#endif +#define USING_INSECURE_CERTS_AND_KEYS_AND_CAS 1 +#include #define CACHE_SIZE 5 // Number of sessions to cache. #define USE_CACHE // Enable SSL session caching. // Caching SSL sessions shortens the length of the SSL handshake. // You can see the performance improvement by looking at the // Network tab of the developer tools of your browser. -//#define DYNAMIC_CACHE // Whether to dynamically allocate the cache. +// #define DYNAMIC_CACHE // Whether to dynamically allocate the cache. #if defined(USE_CACHE) && defined(DYNAMIC_CACHE) // Dynamically allocated cache. diff --git a/libraries/ESP8266WiFi/examples/BearSSL_Server/cert.pem b/libraries/ESP8266WiFi/examples/BearSSL_Server/cert.pem deleted file mode 100644 index 47238368aa..0000000000 --- a/libraries/ESP8266WiFi/examples/BearSSL_Server/cert.pem +++ /dev/null @@ -1,20 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDUTCCAjmgAwIBAgIJAOcfK7c3JQtnMA0GCSqGSIb3DQEBCwUAMD8xCzAJBgNV -BAYTAkFVMQ0wCwYDVQQIDAROb25lMQ0wCwYDVQQKDAROb25lMRIwEAYDVQQDDAlF -U1BTZXJ2ZXIwHhcNMTgwMzE0MTg1NTQ1WhcNMjkwNTMxMTg1NTQ1WjA/MQswCQYD -VQQGEwJBVTENMAsGA1UECAwETm9uZTENMAsGA1UECgwETm9uZTESMBAGA1UEAwwJ -RVNQU2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyW5a4OO7 -xd6pRDTETO7hvEMBOr/wCqcTi/gi2/99rPnVvT7IH/qGSiYMxpGFKCXVqS4rU5k2 -XspALEquyGseUav5hqsgHO6CQFFALqXzUVNCsJA9V6raUFhBaIqqCKmWzmeAkV+a -vM/zDQR9Wj1QTCmi997sJJ5ICQc8cGSdvrhisUSbfPpKI9Ql4FApOZRABBBuZKhN -9ujIzTv3OIAarpQVfACKKuv7a2N2qU0uxRDojeO6odT1c6AZv6BlcF76GQGTo+/o -BhqPdbAQuaBycuWNgTnDQd6KUzV0E4it2fNG+cHN4kEvofN6gHx8IbOrXwFttlpA -H/o7bcfCnUVhTQIDAQABo1AwTjAdBgNVHQ4EFgQUBEk8LqgV+sMjdl/gpP1OlcNW -14EwHwYDVR0jBBgwFoAUBEk8LqgV+sMjdl/gpP1OlcNW14EwDAYDVR0TBAUwAwEB -/zANBgkqhkiG9w0BAQsFAAOCAQEAO1IrqW21KfzrxKmtuDSHdH5YrC3iOhiF/kaK -xXbigdtw6KHW/pIhGiA3BY5u+d5eVuHTR5YSwIbbRvOjuoNBATAw/8f5mt5Wa+C3 -PDpLNxDys561VbCW45RMQ0x5kybvDYi0D1R/grqZ18veuFSfE6QMJ/mzvr575fje -8r5Ou0IZOYYF8cyqG5rA4U7BYXEnH44VgwlpkF8pitPsnyUWaAYqE0KnZ0qw0Py4 -HCkfGJNlNOOamnr6KakVlocwKY0SdxcLoXSs5ogTQvTSrAOjwcm1RA0hOCXr8f/f -UsQIIGpPVh1plR1vYNndDeBpRJSFkoJTkgAIrlFzSMwNebU0pg== ------END CERTIFICATE----- diff --git a/libraries/ESP8266WiFi/examples/BearSSL_Server/key.pem b/libraries/ESP8266WiFi/examples/BearSSL_Server/key.pem deleted file mode 100644 index 4d270b18b5..0000000000 --- a/libraries/ESP8266WiFi/examples/BearSSL_Server/key.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDJblrg47vF3qlE -NMRM7uG8QwE6v/AKpxOL+CLb/32s+dW9Psgf+oZKJgzGkYUoJdWpLitTmTZeykAs -Sq7Iax5Rq/mGqyAc7oJAUUAupfNRU0KwkD1XqtpQWEFoiqoIqZbOZ4CRX5q8z/MN -BH1aPVBMKaL33uwknkgJBzxwZJ2+uGKxRJt8+koj1CXgUCk5lEAEEG5kqE326MjN -O/c4gBqulBV8AIoq6/trY3apTS7FEOiN47qh1PVzoBm/oGVwXvoZAZOj7+gGGo91 -sBC5oHJy5Y2BOcNB3opTNXQTiK3Z80b5wc3iQS+h83qAfHwhs6tfAW22WkAf+jtt -x8KdRWFNAgMBAAECggEAPd+jFL9/d1lc/zGCNuuN9YlTgFti/bKyo2UWOCOz1AVu -LVJyoLgQtggYFoqur1Vn2y7uaiB+/gD8U16hb7jPuGCuJjq8g4aUBfOvVmTtZ8a+ -joPQA/TcWJ+zf8xQTJbjVwWeDYmje2oZC5+cbbK1zp9fiuoz+U+RawyI+TE+700i -ESCmsKFIHy2Ifruva8HgcPYIPpZ9zLxJj0Dii+WDs7zM9h2dzO4HfImSG/DPmgoV -ydo9IcrUE7KoMLa8Uo7u1b2h6BnTn7GfYiMSUsYcYR3CnpDBknBWjZMwrV0uqv9q -TbVc4QXt+c1q89HDg7BIJaOAzbCvJfgAfXUqZyqwQQKBgQD5ENFjicUzCqPw7fOy -Q5Z8GeUbIJ5urT1MheAq7SPd2kK8TsO3hUjNC0LLNSyKPs6gsYaIiObO3wDGeZZk -xeHBhrUVaz2nIjI7TrnCUpMDOrdxcPr4bc+ifV5YT4W3OFBWQ9chQEx3Nm3DbiX4 -fpno34AiFrJF791JkTPFj9OIUQKBgQDPCgcae1pQr77q+GL5Q2tku3RrE4cWtExf -m8DzAb4Vxe3EhPz8bVr+71rqr/KqNfG1uKE3sT0fhB6VMTkHTOQU13jDrvpPUS3W -Vg8cVr5/+iiyF0xb+W8LQ+GVdR5xnMPSZHUtXyURvtzT4nnTAlAtN7lEytX9BzbX -xhltOOwGPQKBgA/Y/BnDSGLpCGlqGpl7J3YaB7PkLXCJYV8fHZZdpGyXWKu2r0lc -F7fEQanAZmcde/RJl2/UlisPkXMPhXxAAw9XTOph+nhJ+rw/VB6DNot8DvQO5kks -Y4vJQlmIJc/0q1fx1RxuhO8I7Y8D0TKwi4Z/wh1pKEq+6mul649kiWchAoGAWn8B -l9uvIHGRO9eSO23ytTcSrfL9Kzln4KqN7iom0hGP2kRe6F9MVP5+ePKrWSb3Hf0z -ysoX83ymeYPob352e32rda04EA9lv7giJrrrzbikrSNt5w3iMcRcCB4HTpW9Kmtq -pIhgBZ+tmpf1s/vg28LtoloeqtjKagpW9tzYnekCgYAZFZ84EGqS9SHw5LELgGY4 -mQLMwbYZ6wBMA2PlqYi/17hoAVWz37mLDjtWDB4ir78QMoGbesQVtK9W/4vzmez4 -ZLKlffdL5tCtA08Gq9aond1z83Xdnh1UjtwHIJvJPc/AoCFW1r5skv/G6acAk6I2 -Zs0aiirNGTEymRX4rw26Qg== ------END PRIVATE KEY----- diff --git a/libraries/ESP8266WiFi/examples/BearSSL_ServerClientCert/BearSSL_ServerClientCert.ino b/libraries/ESP8266WiFi/examples/BearSSL_ServerClientCert/BearSSL_ServerClientCert.ino index d05ccb3dd3..0b962dbbbf 100644 --- a/libraries/ESP8266WiFi/examples/BearSSL_ServerClientCert/BearSSL_ServerClientCert.ino +++ b/libraries/ESP8266WiFi/examples/BearSSL_ServerClientCert/BearSSL_ServerClientCert.ino @@ -58,6 +58,7 @@ line, you will not get connected. ex: wget --quiet --O - --no-check-certificate --certificate=client1_cer.pem --private-key=client1_key.pem https://esp.ip.add.ress/ + ex: curl --insecure --cert client1_cer.pem --key client1_key.pem https://esp.ip.add.ress/ This example is released into the public domain. */ @@ -73,86 +74,17 @@ const char *ssid = STASSID; const char *pass = STAPSK; +constexpr int port = 443; + // The server which will require a client cert signed by the trusted CA -BearSSL::WiFiServerSecure server(443); +BearSSL::WiFiServerSecure server(port); // The hardcoded certificate authority for this example. -// Don't use it on your own apps!!!!! -const char ca_cert[] PROGMEM = R"EOF( ------BEGIN CERTIFICATE----- -MIIC1TCCAb2gAwIBAgIJAMPt1Ms37+hLMA0GCSqGSIb3DQEBCwUAMCExCzAJBgNV -BAYTAlVTMRIwEAYDVQQDDAkxMjcuMC4wLjMwHhcNMTgwMzE0MDQyMTU0WhcNMjkw -NTMxMDQyMTU0WjAhMQswCQYDVQQGEwJVUzESMBAGA1UEAwwJMTI3LjAuMC4zMIIB -IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxsa4qU/tlzN4YTcnn/I/ffsi -jOPc8QRcwClKzasIZNFEye4uThl+LGZWFIFb8X8Dc+xmmBaWlPJbqtphgFKStpar -DdduHSW1ud6Y1FVKxljo3UwCMrYm76Q/jNzXJvGs6Z1MDNsVZzGJaoqit2H2Hkvk -y+7kk3YbEDlcyVsLOw0zCKL4cd2DSNDyhIZxWo2a8Qn5IdjWAYtsTnW6MvLk/ya4 -abNeRfSZwi+r37rqi9CIs++NpL5ynqkKKEMrbeLactWgHbWrZeaMyLpuUEL2GF+w -MRaAwaj7ERwT5gFJRqYwj6bbfIdx5PC7h7ucbyp272MbrDa6WNBCMwQO222t4wID -AQABoxAwDjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCmXfrC42nW -IpL3JDkB8YlB2QUvD9JdMp98xxo33+xE69Gov0e6984F1Gluao0p6sS7KF+q3YLS -4hjnzuGzF9GJMimIB7NMQ20yXKfKpmKJ7YugMaKTDWDhHn5679mKVbLSQxHCUMEe -tEnMT93/UaDbWBjV6zu876q5vjPMYgDHODqO295ySaA71UkijaCn6UwKUT49286T -V9ZtzgabNGHXfklHgUPWoShyze+G3g29I1BR0qABoJI63zaNu8ua42v5g1RldxsW -X8yKI14mFOGxuvcygG8L2xxysW7Zq+9g+O7gW0Pm6RDYnUQmIwY83h1KFCtYCJdS -2PgozwkkUNyP ------END CERTIFICATE----- -)EOF"; - // The server's private key which must be kept secret -const char server_private_key[] PROGMEM = R"EOF( ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAsRNVTvqP++YUh8NrbXwE83xVsDqcB3F76xcXNKFDERfVd2P/ -LvyDovCcoQtT0UCRgPcxRp894EuPH/Ru6Z2Lu85sV//i7ce27tc2WRFSfuhlRxHP -LJWHxTl1CEfXp/owkECQ4MB3pw6Ekc16iTEPiezTG+T+mQ/BkiIwcIK6CMlpR9DI -eYUTqv0f9NrUfAjdBrqlEO2gpgFvLFrkDEU2ntAIc4aPOP7yDOym/xzfy6TiG8Wo -7nlh6M97xTZGfbEPCH9rZDjo5istym1HzF5P+COq+OTSPscjFGXoi978o6hZwa7i -zxorg4h5a5lGnshRu2Gl+Ybfa14OwnIrv/yCswIDAQABAoIBAHxwgbsHCriTcEoY -Yx6F0VTrQ6ydA5mXfuYvS/eIfIE+pp1IgMScYEXZobjrJPQg1CA1l0NyFSHS97oV -JPy34sMQxcLx6KABgeVHCMJ/EeJtnv7a3SUP0GIhhsVS95Lsl8RIG4hWub+EzFVK -eZqAB9N9wr4Pp3wZPodbz37B38rb1QPyMFmQOLlHjKTOmoxsXhL2ot+R3+aLYSur -oPO1kQo7/d0UAZoy8h9OQN4a2EXvawh4O2EvFGbc5X/yXwAdEQ4NPp9VZhkNIRkV -+XZ3FcIqEVOploKtRF/tVBTz3g61/lFz21L9PMmV5y8tvSafr2SpJugGVmp2rrVQ -VNyGlIECgYEA10JSI5gmeCU3zK6kvOfBp54hY/5dDrSUpjKkMxpmm7WZQ6Il/k7A -hMcLeMzHiriT7WhRIXF8AOr2MoEkHkH3DhVNN4ccieVZx2SE5P5mVkItZGLrrpfU -dysR/ARAI1HYegGUiKacZtf9SrRavU0m7fOVOiYwbFRhjyX+MyuteYkCgYEA0pbz -4ZosetScP68uZx1sGlTfkcqLl7i15DHk3gnj6jKlfhvC2MjeLMhNDtKeUAuY7rLQ -guZ0CCghWAv0Glh5eYdfIiPhgqFfX4P5F3Om4zQHVPYj8xHfHG4ZP7dKQTndrO1Q -fLdGDTQLVXabAUSp2YGrijC8J9idSW1pYClvF1sCgYEAjkDn41nzYkbGP1/Swnwu -AEWCL4Czoro32jVxScxSrugt5wJLNWp508VukWBTJhugtq3Pn9hNaJXeKbYqVkyl -pgrxwpZph7+nuxt0r5hnrO2C7eppcjIoWLB/7BorAKxf8REGReBFT7nBTBMwPBW2 -el4U6h6+tXh2GJG1Eb/1nnECgYAydVb0THOx7rWNkNUGggc/++why61M6kYy6j2T -cj05BW+f2tkCBoctpcTI83BZb53yO8g4RS2yMqNirGKN2XspwmTqEjzbhv0KLt4F -X4GyWOoU0nFksXiLIFpOaQWSwWG7KJWrfGJ9kWXR0Xxsfl5QLoDCuNCsn3t4d43T -K7phlwKBgHDzF+50+/Wez3YHCy2a/HgSbHCpLQjkknvgwkOh1z7YitYBUm72HP8Z -Ge6b4wEfNuBdlZll/y9BQQOZJLFvJTE5t51X9klrkGrOb+Ftwr7eI/H5xgcadI52 -tPYglR5fjuRF/wnt3oX9JlQ2RtSbs+3naXH8JoherHaqNn8UpH0t ------END RSA PRIVATE KEY----- -)EOF"; - // The server's public certificate which must be shared -const char server_cert[] PROGMEM = R"EOF( ------BEGIN CERTIFICATE----- -MIIDTzCCAjcCCQDPXvMRYOpeuDANBgkqhkiG9w0BAQsFADCBpjESMBAGA1UEAwwJ -MTI3LjAuMC4xMQswCQYDVQQGEwJVUzElMCMGA1UECgwcTXkgT3duIENlcnRpZmlj -YXRlIEF1dGhvcml0eTEUMBIGA1UECAwLQXJkdWlub0xhbmQxFTATBgNVBAcMDEFy -ZHVpbm9WaWxsZTEVMBMGA1UECgwMRVNQODI2NlVzZXJzMRgwFgYDVQQLDA9FU1A4 -MjY2LUFyZHVpbm8wHhcNMTgwMzE0MDQwMDAwWhcNMjkwMjI0MDQwMDAwWjAsMRYw -FAYDVQQKDA1NeSBTZXJ2ZXIgT3JnMRIwEAYDVQQDDAkxMjcuMC4wLjMwggEiMA0G -CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCxE1VO+o/75hSHw2ttfATzfFWwOpwH -cXvrFxc0oUMRF9V3Y/8u/IOi8JyhC1PRQJGA9zFGnz3gS48f9G7pnYu7zmxX/+Lt -x7bu1zZZEVJ+6GVHEc8slYfFOXUIR9en+jCQQJDgwHenDoSRzXqJMQ+J7NMb5P6Z -D8GSIjBwgroIyWlH0Mh5hROq/R/02tR8CN0GuqUQ7aCmAW8sWuQMRTae0Ahzho84 -/vIM7Kb/HN/LpOIbxajueWHoz3vFNkZ9sQ8If2tkOOjmKy3KbUfMXk/4I6r45NI+ -xyMUZeiL3vyjqFnBruLPGiuDiHlrmUaeyFG7YaX5ht9rXg7Cciu//IKzAgMBAAEw -DQYJKoZIhvcNAQELBQADggEBAEnG+FNyNCOkBvzHiUpHHpScxZqM2f+XDcewJgeS -L6HkYEDIZZDNnd5gduSvkHpdJtWgsvJ7dJZL40w7Ba5sxpZHPIgKJGl9hzMkG+aA -z5GMkjys9h2xpQZx9KL3q7G6A+C0bll7ODZlwBtY07CFMykT4Mp2oMRrQKRucMSV -AB1mKujLAnMRKJ3NM89RQJH4GYiRps9y/HvM5lh7EIK/J0/nEZeJxY5hJngskPKb -oPPdmkR97kaQnll4KNsC3owVlHVU2fMftgYkgQLzyeWgzcNa39AF3B6JlcOzNyQY -seoK24dHmt6tWmn/sbxX7Aa6TL/4mVlFoOgcaTJyVaY/BrY= ------END CERTIFICATE----- -)EOF"; +// Don't use them on your own apps!!!!! +#define USING_INSECURE_CERTS_AND_KEYS_AND_CAS 1 +#include // Note there are no client certificates required here in the server. // That is because all clients will send a certificate that can be @@ -211,6 +143,10 @@ void setup() { // Actually start accepting connections server.begin(); + + Serial.println("This example requires a client certificate."); + Serial.printf("ex: wget --quiet --O - --no-check-certificate --certificate=client1_cer.pem --private-key=client1_key.pem https://%s:%d/\n", WiFi.localIP().toString().c_str(), port); + Serial.printf("ex: curl --insecure --cert client1_cer.pem --key client1_key.pem https://%s:%d/\n", WiFi.localIP().toString().c_str(), port); } static const char *HTTP_RES = "HTTP/1.0 200 OK\r\n" diff --git a/libraries/ESP8266WiFi/examples/BearSSL_Sessions/BearSSL_Sessions.ino b/libraries/ESP8266WiFi/examples/BearSSL_Sessions/BearSSL_Sessions.ino index be1ad6e4b7..cf227403f3 100644 --- a/libraries/ESP8266WiFi/examples/BearSSL_Sessions/BearSSL_Sessions.ino +++ b/libraries/ESP8266WiFi/examples/BearSSL_Sessions/BearSSL_Sessions.ino @@ -63,8 +63,7 @@ void fetchURL(BearSSL::WiFiClientSecure *client, const char *host, const uint16_ if (!path) { path = "/"; } Serial.printf("Trying: %s:443...", host); - client->connect(host, port); - if (!client->connected()) { + if (!client->connect(host, port)) { Serial.printf("*** Can't connect. ***\n-------\n"); return; } @@ -76,7 +75,7 @@ void fetchURL(BearSSL::WiFiClientSecure *client, const char *host, const uint16_ client->write("\r\nUser-Agent: ESP8266\r\n"); client->write("\r\n"); uint32_t to = millis() + 5000; - if (client->connected()) { + while (client->available()) { do { char tmp[32]; memset(tmp, 0, 32); diff --git a/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino b/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino index 7667579157..2f6c08a51a 100644 --- a/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino +++ b/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino @@ -10,6 +10,10 @@ #include #include "certs.h" +#define FINGERPRINT fingerprint_www_example_org +#define PUBKEY pubkey_www_example_org +#define CERT cert_DigiCert_Global_G2_TLS_RSA_SHA256_2020_CA1 + #ifndef STASSID #define STASSID "your-ssid" #define STAPSK "your-password" @@ -45,8 +49,7 @@ void fetchURL(BearSSL::WiFiClientSecure *client, const char *host, const uint16_ ESP.resetFreeContStack(); uint32_t freeStackStart = ESP.getFreeContStack(); Serial.printf("Trying: %s:443...", host); - client->connect(host, port); - if (!client->connected()) { + if (!client->connect(host, port)) { Serial.printf("*** Can't connect. ***\n-------\n"); return; } @@ -58,7 +61,7 @@ void fetchURL(BearSSL::WiFiClientSecure *client, const char *host, const uint16_ client->write("\r\nUser-Agent: ESP8266\r\n"); client->write("\r\n"); uint32_t to = millis() + 5000; - if (client->connected()) { + while (client->available()) { do { char tmp[32]; memset(tmp, 0, 32); @@ -87,7 +90,7 @@ If there are no CAs or insecure options specified, BearSSL will not connect. Expect the following call to fail as none have been configured. )EOF"); BearSSL::WiFiClientSecure client; - fetchURL(&client, gitlab_host, gitlab_port, path); + fetchURL(&client, SSL_host, SSL_port, path); } void fetchInsecure() { @@ -98,7 +101,7 @@ which is subject to man-in-the-middle (MITM) attacks. )EOF"); BearSSL::WiFiClientSecure client; client.setInsecure(); - fetchURL(&client, gitlab_host, gitlab_port, path); + fetchURL(&client, SSL_host, SSL_port, path); } void fetchFingerprint() { @@ -111,8 +114,8 @@ fingerprints will change if anything changes in the certificate chain the root authorities, etc.). )EOF"); BearSSL::WiFiClientSecure client; - client.setFingerprint(fingerprint_gitlab_com); - fetchURL(&client, gitlab_host, gitlab_port, path); + client.setFingerprint(FINGERPRINT); + fetchURL(&client, SSL_host, SSL_port, path); } void fetchSelfSigned() { @@ -137,9 +140,9 @@ private and not shared. A MITM without the private key would not be able to establish communications. )EOF"); BearSSL::WiFiClientSecure client; - BearSSL::PublicKey key(pubkey_gitlab_com); + BearSSL::PublicKey key(PUBKEY); client.setKnownKey(&key); - fetchURL(&client, gitlab_host, gitlab_port, path); + fetchURL(&client, SSL_host, SSL_port, path); } void fetchCertAuthority() { @@ -153,14 +156,14 @@ BearSSL does verify the notValidBefore/After fields. )EOF"); BearSSL::WiFiClientSecure client; - BearSSL::X509List cert(cert_USERTrust_RSA_Certification_Authority); + BearSSL::X509List cert(CERT); client.setTrustAnchors(&cert); Serial.printf("Try validating without setting the time (should fail)\n"); - fetchURL(&client, gitlab_host, gitlab_port, path); + fetchURL(&client, SSL_host, SSL_port, path); Serial.printf("Try again after setting NTP time (should pass)\n"); setClock(); - fetchURL(&client, gitlab_host, gitlab_port, path); + fetchURL(&client, SSL_host, SSL_port, path); } void fetchFaster() { @@ -171,20 +174,23 @@ you won't want to do this. If you need to maximize battery life, these may make sense )EOF"); BearSSL::WiFiClientSecure client; + Serial.printf("Insecure, all ciphers:\n"); client.setInsecure(); uint32_t now = millis(); - fetchURL(&client, gitlab_host, gitlab_port, path); + fetchURL(&client, SSL_host, SSL_port, path); uint32_t delta = millis() - now; + Serial.printf("Insecure, less secure ciphers:\n"); client.setInsecure(); client.setCiphersLessSecure(); now = millis(); - fetchURL(&client, gitlab_host, gitlab_port, path); + fetchURL(&client, SSL_host, SSL_port, path); uint32_t delta2 = millis() - now; + Serial.printf("Insecure, few ciphers:\n"); std::vector myCustomList = { BR_TLS_RSA_WITH_AES_256_CBC_SHA256, BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, BR_TLS_RSA_WITH_3DES_EDE_CBC_SHA }; client.setInsecure(); client.setCiphers(myCustomList); now = millis(); - fetchURL(&client, gitlab_host, gitlab_port, path); + fetchURL(&client, SSL_host, SSL_port, path); uint32_t delta3 = millis() - now; Serial.printf("Using more secure: %dms\nUsing less secure ciphers: %dms\nUsing custom cipher list: %dms\n", delta, delta2, delta3); } diff --git a/libraries/ESP8266WiFi/examples/BearSSL_Validation/certUpdate b/libraries/ESP8266WiFi/examples/BearSSL_Validation/certUpdate index 2162b78e97..58468ae7a7 100755 --- a/libraries/ESP8266WiFi/examples/BearSSL_Validation/certUpdate +++ b/libraries/ESP8266WiFi/examples/BearSSL_Validation/certUpdate @@ -1,2 +1,2 @@ cd ${0%/*} 2>/dev/null -python3 ../../../../tools/cert.py -s www.gitlab.com -n gitlab > certs.h +python3 ../../../../tools/cert.py -s www.example.com -n SSL > certs.h diff --git a/libraries/ESP8266WiFi/examples/BearSSL_Validation/certs.h b/libraries/ESP8266WiFi/examples/BearSSL_Validation/certs.h index cf6c1ca76d..c6b99041a4 100644 --- a/libraries/ESP8266WiFi/examples/BearSSL_Validation/certs.h +++ b/libraries/ESP8266WiFi/examples/BearSSL_Validation/certs.h @@ -1,115 +1,98 @@ // this file is autogenerated - any modification will be overwritten // unused symbols will not be linked in the final binary -// generated on 2021-07-26 22:04:47 -// by ['../../../../tools/cert.py', '-s', 'www.gitlab.com', '-n', 'gitlab'] +// generated on 2024-07-30 23:28:11 +// by ['../../../../tools/cert.py', '-s', 'www.example.com', '-n', 'SSL'] #pragma once //////////////////////////////////////////////////////////// -// certificate chain for www.gitlab.com:443 +// certificate chain for www.example.com:443 -const char* gitlab_host = "www.gitlab.com"; -const uint16_t gitlab_port = 443; +const char* SSL_host = "www.example.com"; +const uint16_t SSL_port = 443; -// CN: gitlab.com => name: gitlab_com -// not valid before: 2021-04-12 00:00:00 -// not valid after: 2022-05-11 23:59:59 -const char fingerprint_gitlab_com [] PROGMEM = "71:55:5e:29:68:99:43:98:c8:85:35:bd:4c:10:4c:f5:cf:17:09:e6"; -const char pubkey_gitlab_com [] PROGMEM = R"PUBKEY( +// CN: www.example.org => name: www_example_org +// 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----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1eeFy86Xbz3ygyCVprHp -sPP3zyg0yldkIfqwjsXPH0b+KwQ85s3pzI/5+MVrR2/BGY4ed6mTZ6hvNwQJ2B0E -sJrsTb2nuUsXQ0UVO4hvnZ7Dnx8r/bT1cndqa+Mn+bms8/TS4etP72+TLaORBRCz -O4L1Hi8r61+zZLnP3DqqHeHAgl5wKHNYpx7yFFl2I71LuLH/pk2ICDBjaHwCIbRW -u484no9s1c4VROxqMrQQ/wDMl80MiO9YeNQ5rBHfnabh4rFe9eb2Sd0H/DWBj3SO -YBD0kiLI6b5CWYfA76pBSlZg7G3ledvQ+n9FEcS3EOCPKBBZqMDCzEahvHqwJ/r6 -pwIDAQAB +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhoUPuw75yl/Z9eAKMiwz +2aoOBymoLwiteL3CBr/3LSumpyc9U6ZMw0uyJ3cg1sFUSbgI2vlwqWH2skmdaVfa ++20kNHIuR/AEP52xW+K8ZjFZMuapfr/UsNRk9WvKe/9yW16a2D/UBrLzyNyPZlpG +hGaoGBV5pwjOBTz7OYnvbfpOcVJ7t+SgpJyWwGE9pApwTcOOzW6zMmzyx0QJBN2g +Vf0jpSB4soVe2DutF/+Fxbl0jTO5uFdutbxpZdsLPJJVmfRztGQkymdMKJnM3Gc9 +eccWnCvmq6qqNXI39oEqSOg/Thmav55GqjKT/6WyWrSxLx5phJIdsLmNr/IxbJWG +8wIDAQAB -----END PUBLIC KEY----- )PUBKEY"; -// http://crt.sectigo.com/SectigoRSADomainValidationSecureServerCA.crt -// CN: Sectigo RSA Domain Validation Secure Server CA => name: Sectigo_RSA_Domain_Validation_Secure_Server_CA -// not valid before: 2018-11-02 00:00:00 -// not valid after: 2030-12-31 23:59:59 -const char cert_Sectigo_RSA_Domain_Validation_Secure_Server_CA [] 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----- -MIIGEzCCA/ugAwIBAgIQfVtRJrR2uhHbdBYLvFMNpzANBgkqhkiG9w0BAQwFADCB -iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl -cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV -BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTgx -MTAyMDAwMDAwWhcNMzAxMjMxMjM1OTU5WjCBjzELMAkGA1UEBhMCR0IxGzAZBgNV -BAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEYMBYGA1UE -ChMPU2VjdGlnbyBMaW1pdGVkMTcwNQYDVQQDEy5TZWN0aWdvIFJTQSBEb21haW4g -VmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEA1nMz1tc8INAA0hdFuNY+B6I/x0HuMjDJsGz99J/LEpgPLT+N -TQEMgg8Xf2Iu6bhIefsWg06t1zIlk7cHv7lQP6lMw0Aq6Tn/2YHKHxYyQdqAJrkj -eocgHuP/IJo8lURvh3UGkEC0MpMWCRAIIz7S3YcPb11RFGoKacVPAXJpz9OTTG0E -oKMbgn6xmrntxZ7FN3ifmgg0+1YuWMQJDgZkW7w33PGfKGioVrCSo1yfu4iYCBsk -Haswha6vsC6eep3BwEIc4gLw6uBK0u+QDrTBQBbwb4VCSmT3pDCg/r8uoydajotY -uK3DGReEY+1vVv2Dy2A0xHS+5p3b4eTlygxfFQIDAQABo4IBbjCCAWowHwYDVR0j -BBgwFoAUU3m/WqorSs9UgOHYm8Cd8rIDZsswHQYDVR0OBBYEFI2MXsRUrYrhd+mb -+ZsF4bgBjWHhMA4GA1UdDwEB/wQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEAMB0G -A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAbBgNVHSAEFDASMAYGBFUdIAAw -CAYGZ4EMAQIBMFAGA1UdHwRJMEcwRaBDoEGGP2h0dHA6Ly9jcmwudXNlcnRydXN0 -LmNvbS9VU0VSVHJ1c3RSU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDB2Bggr -BgEFBQcBAQRqMGgwPwYIKwYBBQUHMAKGM2h0dHA6Ly9jcnQudXNlcnRydXN0LmNv -bS9VU0VSVHJ1c3RSU0FBZGRUcnVzdENBLmNydDAlBggrBgEFBQcwAYYZaHR0cDov -L29jc3AudXNlcnRydXN0LmNvbTANBgkqhkiG9w0BAQwFAAOCAgEAMr9hvQ5Iw0/H -ukdN+Jx4GQHcEx2Ab/zDcLRSmjEzmldS+zGea6TvVKqJjUAXaPgREHzSyrHxVYbH -7rM2kYb2OVG/Rr8PoLq0935JxCo2F57kaDl6r5ROVm+yezu/Coa9zcV3HAO4OLGi -H19+24rcRki2aArPsrW04jTkZ6k4Zgle0rj8nSg6F0AnwnJOKf0hPHzPE/uWLMUx -RP0T7dWbqWlod3zu4f+k+TY4CFM5ooQ0nBnzvg6s1SQ36yOoeNDT5++SR2RiOSLv -xvcRviKFxmZEJCaOEDKNyJOuB56DPi/Z+fVGjmO+wea03KbNIaiGCpXZLoUmGv38 -sbZXQm2V0TP2ORQGgkE49Y9Y3IBbpNV9lXj9p5v//cWoaasm56ekBYdbqbe4oyAL -l6lFhd2zi+WJN44pDfwGF/Y4QA5C5BIG+3vzxhFoYt/jmPQT2BVPi7Fp2RBgvGQq -6jG35LWjOhSbJuMLe/0CjraZwTiXWTb2qHSihrZe68Zk6s+go/lunrotEbaGmAhY -LcmsJWTyXnW0OMGuf1pGg+pRyrbxmRE1a6Vqe8YAsOf4vmSyrcjC8azjUeqkk+B5 -yOGBQMkKW+ESPMFgKuOXwIlCypTPRpgSabuY0MLTDXJLR27lk8QyKGOHQ+SwMj4K -00u/I5sUKUErmgQfky3xxzlIPK1aEn8= +MIIEyDCCA7CgAwIBAgIQDPW9BitWAvR6uFAsI8zwZjANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +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://crt.usertrust.com/USERTrustRSAAddTrustCA.crt -// CN: USERTrust RSA Certification Authority => name: USERTrust_RSA_Certification_Authority -// not valid before: 2019-03-12 00:00:00 -// not valid after: 2028-12-31 23:59:59 -const char cert_USERTrust_RSA_Certification_Authority [] 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----- -MIIFgTCCBGmgAwIBAgIQOXJEOvkit1HX02wQ3TE1lTANBgkqhkiG9w0BAQwFADB7 -MQswCQYDVQQGEwJHQjEbMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYD -VQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UE -AwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTE5MDMxMjAwMDAwMFoXDTI4 -MTIzMTIzNTk1OVowgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5 -MRQwEgYDVQQHEwtKZXJzZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBO -ZXR3b3JrMS4wLAYDVQQDEyVVU0VSVHJ1c3QgUlNBIENlcnRpZmljYXRpb24gQXV0 -aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAgBJlFzYOw9sI -s9CsVw127c0n00ytUINh4qogTQktZAnczomfzD2p7PbPwdzx07HWezcoEStH2jnG -vDoZtF+mvX2do2NCtnbyqTsrkfjib9DsFiCQCT7i6HTJGLSR1GJk23+jBvGIGGqQ -Ijy8/hPwhxR79uQfjtTkUcYRZ0YIUcuGFFQ/vDP+fmyc/xadGL1RjjWmp2bIcmfb -IWax1Jt4A8BQOujM8Ny8nkz+rwWWNR9XWrf/zvk9tyy29lTdyOcSOk2uTIq3XJq0 -tyA9yn8iNK5+O2hmAUTnAU5GU5szYPeUvlM3kHND8zLDU+/bqv50TmnHa4xgk97E -xwzf4TKuzJM7UXiVZ4vuPVb+DNBpDxsP8yUmazNt925H+nND5X4OpWaxKXwyhGNV -icQNwZNUMBkTrNN9N6frXTpsNVzbQdcS2qlJC9/YgIoJk2KOtWbPJYjNhLixP6Q5 -D9kCnusSTJV882sFqV4Wg8y4Z+LoE53MW4LTTLPtW//e5XOsIzstAL81VXQJSdhJ -WBp/kjbmUZIO8yZ9HE0XvMnsQybQv0FfQKlERPSZ51eHnlAfV1SoPv10Yy+xUGUJ -5lhCLkMaTLTwJUdZ+gQek9QmRkpQgbLevni3/GcV4clXhB4PY9bpYrrWX1Uu6lzG -KAgEJTm4Diup8kyXHAc/DVL17e8vgg8CAwEAAaOB8jCB7zAfBgNVHSMEGDAWgBSg -EQojPpbxB+zirynvgqV/0DCktDAdBgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rID -ZsswDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0gBAowCDAG -BgRVHSAAMEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwuY29tb2RvY2EuY29t -L0FBQUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDQGCCsGAQUFBwEBBCgwJjAkBggr -BgEFBQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2EuY29tMA0GCSqGSIb3DQEBDAUA -A4IBAQAYh1HcdCE9nIrgJ7cz0C7M7PDmy14R3iJvm3WOnnL+5Nb+qh+cli3vA0p+ -rvSNb3I8QzvAP+u431yqqcau8vzY7qN7Q/aGNnwU4M309z/+3ri0ivCRlv79Q2R+ -/czSAaF9ffgZGclCKxO/WIu6pKJmBHaIkU4MiRTOok3JMrO66BQavHHxW/BBC5gA -CiIDEOUMsfnNkjcZ7Tvx5Dq2+UUTJnWvu6rvP3t3O9LEApE9GQDTF1w52z97GA1F -zZOFli9d31kWTz9RvdVFGD/tSo7oBmF0Ixa1DVBzJ0RHfxBdiSprhTEUxOipakyA -vGp4z7h/jnZymQyd/teRCBaho1+V +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +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"; -// end of certificate chain for www.gitlab.com:443 +// end of certificate chain for www.example.com:443 //////////////////////////////////////////////////////////// diff --git a/libraries/ESP8266WiFi/examples/CustomOffer/CustomOffer.ino b/libraries/ESP8266WiFi/examples/CustomOffer/CustomOffer.ino new file mode 100644 index 0000000000..fa746277c9 --- /dev/null +++ b/libraries/ESP8266WiFi/examples/CustomOffer/CustomOffer.ino @@ -0,0 +1,20 @@ +#include +#include + +void setup() { + auto& server = WiFi.softAPDhcpServer(); + server.onSendOptions([](const DhcpServer& server, auto& options) { + // VENDOR is... vendor specific + options.add(43, { 0xca, 0xfe, 0xca, 0xfe, 0xfe }); + + // Captive Portal URI + const IPAddress gateway = netif_ip4_addr(server.getNetif()); + const String captive = F("http://") + gateway.toString(); + options.add(114, captive.c_str(), captive.length()); + }); + WiFi.softAP("TEST", "testtesttest"); +} + +void loop() { + delay(100); +} diff --git a/libraries/ESP8266WiFi/examples/HTTPSRequest/HTTPSRequest.ino b/libraries/ESP8266WiFi/examples/HTTPSRequest/HTTPSRequest.ino index 5b25b40559..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_High_Assurance_EV_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_High_Assurance_EV_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)) { @@ -77,7 +78,7 @@ void setup() { client.print(String("GET ") + url + " HTTP/1.1\r\n" + "Host: " + github_host + "\r\n" + "User-Agent: BuildFailureDetectorESP8266\r\n" + "Connection: close\r\n\r\n"); Serial.println("Request sent"); - while (client.connected()) { + while (client.available()) { String line = client.readStringUntil('\n'); if (line == "\r") { Serial.println("Headers received"); diff --git a/libraries/ESP8266WiFi/examples/HTTPSRequest/certs.h b/libraries/ESP8266WiFi/examples/HTTPSRequest/certs.h index 04765d956c..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 2021-07-26 22:04:48 +// generated on 2024-07-30 23:28:12 // by ['../../../../tools/cert.py', '-s', 'api.github.com', '-n', 'github'] #pragma once @@ -13,74 +13,72 @@ const char* github_host = "api.github.com"; const uint16_t github_port = 443; // CN: *.github.com => name: __github_com -// not valid before: 2021-03-25 00:00:00 -// not valid after: 2022-03-30 23:59:59 -const char fingerprint___github_com [] PROGMEM = "96:84:07:df:0b:1c:f6:58:14:df:d7:33:35:57:51:9b:15:4d:8c:e7"; +// 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----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAElL9/+0TidTIALPfU/tiS6pI8zAIk -rU4pohUldVc0bb6O3FARl3cnqIDK9SoF65z3xiR6XsnFS8F0Oy/chXR/kQ== +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEcAMYSUSbAQpBM6MJN5kRD5gVpxvK +QgpD4jQ4jY1CqNOeWP7fOkn+PxdiJq76Qv5bPmv3tTxD6plhoNDYDohvMg== -----END PUBLIC KEY----- )PUBKEY"; -// http://cacerts.digicert.com/DigiCertHighAssuranceTLSHybridECCSHA2562020CA1.crt -// CN: DigiCert High Assurance TLS Hybrid ECC SHA256 2020 CA1 => name: DigiCert_High_Assurance_TLS_Hybrid_ECC_SHA256_2020_CA1 -// not valid before: 2020-12-17 00:00:00 -// not valid after: 2030-12-16 23:59:59 -const char cert_DigiCert_High_Assurance_TLS_Hybrid_ECC_SHA256_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----- -MIIEGzCCAwOgAwIBAgIQBmcDW7sU/WOvwNaoU07+FjANBgkqhkiG9w0BAQsFADBs -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBFViBSb290IENBMB4XDTIwMTIxNzAwMDAwMFoXDTMwMTIxNjIzNTk1OVowZzEL -MAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMT8wPQYDVQQDEzZE -aWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBUTFMgSHlicmlkIEVDQyBTSEEyNTYgMjAy -MCBDQTEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARnvW/xPOudvtC252wTq9ef -6fbdFeWPkOscfpRTkciuHj7UcumQSH3lzkPEIx0KpesWa8epsks7QwkZ4fU/Tkf9 -o4IBhzCCAYMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUUGGmoNI1xBEq -II0fD6xC8M0pz0swHwYDVR0jBBgwFoAUsT7DaQP4v0cB1JgmGggC72NkK8MwDgYD -VR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjB/Bggr -BgEFBQcBAQRzMHEwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNv -bTBJBggrBgEFBQcwAoY9aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lD -ZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNydDBLBgNVHR8ERDBCMECgPqA8hjpo -dHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZS -b290Q0EuY3JsMDAGA1UdIAQpMCcwCAYGZ4EMAQICMAgGBmeBDAECAzAHBgVngQwB -ATAIBgZngQwBAgEwDQYJKoZIhvcNAQELBQADggEBAHMQH8hhiBfNbxwEwxbbTAnu -jPyUh/oi0JrfZI3u9JuiLqca720D6foS/AB5+4EIxpm7CMG4MdN/l7oAiDipaCPv -mOmpYUpnT7A63Cr0q4g84rI1ZmdqA40lVUUf6qC6E34tC73qDQF8TJSrfscWFdCl -RXR9J4QGrkZ2VNMSDzlDRzWCaA95MfO8x01l+ZdopdE8FvM78gGd4zxeWb8v991+ -mBxTDepqKuy/jF5Rm6Bhfxr33ADRs60s1t16dtZ3pOYLALBTPD5KhZ6a+/dk5dnh -6c4PaeZQYBUAh+GuxfaBlU4qQ8EtjBMCQHreMIwXHYHW5FRYGjgR4NMuaIw2jD0= +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/DigiCertHighAssuranceEVRootCA.crt -// CN: DigiCert High Assurance EV Root CA => name: DigiCert_High_Assurance_EV_Root_CA -// not valid before: 2006-11-10 00:00:00 -// not valid after: 2031-11-10 00:00:00 -const char cert_DigiCert_High_Assurance_EV_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----- -MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL -MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 -LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug -RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm -+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW -PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM -xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB -Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 -hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg -EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF -MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA -FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec -nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z -eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF -hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 -Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe -vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep -+OkuE6N36B9K +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/RangeExtender-NAPT/RangeExtender-NAPT.ino b/libraries/ESP8266WiFi/examples/RangeExtender-NAPT/RangeExtender-NAPT.ino index 2c62ded038..c431df572c 100644 --- a/libraries/ESP8266WiFi/examples/RangeExtender-NAPT/RangeExtender-NAPT.ino +++ b/libraries/ESP8266WiFi/examples/RangeExtender-NAPT/RangeExtender-NAPT.ino @@ -13,7 +13,6 @@ #include #include #include -#include #define NAPT 1000 #define NAPT_PORT 10 @@ -53,9 +52,13 @@ void setup() { } Serial.printf("\nSTA: %s (dns: %s / %s)\n", WiFi.localIP().toString().c_str(), WiFi.dnsIP(0).toString().c_str(), WiFi.dnsIP(1).toString().c_str()); - // give DNS servers to AP side - dhcpSoftAP.dhcps_set_dns(0, WiFi.dnsIP(0)); - dhcpSoftAP.dhcps_set_dns(1, WiFi.dnsIP(1)); + // By default, DNS option will point to the interface IP + // Instead, point it to the real DNS server. + // Notice that: + // - DhcpServer class only supports IPv4 + // - Only a single IP can be set + auto& server = WiFi.softAPDhcpServer(); + server.setDns(WiFi.dnsIP(0)); WiFi.softAPConfig( // enable AP, with android-compatible google domain IPAddress(172, 217, 28, 254), IPAddress(172, 217, 28, 254), IPAddress(255, 255, 255, 0)); diff --git a/libraries/ESP8266WiFi/examples/StaticLease/StaticLease.ino b/libraries/ESP8266WiFi/examples/StaticLease/StaticLease.ino index e4520c4720..d4ed0cd375 100644 --- a/libraries/ESP8266WiFi/examples/StaticLease/StaticLease.ino +++ b/libraries/ESP8266WiFi/examples/StaticLease/StaticLease.ino @@ -4,7 +4,6 @@ #include #include #include -#include /* Set these to your desired credentials. */ const char *ssid = "ESPap"; @@ -76,8 +75,9 @@ void setup() { ... any client not listed will use next IP address available from the range (here 192.168.0.102 and more) */ - dhcpSoftAP.add_dhcps_lease(mac_CAM); // always 192.168.0.100 - dhcpSoftAP.add_dhcps_lease(mac_PC); // always 192.168.0.101 + auto &dhcpServer = WiFi.softAPDhcpServer(); + dhcpServer.add_dhcps_lease(mac_CAM); // always 192.168.0.100 + dhcpServer.add_dhcps_lease(mac_PC); // always 192.168.0.101 /* Start Access Point. You can remove the password parameter if you want the AP to be open. */ WiFi.softAP(ssid, password); Serial.print("AP IP address: "); 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/examples/WiFiScan/WiFiScan.ino b/libraries/ESP8266WiFi/examples/WiFiScan/WiFiScan.ino index 7cb8fa9dd6..dd207141f6 100644 --- a/libraries/ESP8266WiFi/examples/WiFiScan/WiFiScan.ino +++ b/libraries/ESP8266WiFi/examples/WiFiScan/WiFiScan.ino @@ -22,7 +22,7 @@ void loop() { String ssid; int32_t rssi; uint8_t encryptionType; - uint8_t* bssid; + uint8_t *bssid; int32_t channel; bool hidden; int scanResult; @@ -40,7 +40,30 @@ void loop() { for (int8_t i = 0; i < scanResult; i++) { WiFi.getNetworkInfo(i, ssid, encryptionType, rssi, bssid, channel, hidden); - Serial.printf(PSTR(" %02d: [CH %02d] [%02X:%02X:%02X:%02X:%02X:%02X] %ddBm %c %c %s\n"), i, channel, bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], rssi, (encryptionType == ENC_TYPE_NONE) ? ' ' : '*', hidden ? 'H' : 'V', ssid.c_str()); + // get extra info + const bss_info *bssInfo = WiFi.getScanInfoByIndex(i); + String phyMode; + const char *wps = ""; + if (bssInfo) { + phyMode.reserve(12); + phyMode = F("802.11"); + String slash; + if (bssInfo->phy_11b) { + phyMode += 'b'; + slash = '/'; + } + if (bssInfo->phy_11g) { + phyMode += slash + 'g'; + slash = '/'; + } + if (bssInfo->phy_11n) { + phyMode += slash + 'n'; + } + if (bssInfo->wps) { + wps = PSTR("WPS"); + } + } + Serial.printf(PSTR(" %02d: [CH %02d] [%02X:%02X:%02X:%02X:%02X:%02X] %ddBm %c %c %-11s %3S %s\n"), i, channel, bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], rssi, (encryptionType == ENC_TYPE_NONE) ? ' ' : '*', hidden ? 'H' : 'V', phyMode.c_str(), wps, ssid.c_str()); yield(); } } else { diff --git a/libraries/ESP8266WiFi/examples/WiFiTelnetToSerial/WiFiTelnetToSerial.ino b/libraries/ESP8266WiFi/examples/WiFiTelnetToSerial/WiFiTelnetToSerial.ino index 05bdb94aa9..15c0cd38bd 100644 --- a/libraries/ESP8266WiFi/examples/WiFiTelnetToSerial/WiFiTelnetToSerial.ino +++ b/libraries/ESP8266WiFi/examples/WiFiTelnetToSerial/WiFiTelnetToSerial.ino @@ -29,9 +29,9 @@ /* SWAP_PINS: - 0: use Serial1 for logging (legacy example) + 0: use Serial1 for logging 1: configure Hardware Serial port on RX:GPIO13 TX:GPIO15 - and use SoftwareSerial for logging on + and use EspSoftwareSerial for logging on standard Serial pins RX:GPIO3 and TX:GPIO1 */ @@ -84,12 +84,14 @@ void setup() { #if SWAP_PINS Serial.swap(); // Hardware serial is now on RX:GPIO13 TX:GPIO15 - // use SoftwareSerial on regular RX(3)/TX(1) for logging + // use EspSoftwareSerial on regular RX(3)/TX(1) for logging logger = new SoftwareSerial(3, 1); logger->begin(BAUD_LOGGER); logger->enableIntTx(false); - logger->println("\n\nUsing SoftwareSerial for logging"); + logger->println("\n\nUsing EspSoftwareSerial for logging"); #else + // Hardware serial0 is on RX(3)/TX(1) + // Hardware serial1 is on (no RX)/TX(2) logger->begin(BAUD_LOGGER); logger->println("\n\nUsing Serial1 for logging"); #endif diff --git a/libraries/ESP8266WiFi/src/ArduinoWiFiServer.h b/libraries/ESP8266WiFi/src/ArduinoWiFiServer.h index 21dec5f62c..45e5e59d6e 100644 --- a/libraries/ESP8266WiFi/src/ArduinoWiFiServer.h +++ b/libraries/ESP8266WiFi/src/ArduinoWiFiServer.h @@ -32,7 +32,7 @@ class ArduinoCompatibleWiFiServerTemplate : public TServer, public Print { public: ArduinoCompatibleWiFiServerTemplate(const IPAddress& addr, uint16_t port) : TServer(addr, port) {} - ArduinoCompatibleWiFiServerTemplate(uint16_t port) : TServer(port) {} + ArduinoCompatibleWiFiServerTemplate(uint16_t port = 23) : TServer(port) {} virtual ~ArduinoCompatibleWiFiServerTemplate() {} // https://www.arduino.cc/en/Reference/WiFiServerAvailable diff --git a/libraries/ESP8266WiFi/src/BearSSLHelpers.cpp b/libraries/ESP8266WiFi/src/BearSSLHelpers.cpp index 08549cc99a..0b18334cbd 100644 --- a/libraries/ESP8266WiFi/src/BearSSLHelpers.cpp +++ b/libraries/ESP8266WiFi/src/BearSSLHelpers.cpp @@ -937,11 +937,15 @@ uint32_t SigningVerifier::length() // directly inside the class function for ease of use. extern "C" bool SigningVerifier_verify(PublicKey *_pubKey, UpdaterHashClass *hash, const void *signature, uint32_t signatureLen) { if (_pubKey->isRSA()) { - bool ret; - unsigned char vrf[hash->len()]; + // see https://github.com/earlephilhower/bearssl-esp8266/blob/6105635531027f5b298aa656d44be2289b2d434f/inc/bearssl_rsa.h#L257 + static constexpr int HashLengthMax = 64; + unsigned char vrf[HashLengthMax]; + if (hash->len() > HashLengthMax) { + return false; + } br_rsa_pkcs1_vrfy vrfy = br_rsa_pkcs1_vrfy_get_default(); - ret = vrfy((const unsigned char *)signature, signatureLen, hash->oid(), sizeof(vrf), _pubKey->getRSA(), vrf); - if (!ret || memcmp(vrf, hash->hash(), sizeof(vrf)) ) { + bool ret = vrfy((const unsigned char *)signature, signatureLen, hash->oid(), hash->len(), _pubKey->getRSA(), vrf); + if (!ret || memcmp(vrf, hash->hash(), std::min(HashLengthMax, hash->len())) ) { return false; } else { return true; diff --git a/libraries/ESP8266WiFi/src/BearSSLHelpers.h b/libraries/ESP8266WiFi/src/BearSSLHelpers.h index 397a557522..3033a80aec 100644 --- a/libraries/ESP8266WiFi/src/BearSSLHelpers.h +++ b/libraries/ESP8266WiFi/src/BearSSLHelpers.h @@ -58,6 +58,7 @@ class PublicKey { // Disable the copy constructor, we're pointer based PublicKey(const PublicKey& that) = delete; + PublicKey& operator=(const PublicKey& that) = delete; private: brssl::public_key *_key; @@ -86,6 +87,7 @@ class PrivateKey { // Disable the copy constructor, we're pointer based PrivateKey(const PrivateKey& that) = delete; + PrivateKey& operator=(const PrivateKey& that) = delete; private: brssl::private_key *_key; @@ -122,6 +124,7 @@ class X509List { // Disable the copy constructor, we're pointer based X509List(const X509List& that) = delete; + X509List& operator=(const X509List& that) = delete; private: size_t _count; diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiAP.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiAP.cpp index b0a0084853..cab599b6a8 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiAP.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiAP.cpp @@ -26,6 +26,8 @@ #include "ESP8266WiFiGeneric.h" #include "ESP8266WiFiAP.h" +#include + extern "C" { #include "c_types.h" #include "ets_sys.h" @@ -37,7 +39,6 @@ extern "C" { } #include "debug.h" -#include "LwipDhcpServer.h" // ----------------------------------------------------------------------------------------------------------------------- // ---------------------------------------------------- Private functions ------------------------------------------------ @@ -93,8 +94,9 @@ static bool softap_config_equal(const softap_config& lhs, const softap_config& r * @param channel WiFi channel number, 1 - 13. * @param ssid_hidden Network cloaking (0 = broadcast SSID, 1 = hide SSID) * @param max_connection Max simultaneous connected clients, 0 - 8. https://bbs.espressif.com/viewtopic.php?f=46&t=481&p=1832&hilit=max_connection#p1832 + * @param beacon_interval set arbitrary beacon interval (influences DTIM) */ -bool ESP8266WiFiAPClass::softAP(const char* ssid, const char* psk, int channel, int ssid_hidden, int max_connection) { +bool ESP8266WiFiAPClass::softAP(const char* ssid, const char* psk, int channel, int ssid_hidden, int max_connection, int beacon_interval) { if(!WiFi.enableAP(true)) { // enable AP failed @@ -137,7 +139,7 @@ bool ESP8266WiFiAPClass::softAP(const char* ssid, const char* psk, int channel, conf.channel = channel; conf.ssid_hidden = ssid_hidden; conf.max_connection = max_connection; - conf.beacon_interval = 100; + conf.beacon_interval = beacon_interval; struct softap_config conf_compare; if(WiFi._persistent){ @@ -166,33 +168,30 @@ bool ESP8266WiFiAPClass::softAP(const char* ssid, const char* psk, int channel, DEBUG_WIFI("[AP] softap config unchanged\n"); } - dhcpSoftAP.end(); + wifi_softap_dhcps_stop(); // check IP config struct ip_info ip; if(wifi_get_ip_info(SOFTAP_IF, &ip)) { if(ip.ip.addr == 0x00000000) { - // Invalid config DEBUG_WIFI("[AP] IP config Invalid resetting...\n"); - //192.168.4.1 , 192.168.4.1 , 255.255.255.0 - ret = softAPConfig(0x0104A8C0, 0x0104A8C0, 0x00FFFFFF); - if(!ret) { - DEBUG_WIFI("[AP] softAPConfig failed!\n"); - ret = false; - } + ret = softAPConfig( + IPAddress(192, 168, 4, 1), + IPAddress(192, 168, 4, 1), + IPAddress(255, 255, 255, 0)); } } else { DEBUG_WIFI("[AP] wifi_get_ip_info failed!\n"); ret = false; } - dhcpSoftAP.begin(&ip); + wifi_softap_dhcps_start(); return ret; } -bool ESP8266WiFiAPClass::softAP(const String& ssid, const String& psk, int channel, int ssid_hidden, int max_connection) { - return softAP(ssid.c_str(), psk.c_str(), channel, ssid_hidden, max_connection); +bool ESP8266WiFiAPClass::softAP(const String& ssid, const String& psk, int channel, int ssid_hidden, int max_connection, int beacon_interval) { + return softAP(ssid.c_str(), psk.c_str(), channel, ssid_hidden, max_connection, beacon_interval); } /** @@ -224,10 +223,10 @@ bool ESP8266WiFiAPClass::softAPConfig(IPAddress local_ip, IPAddress gateway, IPA info.gw.addr = gateway.v4(); info.netmask.addr = subnet.v4(); - if(!wifi_softap_dhcps_stop()) { - DEBUG_WIFI("[APConfig] wifi_softap_dhcps_stop failed!\n"); - } - + // use SDK function for dhcps, not just server.begin() + // setting info with static IPs will fail otherwise + // (TODO: dhcps_flag seems to store 'SDK' DHCPs status) + wifi_softap_dhcps_stop(); if(!wifi_set_ip_info(SOFTAP_IF, &info)) { DEBUG_WIFI("[APConfig] wifi_set_ip_info failed!\n"); ret = false; @@ -244,30 +243,17 @@ bool ESP8266WiFiAPClass::softAPConfig(IPAddress local_ip, IPAddress gateway, IPA dhcp_lease.end_ip.addr = ip.v4(); DEBUG_WIFI("[APConfig] DHCP IP end: %s\n", ip.toString().c_str()); - if(!dhcpSoftAP.set_dhcps_lease(&dhcp_lease)) + auto& server = softAPDhcpServer(); + if(!server.set_dhcps_lease(&dhcp_lease)) { - DEBUG_WIFI("[APConfig] wifi_set_ip_info failed!\n"); + DEBUG_WIFI("[APConfig] server set_dhcps_lease failed!\n"); ret = false; } - // set lease time to 720min --> 12h - if(!dhcpSoftAP.set_dhcps_lease_time(720)) - { - DEBUG_WIFI("[APConfig] wifi_softap_set_dhcps_lease_time failed!\n"); - ret = false; - } + // send ROUTER option with netif's gateway IP + server.setRouter(true); - uint8 mode = info.gw.addr ? 1 : 0; - if(!dhcpSoftAP.set_dhcps_offer_option(OFFER_ROUTER, &mode)) - { - DEBUG_WIFI("[APConfig] wifi_softap_set_dhcps_offer_option failed!\n"); - ret = false; - } - - if(!wifi_softap_dhcps_start()) { - DEBUG_WIFI("[APConfig] wifi_softap_dhcps_start failed!\n"); - ret = false; - } + wifi_softap_dhcps_start(); // check config if(wifi_get_ip_info(SOFTAP_IF, &info)) { @@ -291,7 +277,7 @@ bool ESP8266WiFiAPClass::softAPConfig(IPAddress local_ip, IPAddress gateway, IPA /** * Disconnect from the network (close AP) * @param wifioff disable mode? - * @return one value of wl_status_t enum + * @return operation success */ bool ESP8266WiFiAPClass::softAPdisconnect(bool wifioff) { bool ret; @@ -337,6 +323,17 @@ IPAddress ESP8266WiFiAPClass::softAPIP() { return IPAddress(ip.ip.addr); } +/** + * Get the softAP broadcast ip address. + * @return IPAddress softAP broadcast IP + */ +IPAddress ESP8266WiFiAPClass::softAPbroadcastIP() +{ + struct ip_info ip; + wifi_get_ip_info(SOFTAP_IF, &ip); + return IPAddress(ip.ip.addr | ~(ip.netmask.addr)); +} + /** * Get the softAP interface MAC address. @@ -389,3 +386,11 @@ String ESP8266WiFiAPClass::softAPPSK() const { return psk; } + +/** + * Get the static DHCP server instance attached to the softAP interface + * @return DhcpServer instance. + */ +DhcpServer& ESP8266WiFiAPClass::softAPDhcpServer() { + return getNonOSDhcpServer(); +} diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiAP.h b/libraries/ESP8266WiFi/src/ESP8266WiFiAP.h index b8b3b8fb12..1bbc3a6d89 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiAP.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiAP.h @@ -27,6 +27,7 @@ #include "ESP8266WiFiType.h" #include "ESP8266WiFiGeneric.h" +#include class ESP8266WiFiAPClass { @@ -36,20 +37,23 @@ class ESP8266WiFiAPClass { public: - bool softAP(const char* ssid, const char* psk = NULL, int channel = 1, int ssid_hidden = 0, int max_connection = 4); - bool softAP(const String& ssid,const String& psk = emptyString,int channel = 1,int ssid_hidden = 0,int max_connection = 4); + bool softAP(const char* ssid, const char* psk = NULL, int channel = 1, int ssid_hidden = 0, int max_connection = 4, int beacon_interval = 100); + bool softAP(const String& ssid,const String& psk = emptyString,int channel = 1,int ssid_hidden = 0,int max_connection = 4,int beacon_interval = 100); bool softAPConfig(IPAddress local_ip, IPAddress gateway, IPAddress subnet); bool softAPdisconnect(bool wifioff = false); uint8_t softAPgetStationNum(); IPAddress softAPIP(); + IPAddress softAPbroadcastIP(); uint8_t* softAPmacAddress(uint8_t* mac); String softAPmacAddress(void); - String softAPSSID() const; - String softAPPSK() const; + String softAPSSID() const; + String softAPPSK() const; + + static DhcpServer& softAPDhcpServer(); protected: diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp index 4d3d5fae82..f64e6d3ac5 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp @@ -22,8 +22,11 @@ */ +#include #include -#include +#include +#include + #include #include #include "ESP8266WiFi.h" @@ -49,6 +52,9 @@ extern "C" { #include "debug.h" #include "include/WiFiState.h" +// see comments on wifi_station_hostname in LwipIntf.cpp +extern "C" char* wifi_station_hostname; // sdk's hostname location + // ----------------------------------------------------------------------------------------------------------------------- // ------------------------------------------------- Generic WiFi function ----------------------------------------------- // ----------------------------------------------------------------------------------------------------------------------- @@ -222,9 +228,14 @@ void ESP8266WiFiGenericClass::_eventCallback(void* arg) System_Event_t* event = reinterpret_cast(arg); DEBUG_WIFI("wifi evt: %d\n", event->event); - if(event->event == EVENT_STAMODE_DISCONNECTED) { + if (event->event == EVENT_STAMODE_DISCONNECTED) { DEBUG_WIFI("STA disconnect: %d\n", event->event_info.disconnected.reason); - WiFiClient::stopAll(); + // workaround for https://github.com/esp8266/Arduino/issues/7432 + // still delivers the event, just handle this specific case + if ((wifi_station_get_connect_status() == STATION_GOT_IP) && !wifi_station_get_reconnect_policy()) { + DEBUG_WIFI("forcibly stopping the station connection manager\n"); + wifi_station_disconnect(); + } } if (event->event == EVENT_STAMODE_AUTHMODE_CHANGE) { @@ -295,7 +306,7 @@ bool ESP8266WiFiGenericClass::setSleepMode(WiFiSleepType_t type, uint8_t listenI */ -#ifdef NONOSDK3V0 +#if (NONOSDK >= (0x30000)) #ifdef DEBUG_ESP_WIFI if (listenInterval && type == WIFI_NONE_SLEEP) @@ -326,9 +337,9 @@ bool ESP8266WiFiGenericClass::setSleepMode(WiFiSleepType_t type, uint8_t listenI } } } -#else // !defined(NONOSDK3V0) +#else // (NONOSDK >= (0x30000)) (void)listenInterval; -#endif // !defined(NONOSDK3V0) +#endif // (NONOSDK >= (0x30000)) bool ret = wifi_set_sleep_type((sleep_type_t) type); if (!ret) { @@ -414,7 +425,10 @@ bool ESP8266WiFiGenericClass::mode(WiFiMode_t m) { return true; } + char backup_hostname [33] { 0 }; // hostname is 32 chars long (RFC) + if (m != WIFI_OFF && wifi_fpm_get_sleep_type() != NONE_SLEEP_T) { + memcpy(backup_hostname, wifi_station_hostname, sizeof(backup_hostname)); // wifi starts asleep by default wifi_fpm_do_wakeup(); wifi_fpm_close(); @@ -437,9 +451,8 @@ bool ESP8266WiFiGenericClass::mode(WiFiMode_t m) { //tasks to wait correctly. constexpr unsigned int timeoutValue = 1000; //1 second if(can_yield()) { - // The final argument, intvl_ms, to esp_delay influences how frequently - // the scheduled recurrent functions (Schedule.h) are probed. - esp_delay(timeoutValue, [m]() { return wifi_get_opmode() != m; }, 5); + // check opmode every 100ms or give up after timeout + esp_delay(timeoutValue, [m]() { return wifi_get_opmode() != m; }, 100); //if at this point mode still hasn't been reached, give up if(wifi_get_opmode() != (uint8) m) { @@ -447,6 +460,9 @@ bool ESP8266WiFiGenericClass::mode(WiFiMode_t m) { } } + if (backup_hostname[0]) + memcpy(wifi_station_hostname, backup_hostname, sizeof(backup_hostname)); + return ret; } @@ -557,10 +573,10 @@ bool ESP8266WiFiGenericClass::forceSleepWake() { * @return interval */ uint8_t ESP8266WiFiGenericClass::getListenInterval () { -#ifndef NONOSDK3V0 - return 0; -#else +#if (NONOSDK >= (0x30000)) return wifi_get_listen_interval(); +#else + return 0; #endif } @@ -569,10 +585,10 @@ uint8_t ESP8266WiFiGenericClass::getListenInterval () { * @return true if max level */ bool ESP8266WiFiGenericClass::isSleepLevelMax () { -#ifndef NONOSDK3V0 - return false; -#else +#if (NONOSDK >= (0x30000)) return wifi_get_sleep_level() == MAX_SLEEP_T; +#else + return false; #endif } @@ -581,112 +597,102 @@ bool ESP8266WiFiGenericClass::isSleepLevelMax () { // ------------------------------------------------ Generic Network function --------------------------------------------- // ----------------------------------------------------------------------------------------------------------------------- -void wifi_dns_found_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg); +namespace { -static bool _dns_lookup_pending = false; +struct _dns_found_result { + IPAddress addr; + bool done; +}; -/** - * Resolve the given hostname to an IP address. - * @param aHostname Name to be resolved - * @param aResult IPAddress structure to store the returned IP address - * @return 1 if aIPAddrString was successfully converted to an IP address, - * else 0 - */ -int ESP8266WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult) -{ - return hostByName(aHostname, aResult, 10000); } +static void _dns_found_callback(const char *, const ip_addr_t *, void *); -int ESP8266WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult, uint32_t timeout_ms) -{ - ip_addr_t addr; - aResult = static_cast(INADDR_NONE); - - if(aResult.fromString(aHostname)) { - // Host name is a IP address use it! - DEBUG_WIFI_GENERIC("[hostByName] Host: %s is a IP!\n", aHostname); +static int hostByNameImpl(const char* aHostname, IPAddress& aResult, uint32_t timeout_ms, DNSResolveType resolveType) { + if (aResult.fromString(aHostname)) { + DEBUG_WIFI_GENERIC("[hostByName] Host: %s is IP!\n", aHostname); return 1; } + static_assert(std::is_same_v>, ""); DEBUG_WIFI_GENERIC("[hostByName] request IP for: %s\n", aHostname); -#if LWIP_IPV4 && LWIP_IPV6 - err_t err = dns_gethostbyname_addrtype(aHostname, &addr, &wifi_dns_found_callback, &aResult,LWIP_DNS_ADDRTYPE_DEFAULT); -#else - err_t err = dns_gethostbyname(aHostname, &addr, &wifi_dns_found_callback, &aResult); -#endif - if(err == ERR_OK) { - aResult = IPAddress(&addr); - } else if(err == ERR_INPROGRESS) { - _dns_lookup_pending = true; - // Will resume on timeout or when wifi_dns_found_callback fires. - // The final argument, intvl_ms, to esp_delay influences how frequently - // the scheduled recurrent functions (Schedule.h) are probed; here, to allow - // the ethernet driver perform work. - esp_delay(timeout_ms, []() { return _dns_lookup_pending; }, 1); - _dns_lookup_pending = false; - if(aResult.isSet()) { - err = ERR_OK; + + ip_addr_t addr; + auto pending = std::make_unique<_dns_found_result>( + _dns_found_result{ + .addr = IPADDR_NONE, + .done = false, + }); + + err_t err = dns_gethostbyname_addrtype(aHostname, + &addr, &_dns_found_callback, pending.get(), + static_cast(resolveType)); + + switch (err) { + // Address already known + case ERR_OK: + aResult = addr; + break; + + // We are no longer able to issue requests + case ERR_MEM: + break; + + // We need to wait for c/b to fire *or* we exit on our own timeout + // (which also requires us to notify the c/b that it is supposed to delete the pending obj) + case ERR_INPROGRESS: + // sleep until dns_found_callback is called or timeout is reached + esp_delay(timeout_ms, [&]() { return !pending->done; }); + + if (pending->done) { + if ((pending->addr).isSet()) { + aResult = pending->addr; + err = ERR_OK; + } + } else { + pending->done = true; + pending.release(); + err = ERR_TIMEOUT; } + + break; } - if(err == ERR_OK) { + if (err == ERR_OK) { DEBUG_WIFI_GENERIC("[hostByName] Host: %s IP: %s\n", aHostname, aResult.toString().c_str()); return 1; } - - DEBUG_WIFI_GENERIC("[hostByName] Host: %s lookup error: %s (%d)!\n", aHostname, lwip_strerr(err), (int)err); + + DEBUG_WIFI_GENERIC("[hostByName] Host: %s lookup error: %s (%d)!\n", + aHostname, + (err == ERR_TIMEOUT) ? "Timeout" : + (err == ERR_INPROGRESS) ? "No response" : + "Unknown", static_cast(err)); + return 0; } -#if LWIP_IPV4 && LWIP_IPV6 -int ESP8266WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult, uint32_t timeout_ms, DNSResolveType resolveType) +/** + * Resolve the given hostname to an IP address. + * @param aHostname Name to be resolved + * @param aResult IPAddress structure to store the returned IP address + * @return 1 if aIPAddrString was successfully converted to an IP address, + * else 0 + */ +int ESP8266WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult) { - ip_addr_t addr; - err_t err; - aResult = static_cast(INADDR_NONE); - - if(aResult.fromString(aHostname)) { - // Host name is a IP address use it! - DEBUG_WIFI_GENERIC("[hostByName] Host: %s is a IP!\n", aHostname); - return 1; - } - - DEBUG_WIFI_GENERIC("[hostByName] request IP for: %s\n", aHostname); - switch(resolveType) - { - // Use selected addrtype - case DNSResolveType::DNS_AddrType_IPv4: - case DNSResolveType::DNS_AddrType_IPv6: - case DNSResolveType::DNS_AddrType_IPv4_IPv6: - case DNSResolveType::DNS_AddrType_IPv6_IPv4: - err = dns_gethostbyname_addrtype(aHostname, &addr, &wifi_dns_found_callback, &aResult, (uint8_t) resolveType); - break; - default: - err = dns_gethostbyname_addrtype(aHostname, &addr, &wifi_dns_found_callback, &aResult, LWIP_DNS_ADDRTYPE_DEFAULT); // If illegal type, use default. - break; - } - - if(err == ERR_OK) { - aResult = IPAddress(&addr); - } else if(err == ERR_INPROGRESS) { - _dns_lookup_pending = true; - // will resume on timeout or when wifi_dns_found_callback fires - esp_delay(timeout_ms, []() { return _dns_lookup_pending; }); - _dns_lookup_pending = false; - // will return here when dns_found_callback fires - if(aResult.isSet()) { - err = ERR_OK; - } - } + return hostByNameImpl(aHostname, aResult, DNSDefaultTimeoutMs, DNSResolveTypeDefault); +} - if(err == ERR_OK) { - DEBUG_WIFI_GENERIC("[hostByName] Host: %s IP: %s\n", aHostname, aResult.toString().c_str()); - return 1; - } +int ESP8266WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult, uint32_t timeout_ms) +{ + return hostByNameImpl(aHostname, aResult, timeout_ms, DNSResolveTypeDefault); +} - DEBUG_WIFI_GENERIC("[hostByName] Host: %s lookup error: %d!\n", aHostname, (int)err); - return 0; +#if LWIP_IPV4 && LWIP_IPV6 +int ESP8266WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult, uint32_t timeout_ms, DNSResolveType resolveType) +{ + return hostByNameImpl(aHostname, aResult, timeout_ms, resolveType); } #endif @@ -696,16 +702,19 @@ int ESP8266WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResul * @param ipaddr * @param callback_arg */ -void wifi_dns_found_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg) +static void _dns_found_callback(const char*, const ip_addr_t* ipaddr, void* arg) { - (void) name; - if (!_dns_lookup_pending) { + auto result = reinterpret_cast<_dns_found_result*>(arg); + if (result->done) { + delete result; return; } - if(ipaddr) { - (*reinterpret_cast(callback_arg)) = IPAddress(ipaddr); + + if (ipaddr) { + result->addr = IPAddress(ipaddr); } - _dns_lookup_pending = false; // resume hostByName + + result->done = true; esp_schedule(); } diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h index 3d3ceb3262..e0ad560d0e 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h @@ -24,6 +24,10 @@ #define ESP8266WIFIGENERIC_H_ #include "ESP8266WiFiType.h" + +#include +#include + #include #include @@ -44,12 +48,15 @@ typedef void (*WiFiEventCb)(WiFiEvent_t); enum class DNSResolveType: uint8_t { - DNS_AddrType_IPv4 = 0, // LWIP_DNS_ADDRTYPE_IPV4 = 0 - DNS_AddrType_IPv6, // LWIP_DNS_ADDRTYPE_IPV6 = 1 - DNS_AddrType_IPv4_IPv6, // LWIP_DNS_ADDRTYPE_IPV4_IPV6 = 2 - DNS_AddrType_IPv6_IPv4 // LWIP_DNS_ADDRTYPE_IPV6_IPV4 = 3 + DNS_AddrType_IPv4 = LWIP_DNS_ADDRTYPE_IPV4, + DNS_AddrType_IPv6 = LWIP_DNS_ADDRTYPE_IPV6, + DNS_AddrType_IPv4_IPv6 = LWIP_DNS_ADDRTYPE_IPV4_IPV6, + DNS_AddrType_IPv6_IPv4 = LWIP_DNS_ADDRTYPE_IPV6_IPV4, }; +inline constexpr auto DNSDefaultTimeoutMs = 10000; +inline constexpr auto DNSResolveTypeDefault = static_cast(LWIP_DNS_ADDRTYPE_DEFAULT); + struct WiFiState; class ESP8266WiFiGenericClass { @@ -64,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.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.cpp index dada704ab1..bcd433e1b1 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.cpp @@ -84,13 +84,13 @@ static void printWiFiStatus(wl_status_t status) static wl_status_t waitWiFiConnect(uint32_t connectTimeoutMs) { wl_status_t status = WL_CONNECT_FAILED; - // The final argument, intvl_ms, to esp_delay influences how frequently - // the scheduled recurrent functions (Schedule.h) are probed. + // Wait for WiFi to connect + // stop waiting upon status checked every 100ms or when timeout is reached esp_delay(connectTimeoutMs, [&status]() { status = WiFi.status(); return status != WL_CONNECTED && status != WL_CONNECT_FAILED; - }, 0); + }, 100); // Check status if (status == WL_CONNECTED) { @@ -236,13 +236,12 @@ int8_t ESP8266WiFiMulti::startScan() WiFi.scanNetworks(true); // Wait for WiFi scan change or timeout - // The final argument, intvl_ms, to esp_delay influences how frequently - // the scheduled recurrent functions (Schedule.h) are probed. + // stop waiting upon status checked every 100ms or when timeout is reached esp_delay(WIFI_SCAN_TIMEOUT_MS, [&scanResult]() { scanResult = WiFi.scanComplete(); return scanResult < 0; - }, 0); + }, 100); // Check for scan timeout which may occur when scan does not report completion if (scanResult < 0) { DEBUG_WIFI_MULTI("[WIFIM] Scan timeout\n"); 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/ESP8266WiFiSTA.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp index c1c63996aa..dfea41298b 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp @@ -61,7 +61,7 @@ static bool sta_config_equal(const station_config& lhs, const station_config& rh */ static bool sta_config_equal(const station_config& lhs, const station_config& rhs) { -#ifdef NONOSDK3V0 +#if (NONOSDK >= (0x30000)) static_assert(sizeof(station_config) == 116, "struct station_config has changed, please update comparison function"); #else static_assert(sizeof(station_config) == 112, "struct station_config has changed, please update comparison function"); @@ -94,8 +94,18 @@ static bool sta_config_equal(const station_config& lhs, const station_config& rh return false; } -#ifdef NONOSDK3V0 - if (lhs.open_and_wep_mode_disable != rhs.open_and_wep_mode_disable) { +#if (NONOSDK >= (0x30000)) + if(lhs.open_and_wep_mode_disable != rhs.open_and_wep_mode_disable) { + return false; + } +#endif + +#if (NONOSDK >= (0x30200)) + if(lhs.channel != rhs.channel) { + return false; + } + + if(lhs.all_channel_scan != rhs.all_channel_scan) { return false; } #endif @@ -156,9 +166,13 @@ wl_status_t ESP8266WiFiSTAClass::begin(const char* ssid, const char *passphrase, } conf.threshold.rssi = -127; -#ifdef NONOSDK3V0 +#if (NONOSDK >= (0x30000)) conf.open_and_wep_mode_disable = !(_useInsecureWEP || *conf.password == 0); #endif +#if (NONOSDK >= (0x30200)) + conf.channel = channel; + conf.all_channel_scan = true; +#endif if(bssid) { conf.bssid_set = 1; @@ -325,6 +339,45 @@ bool ESP8266WiFiSTAClass::config(IPAddress local_ip, IPAddress arg1, IPAddress a return true; } +bool ESP8266WiFiSTAClass::config(IPAddress local_ip, IPAddress dns) { + + if (!local_ip.isSet()) + return config(INADDR_ANY, INADDR_ANY, INADDR_ANY); + + if (!local_ip.isV4()) + return false; + + IPAddress gw(local_ip); + gw[3] = 1; + if (!dns.isSet()) { + dns = gw; + } + return config(local_ip, dns, gw); +} + +/** + * Change DNS for static IP configuration + * @param dns1 Static DNS server 1 + * @param dns2 Static DNS server 2 (optional) + */ +bool ESP8266WiFiSTAClass::setDNS(IPAddress dns1, IPAddress dns2) { + + if((WiFi.getMode() & WIFI_STA) == 0) + return false; + + if(dns1.isSet()) { + // Set DNS1-Server + dns_setserver(0, dns1); + } + + if(dns2.isSet()) { + // Set DNS2-Server + dns_setserver(1, dns2); + } + + return true; +} + /** * will force a disconnect an then start reconnecting to AP * @return ok @@ -339,15 +392,42 @@ bool ESP8266WiFiSTAClass::reconnect() { } /** - * Disconnect from the network - * @param wifioff + * Disconnect from the network with clearing saved credentials + * @param wifioff Bool indicating whether STA should be disabled. * @return one value of wl_status_t enum */ bool ESP8266WiFiSTAClass::disconnect(bool wifioff) { + // Disconnect with clearing saved credentials. + return disconnect(wifioff, true); +} + +/** + * Disconnect from the network + * @param wifioff Bool indicating whether STA should be disabled. + * @param eraseCredentials Bool indicating whether saved credentials should be erased. + * @return one value of wl_status_t enum + */ +bool ESP8266WiFiSTAClass::disconnect(bool wifioff, bool eraseCredentials) { bool ret = false; - struct station_config conf; - *conf.ssid = 0; - *conf.password = 0; + + if (eraseCredentials) { + // Read current config. + struct station_config conf; + wifi_station_get_config(&conf); + + // Erase credentials. + memset(&conf.ssid, 0, sizeof(conf.ssid)); + memset(&conf.password, 0, sizeof(conf.password)); + + // Store modiffied config. + ETS_UART_INTR_DISABLE(); + if(WiFi._persistent) { + wifi_station_set_config(&conf); + } else { + wifi_station_set_config_current(&conf); + } + ETS_UART_INTR_ENABLE(); + } // API Reference: wifi_station_disconnect() need to be called after system initializes and the ESP8266 Station mode is enabled. if (WiFi.getMode() & WIFI_STA) @@ -355,15 +435,6 @@ bool ESP8266WiFiSTAClass::disconnect(bool wifioff) { else ret = true; - ETS_UART_INTR_DISABLE(); - if(WiFi._persistent) { - wifi_station_set_config(&conf); - } else { - wifi_station_set_config_current(&conf); - } - - ETS_UART_INTR_ENABLE(); - if(wifioff) { WiFi.enableSTA(false); } @@ -575,6 +646,18 @@ uint8_t* ESP8266WiFiSTAClass::BSSID(void) { return reinterpret_cast(conf.bssid); } +/** + * Fill the current bssid / mac associated with the network if configured + * @param bssid pointer to uint8_t array with length WL_MAC_ADDR_LENGTH + * @return bssid uint8_t * + */ +uint8_t* ESP8266WiFiSTAClass::BSSID(uint8_t* bssid) { + struct station_config conf; + wifi_station_get_config(&conf); + memcpy(bssid, conf.bssid, WL_MAC_ADDR_LENGTH); + return bssid; +} + /** * Return the current bssid / mac associated with the network if configured * @return String bssid mac diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.h b/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.h index 32813a5377..ad1b655a55 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.h @@ -45,10 +45,19 @@ class ESP8266WiFiSTAClass: public LwipIntf { //The argument order for ESP is not the same as for Arduino. However, there is compatibility code under the hood //to detect Arduino arg order, and handle it correctly. Be aware that the Arduino default value handling doesn't //work here (see Arduino docs for gway/subnet defaults). In other words: at least 3 args must always be given. - bool config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1 = (uint32_t)0x00000000, IPAddress dns2 = (uint32_t)0x00000000); + bool config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1 = INADDR_ANY, IPAddress dns2 = INADDR_ANY); + + // two and one parameter version. 2nd parameter is DNS like in Arduino + // IPv4 only + [[deprecated("It is discouraged to use this 1 or 2 parameters network configuration legacy function config(ip[,dns]) as chosen defaults may not match the local network configuration")]] + bool config(IPAddress local_ip, IPAddress dns = INADDR_ANY); + + bool setDNS(IPAddress dns1, IPAddress dns2 = INADDR_ANY); bool reconnect(); + bool disconnect(bool wifioff = false); + bool disconnect(bool wifioff, bool eraseCredentials); bool isConnected(); @@ -77,6 +86,7 @@ class ESP8266WiFiSTAClass: public LwipIntf { String psk() const; uint8_t * BSSID(); + uint8_t * BSSID(uint8_t* bssid); String BSSIDstr(); int8_t RSSI(); diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiScan.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiScan.cpp index 6aaf432e2d..1fdc6e04c7 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiScan.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiScan.cpp @@ -147,6 +147,14 @@ void ESP8266WiFiScanClass::scanDelete() { _scanComplete = false; } +/** + * returns const pointer to the requested scanned wifi entry for furthor parsing. + * @param networkItem int + * @return struct bss_info*, may be NULL + */ +const bss_info *ESP8266WiFiScanClass::getScanInfoByIndex(int i) { + return reinterpret_cast(_getScanInfoByIndex(i)); +}; /** * loads all infos from a scanned wifi in to the ptr parameters @@ -251,6 +259,21 @@ uint8_t * ESP8266WiFiScanClass::BSSID(uint8_t i) { return it->bssid; } +/** + * fill MAC / BSSID of scanned wifi + * @param i specify from which network item want to get the information + * @param bssid pointer to uint8_t array with length WL_MAC_ADDR_LENGTH + * @return uint8_t * MAC / BSSID of scanned wifi + */ +uint8_t * ESP8266WiFiScanClass::BSSID(uint8_t i, uint8_t* bssid) { + struct bss_info* it = reinterpret_cast(_getScanInfoByIndex(i)); + if(!it) { + return 0; + } + memcpy(bssid, it->bssid, WL_MAC_ADDR_LENGTH); + return bssid; +} + /** * return MAC / BSSID of scanned wifi * @param i specify from which network item want to get the information diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiScan.h b/libraries/ESP8266WiFi/src/ESP8266WiFiScan.h index e799c4bd7d..1c9c3a7408 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiScan.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiScan.h @@ -41,12 +41,14 @@ class ESP8266WiFiScanClass { void scanDelete(); // scan result + const bss_info *getScanInfoByIndex(int i); bool getNetworkInfo(uint8_t networkItem, String &ssid, uint8_t &encryptionType, int32_t &RSSI, uint8_t* &BSSID, int32_t &channel, bool &isHidden); String SSID(uint8_t networkItem); uint8_t encryptionType(uint8_t networkItem); int32_t RSSI(uint8_t networkItem); uint8_t * BSSID(uint8_t networkItem); + uint8_t * BSSID(uint8_t networkItem, uint8_t* bssid); String BSSIDstr(uint8_t networkItem); int32_t channel(uint8_t networkItem); bool isHidden(uint8_t networkItem); diff --git a/libraries/ESP8266WiFi/src/WiFi.h b/libraries/ESP8266WiFi/src/WiFi.h new file mode 100644 index 0000000000..379989252d --- /dev/null +++ b/libraries/ESP8266WiFi/src/WiFi.h @@ -0,0 +1,2 @@ + +#include "ESP8266WiFi.h" \ No newline at end of file diff --git a/libraries/ESP8266WiFi/src/WiFiClient.cpp b/libraries/ESP8266WiFi/src/WiFiClient.cpp index 74abc23655..fb10209ec0 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClient.cpp @@ -20,8 +20,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#define LWIP_INTERNAL - extern "C" { #include "wl_definitions.h" @@ -378,6 +376,17 @@ uint16_t WiFiClient::localPort() return _client->getLocalPort(); } +// Api for heap saving. Optional use instead of WiFiClient::stop to systematically retreive some heap memory +// and avoiding server crashes in case of frequent clients connections. +void WiFiClient::abort() +{ + if (!_client) + return; + + flush(0); // Flush output buffer. Don't make any use of return boolean. + _client->abort(); // Wich in turn calls tcp_abort which calls tcp_abandon(). +} + void WiFiClient::stopAll() { for (WiFiClient* it = _s_first; it; it = it->_next) { diff --git a/libraries/ESP8266WiFi/src/WiFiClient.h b/libraries/ESP8266WiFi/src/WiFiClient.h index 5065622cc2..711adb6204 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.h +++ b/libraries/ESP8266WiFi/src/WiFiClient.h @@ -91,10 +91,10 @@ class WiFiClient : public Client, public SList { virtual uint8_t connected() override; virtual operator bool() override; - IPAddress remoteIP(); - uint16_t remotePort(); - IPAddress localIP(); - uint16_t localPort(); + virtual IPAddress remoteIP(); + virtual uint16_t remotePort(); + virtual IPAddress localIP(); + virtual uint16_t localPort(); static void setLocalPortStart(uint16_t port) { _localPort = port; } @@ -103,16 +103,16 @@ class WiFiClient : public Client, public SList { friend class WiFiServer; using Print::write; - + static void stopAll(); static void stopAllExcept(WiFiClient * c); - void keepAlive (uint16_t idle_sec = TCP_DEFAULT_KEEPALIVE_IDLE_SEC, uint16_t intv_sec = TCP_DEFAULT_KEEPALIVE_INTERVAL_SEC, uint8_t count = TCP_DEFAULT_KEEPALIVE_COUNT); - bool isKeepAliveEnabled () const; - uint16_t getKeepAliveIdle () const; - uint16_t getKeepAliveInterval () const; - uint8_t getKeepAliveCount () const; - void disableKeepAlive () { keepAlive(0, 0, 0); } + virtual void keepAlive (uint16_t idle_sec = TCP_DEFAULT_KEEPALIVE_IDLE_SEC, uint16_t intv_sec = TCP_DEFAULT_KEEPALIVE_INTERVAL_SEC, uint8_t count = TCP_DEFAULT_KEEPALIVE_COUNT); + virtual bool isKeepAliveEnabled () const; + virtual uint16_t getKeepAliveIdle () const; + virtual uint16_t getKeepAliveInterval () const; + virtual uint8_t getKeepAliveCount () const; + virtual void disableKeepAlive () { keepAlive(0, 0, 0); } // default NoDelay=False (Nagle=True=!NoDelay) // Nagle is for shortly delaying outgoing data, to send less/bigger packets @@ -148,6 +148,10 @@ class WiFiClient : public Client, public SList { virtual bool outputCanTimeout () override { return connected(); } virtual bool inputCanTimeout () override { return connected(); } + // Immediately stops this client instance. + // Unlike stop(), does not wait to gracefuly shutdown the connection. + void abort(); + protected: static int8_t _s_connected(void* arg, void* tpcb, int8_t err); diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp index 9ad357c33a..7bc0c70df6 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp @@ -20,8 +20,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#define LWIP_INTERNAL - #include #include #include @@ -252,19 +250,32 @@ void WiFiClientSecureCtx::_freeSSL() { } bool WiFiClientSecureCtx::_clientConnected() { - return (_client && _client->state() == ESTABLISHED); + if (!_client || (_client->state() == CLOSED)) { + return false; + } + + return _client->state() == ESTABLISHED; +} + +bool WiFiClientSecureCtx::_engineConnected() { + return _clientConnected() && _handshake_done && _eng && (br_ssl_engine_current_state(_eng) != BR_SSL_CLOSED); } uint8_t WiFiClientSecureCtx::connected() { - if (available() || (_clientConnected() && _handshake_done && (br_ssl_engine_current_state(_eng) != BR_SSL_CLOSED))) { + if (!_engineConnected()) { + return false; + } + + if (_pollRecvBuffer() > 0) { return true; } - return false; + + return _engineConnected(); } int WiFiClientSecureCtx::availableForWrite () { - // code taken from ::_write() - if (!connected() || !_handshake_done) { + // Can't write things when there's no connection or br_ssl engine is closed + if (!_engineConnected()) { return 0; } // Get BearSSL to a state where we can send @@ -286,7 +297,7 @@ int WiFiClientSecureCtx::availableForWrite () { size_t WiFiClientSecureCtx::_write(const uint8_t *buf, size_t size, bool pmem) { size_t sent_bytes = 0; - if (!connected() || !size || !_handshake_done) { + if (!size || !_engineConnected()) { return 0; } @@ -333,10 +344,11 @@ size_t WiFiClientSecureCtx::write_P(PGM_P buf, size_t size) { } size_t WiFiClientSecureCtx::write(Stream& stream) { - if (!connected() || !_handshake_done) { - DEBUG_BSSL("write: Connect/handshake not completed yet\n"); + if (!_engineConnected()) { + DEBUG_BSSL("write: no br_ssl engine to work with\n"); return 0; } + return stream.sendAll(this); } @@ -345,12 +357,20 @@ int WiFiClientSecureCtx::read(uint8_t *buf, size_t size) { return -1; } - int avail = available(); - bool conn = connected(); - if (!avail && conn) { - return 0; // We're still connected, but nothing to read + // will either check the internal buffer, or try to wait for some data + // *may* attempt to write some pending ::write() data b/c of _run_until + int avail = _pollRecvBuffer(); + + // internal buffer might still be available for some time + bool engine = _engineConnected(); + + // we're still connected, but nothing to read + if (!avail && engine) { + return 0; } - if (!avail && !conn) { + + // or, available failed to assign the internal buffer and we are already disconnected + if (!avail && !engine) { DEBUG_BSSL("read: Not connected, none left available\n"); return -1; } @@ -365,10 +385,11 @@ int WiFiClientSecureCtx::read(uint8_t *buf, size_t size) { return to_copy; } - if (!conn) { + if (!engine) { DEBUG_BSSL("read: Not connected\n"); return -1; } + return 0; // If we're connected, no error but no read. } @@ -393,11 +414,10 @@ int WiFiClientSecureCtx::read() { if (1 == read(&c, 1)) { return c; } - DEBUG_BSSL("read: failed\n"); return -1; } -int WiFiClientSecureCtx::available() { +int WiFiClientSecureCtx::_pollRecvBuffer() { if (_recvapp_buf) { return _recvapp_len; // Anything from last call? } @@ -418,8 +438,12 @@ int WiFiClientSecureCtx::available() { return 0; } +int WiFiClientSecureCtx::available() { + return _pollRecvBuffer(); +} + int WiFiClientSecureCtx::peek() { - if (!ctx_present() || !available()) { + if (!ctx_present() || (0 == _pollRecvBuffer())) { DEBUG_BSSL("peek: Not connected, none left available\n"); return -1; } @@ -438,7 +462,7 @@ size_t WiFiClientSecureCtx::peekBytes(uint8_t *buffer, size_t length) { } _startMillis = millis(); - while ((available() < (int) length) && ((millis() - _startMillis) < 5000)) { + while ((_pollRecvBuffer() < (int) length) && ((millis() - _startMillis) < 5000)) { yield(); } diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h index 8a77648d90..e0cb44928f 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h @@ -34,7 +34,7 @@ namespace BearSSL { class WiFiClientSecureCtx : public WiFiClient { public: WiFiClientSecureCtx(); - WiFiClientSecureCtx(const WiFiClientSecure &rhs) = delete; + WiFiClientSecureCtx(const WiFiClientSecureCtx &rhs) = delete; ~WiFiClientSecureCtx() override; WiFiClientSecureCtx& operator=(const WiFiClientSecureCtx&) = delete; @@ -43,7 +43,7 @@ class WiFiClientSecureCtx : public WiFiClient { // TODO: don't remove just yet to avoid including the WiFiClient default implementation and unintentionally causing // a 'slice' that this method tries to avoid in the first place std::unique_ptr clone() const override { - return std::unique_ptr(new WiFiClientSecureCtx(*this)); + return nullptr; } int connect(IPAddress ip, uint16_t port) override; @@ -195,7 +195,13 @@ class WiFiClientSecureCtx : public WiFiClient { unsigned char *_recvapp_buf; size_t _recvapp_len; + int _pollRecvBuffer(); // If there's a buffer with some pending data, return it's length + // If there's no buffer, poll the engine and store any received data there and return the length + // (which also may change the internal state, e.g. make us disconnected) + bool _clientConnected(); // Is the underlying socket alive? + bool _engineConnected(); // Are both socket and the bearssl engine alive? + std::shared_ptr _alloc_iobuf(size_t sz); void _freeSSL(); int _run_until(unsigned target, bool blocking = true); @@ -278,6 +284,11 @@ class WiFiClientSecure : public WiFiClient { void flush() override { (void)flush(0); } void stop() override { (void)stop(0); } + IPAddress remoteIP() override { return _ctx->remoteIP(); } + uint16_t remotePort() override { return _ctx->remotePort(); } + IPAddress localIP() override { return _ctx->localIP(); } + uint16_t localPort() override { return _ctx->localPort(); } + // Allow sessions to be saved/restored automatically to a memory area void setSession(Session *session) { _ctx->setSession(session); } @@ -346,6 +357,21 @@ class WiFiClientSecure : public WiFiClient { // consume bytes after use (see peekBuffer) virtual void peekConsume (size_t consume) override { return _ctx->peekConsume(consume); } + + void keepAlive(uint16_t idle_sec = TCP_DEFAULT_KEEPALIVE_IDLE_SEC, uint16_t intv_sec = TCP_DEFAULT_KEEPALIVE_INTERVAL_SEC, uint8_t count = TCP_DEFAULT_KEEPALIVE_COUNT) override + { + _ctx->keepAlive(idle_sec, intv_sec, count); + } + + bool isKeepAliveEnabled() const override { return _ctx->isKeepAliveEnabled(); }; + + uint16_t getKeepAliveIdle() const override { return _ctx->getKeepAliveIdle(); }; + + uint16_t getKeepAliveInterval() const override { return _ctx->getKeepAliveInterval(); }; + + uint8_t getKeepAliveCount() const override { return _ctx->getKeepAliveCount(); }; + + void disableKeepAlive() override { _ctx->disableKeepAlive(); }; private: std::shared_ptr _ctx; diff --git a/libraries/ESP8266WiFi/src/WiFiServer.cpp b/libraries/ESP8266WiFi/src/WiFiServer.cpp index bfe2377220..462dbd7f74 100644 --- a/libraries/ESP8266WiFi/src/WiFiServer.cpp +++ b/libraries/ESP8266WiFi/src/WiFiServer.cpp @@ -21,8 +21,6 @@ */ -#define LWIP_INTERNAL - extern "C" { #include "osapi.h" #include "ets_sys.h" @@ -175,18 +173,15 @@ void WiFiServer::stop() { close(); } -template -T* slist_append_tail(T* head, T* item) { - if (!head) - return item; - T* last = head; - while(last->next()) - last = last->next(); - last->next(item); - return head; +void WiFiServer::end() { + close(); +} + +WiFiServer::operator bool() { + return (status() != CLOSED); } -long WiFiServer::_accept(tcp_pcb* apcb, long err) { +err_t WiFiServer::_accept(tcp_pcb* apcb, err_t err) { (void) err; DEBUGV("WS:ac\r\n"); @@ -212,7 +207,7 @@ void WiFiServer::_discard(ClientContext* client) { DEBUGV("WS:dis\r\n"); } -long WiFiServer::_s_accept(void *arg, tcp_pcb* newpcb, long err) { +err_t WiFiServer::_s_accept(void *arg, tcp_pcb* newpcb, err_t err) { return reinterpret_cast(arg)->_accept(newpcb, err); } diff --git a/libraries/ESP8266WiFi/src/WiFiServer.h b/libraries/ESP8266WiFi/src/WiFiServer.h index 081a952bb3..b0f51b02f0 100644 --- a/libraries/ESP8266WiFi/src/WiFiServer.h +++ b/libraries/ESP8266WiFi/src/WiFiServer.h @@ -23,13 +23,14 @@ #define wifiserver_h extern "C" { - #include "wl_definitions.h" + #include struct tcp_pcb; } -#include "Server.h" -#include "IPAddress.h" +#include +#include +#include // lwIP-v2 backlog facility allows to keep memory safe by limiting the // maximum number of incoming *pending clients*. Default number of possibly @@ -78,7 +79,7 @@ class WiFiServer { public: WiFiServer(const IPAddress& addr, uint16_t port); - WiFiServer(uint16_t port); + WiFiServer(uint16_t port = 23); virtual ~WiFiServer() {} WiFiClient accept(); // https://www.arduino.cc/en/Reference/EthernetServerAccept WiFiClient available(uint8_t* status = NULL) __attribute__((deprecated("Renamed to accept()."))); @@ -99,15 +100,22 @@ class WiFiServer { uint16_t port() const; void close(); void stop(); + void end(); + explicit operator bool(); using ClientType = WiFiClient; protected: - long _accept(tcp_pcb* newpcb, long err); + err_t _accept(tcp_pcb* newpcb, err_t err); void _discard(ClientContext* client); - static long _s_accept(void *arg, tcp_pcb* newpcb, long err); + static err_t _s_accept(void *arg, tcp_pcb* newpcb, err_t err); static void _s_discard(void* server, ClientContext* ctx); + +#if CORE_MOCK + void _mockUnclaimed (); +#endif + }; #endif diff --git a/libraries/ESP8266WiFi/src/WiFiServerSecureBearSSL.cpp b/libraries/ESP8266WiFi/src/WiFiServerSecureBearSSL.cpp index 48d221aa14..cc7640b66f 100644 --- a/libraries/ESP8266WiFi/src/WiFiServerSecureBearSSL.cpp +++ b/libraries/ESP8266WiFi/src/WiFiServerSecureBearSSL.cpp @@ -19,8 +19,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#define LWIP_INTERNAL - extern "C" { #include "osapi.h" #include "ets_sys.h" @@ -81,6 +79,9 @@ WiFiClientSecure WiFiServerSecure::available(uint8_t* status) { } WiFiClientSecure WiFiServerSecure::accept() { +#if CORE_MOCK + _mockUnclaimed(); +#endif if (_unclaimed) { if (_sk && _sk->isRSA()) { WiFiClientSecure result(_unclaimed, _chain, _sk, _iobuf_in_size, _iobuf_out_size, _cache, _client_CA_ta, _tls_min, _tls_max); diff --git a/libraries/ESP8266WiFi/src/WiFiUdp.cpp b/libraries/ESP8266WiFi/src/WiFiUdp.cpp index fc4bfe324e..cf22e3cd2a 100644 --- a/libraries/ESP8266WiFi/src/WiFiUdp.cpp +++ b/libraries/ESP8266WiFi/src/WiFiUdp.cpp @@ -20,7 +20,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#define LWIP_INTERNAL #include extern "C" @@ -85,6 +84,11 @@ uint8_t WiFiUDP::begin(uint16_t port) return (_ctx->listen(IPAddress(), port)) ? 1 : 0; } +uint8_t WiFiUDP::beginMulticast(IPAddress multicast, uint16_t port) +{ + return beginMulticast(IP_ADDR_ANY, multicast, port); +} + uint8_t WiFiUDP::beginMulticast(IPAddress interfaceAddr, IPAddress multicast, uint16_t port) { if (_ctx) { diff --git a/libraries/ESP8266WiFi/src/WiFiUdp.h b/libraries/ESP8266WiFi/src/WiFiUdp.h index d8fbcbe221..fdf82217bc 100644 --- a/libraries/ESP8266WiFi/src/WiFiUdp.h +++ b/libraries/ESP8266WiFi/src/WiFiUdp.h @@ -47,6 +47,8 @@ class WiFiUDP : public UDP, public SList { // Finish with the UDP connection void stop() override; // join a multicast group and listen on the given port + virtual uint8_t beginMulticast(IPAddress interfaceAddr, uint16_t port); + // join a multicast group and listen on the given port, using a specific interface address uint8_t beginMulticast(IPAddress interfaceAddr, IPAddress multicast, uint16_t port); // Sending UDP packets 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/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index 7fcad3678a..5c579c8848 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -144,8 +144,7 @@ class ClientContext _connect_pending = true; _op_start_time = millis(); // will resume on timeout or when _connected or _notify_error fires - // give scheduled functions a chance to run (e.g. Ethernet uses recurrent) - esp_delay(_timeout_ms, [this]() { return this->_connect_pending; }, 1); + esp_delay(_timeout_ms, [this]() { return this->_connect_pending; }); _connect_pending = false; if (!_pcb) { DEBUGV(":cabrt\r\n"); @@ -485,8 +484,7 @@ class ClientContext _send_waiting = true; // will resume on timeout or when _write_some_from_cb or _notify_error fires - // give scheduled functions a chance to run (e.g. Ethernet uses recurrent) - esp_delay(_timeout_ms, [this]() { return this->_send_waiting; }, 1); + esp_delay(_timeout_ms, [this]() { return this->_send_waiting; }); _send_waiting = false; } while(true); diff --git a/libraries/ESP8266WiFi/src/include/slist.h b/libraries/ESP8266WiFi/src/include/slist.h index 0606f72431..f05846d3b3 100644 --- a/libraries/ESP8266WiFi/src/include/slist.h +++ b/libraries/ESP8266WiFi/src/include/slist.h @@ -34,5 +34,15 @@ class SList { T* _next; }; +template +T* slist_append_tail(T* head, T* item) { + if (!head) + return item; + T* last = head; + while(last->next()) + last = last->next(); + last->next(item); + return head; +} #endif //SLIST_H diff --git a/libraries/ESP8266WiFi/src/ssl-tls-ca-key-cert-example.h b/libraries/ESP8266WiFi/src/ssl-tls-ca-key-cert-example.h new file mode 100644 index 0000000000..25e2401f74 --- /dev/null +++ b/libraries/ESP8266WiFi/src/ssl-tls-ca-key-cert-example.h @@ -0,0 +1,124 @@ + +// check examples/BearSSL_ServerClientCert/ for documentation on how to +// generate such certificates and keys for your own project. + +#pragma message("DO NOT USE THE SAMPLE CERTS, KEYS, OR CAS IN YOUR OWN PROJECT!!!") + +#if !USING_INSECURE_CERTS_AND_KEYS_AND_CAS +#error Certificates, keys and CAs which are not kept secretly are absolutely not safe to use +#endif + +#ifndef USE_EC + +#pragma message("SSL: Elliptic curve is NOT used in this example") + +// The hardcoded certificate authority used in examples +// Don't use it on your own apps!!!!! +const char ca_cert[] PROGMEM = R"EOF( +-----BEGIN CERTIFICATE----- +MIIC1TCCAb2gAwIBAgIJAMPt1Ms37+hLMA0GCSqGSIb3DQEBCwUAMCExCzAJBgNV +BAYTAlVTMRIwEAYDVQQDDAkxMjcuMC4wLjMwHhcNMTgwMzE0MDQyMTU0WhcNMjkw +NTMxMDQyMTU0WjAhMQswCQYDVQQGEwJVUzESMBAGA1UEAwwJMTI3LjAuMC4zMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxsa4qU/tlzN4YTcnn/I/ffsi +jOPc8QRcwClKzasIZNFEye4uThl+LGZWFIFb8X8Dc+xmmBaWlPJbqtphgFKStpar +DdduHSW1ud6Y1FVKxljo3UwCMrYm76Q/jNzXJvGs6Z1MDNsVZzGJaoqit2H2Hkvk +y+7kk3YbEDlcyVsLOw0zCKL4cd2DSNDyhIZxWo2a8Qn5IdjWAYtsTnW6MvLk/ya4 +abNeRfSZwi+r37rqi9CIs++NpL5ynqkKKEMrbeLactWgHbWrZeaMyLpuUEL2GF+w +MRaAwaj7ERwT5gFJRqYwj6bbfIdx5PC7h7ucbyp272MbrDa6WNBCMwQO222t4wID +AQABoxAwDjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCmXfrC42nW +IpL3JDkB8YlB2QUvD9JdMp98xxo33+xE69Gov0e6984F1Gluao0p6sS7KF+q3YLS +4hjnzuGzF9GJMimIB7NMQ20yXKfKpmKJ7YugMaKTDWDhHn5679mKVbLSQxHCUMEe +tEnMT93/UaDbWBjV6zu876q5vjPMYgDHODqO295ySaA71UkijaCn6UwKUT49286T +V9ZtzgabNGHXfklHgUPWoShyze+G3g29I1BR0qABoJI63zaNu8ua42v5g1RldxsW +X8yKI14mFOGxuvcygG8L2xxysW7Zq+9g+O7gW0Pm6RDYnUQmIwY83h1KFCtYCJdS +2PgozwkkUNyP +-----END CERTIFICATE----- +)EOF"; + +// The server's private key which must be kept secret +const char server_private_key[] PROGMEM = R"EOF( +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAsRNVTvqP++YUh8NrbXwE83xVsDqcB3F76xcXNKFDERfVd2P/ +LvyDovCcoQtT0UCRgPcxRp894EuPH/Ru6Z2Lu85sV//i7ce27tc2WRFSfuhlRxHP +LJWHxTl1CEfXp/owkECQ4MB3pw6Ekc16iTEPiezTG+T+mQ/BkiIwcIK6CMlpR9DI +eYUTqv0f9NrUfAjdBrqlEO2gpgFvLFrkDEU2ntAIc4aPOP7yDOym/xzfy6TiG8Wo +7nlh6M97xTZGfbEPCH9rZDjo5istym1HzF5P+COq+OTSPscjFGXoi978o6hZwa7i +zxorg4h5a5lGnshRu2Gl+Ybfa14OwnIrv/yCswIDAQABAoIBAHxwgbsHCriTcEoY +Yx6F0VTrQ6ydA5mXfuYvS/eIfIE+pp1IgMScYEXZobjrJPQg1CA1l0NyFSHS97oV +JPy34sMQxcLx6KABgeVHCMJ/EeJtnv7a3SUP0GIhhsVS95Lsl8RIG4hWub+EzFVK +eZqAB9N9wr4Pp3wZPodbz37B38rb1QPyMFmQOLlHjKTOmoxsXhL2ot+R3+aLYSur +oPO1kQo7/d0UAZoy8h9OQN4a2EXvawh4O2EvFGbc5X/yXwAdEQ4NPp9VZhkNIRkV ++XZ3FcIqEVOploKtRF/tVBTz3g61/lFz21L9PMmV5y8tvSafr2SpJugGVmp2rrVQ +VNyGlIECgYEA10JSI5gmeCU3zK6kvOfBp54hY/5dDrSUpjKkMxpmm7WZQ6Il/k7A +hMcLeMzHiriT7WhRIXF8AOr2MoEkHkH3DhVNN4ccieVZx2SE5P5mVkItZGLrrpfU +dysR/ARAI1HYegGUiKacZtf9SrRavU0m7fOVOiYwbFRhjyX+MyuteYkCgYEA0pbz +4ZosetScP68uZx1sGlTfkcqLl7i15DHk3gnj6jKlfhvC2MjeLMhNDtKeUAuY7rLQ +guZ0CCghWAv0Glh5eYdfIiPhgqFfX4P5F3Om4zQHVPYj8xHfHG4ZP7dKQTndrO1Q +fLdGDTQLVXabAUSp2YGrijC8J9idSW1pYClvF1sCgYEAjkDn41nzYkbGP1/Swnwu +AEWCL4Czoro32jVxScxSrugt5wJLNWp508VukWBTJhugtq3Pn9hNaJXeKbYqVkyl +pgrxwpZph7+nuxt0r5hnrO2C7eppcjIoWLB/7BorAKxf8REGReBFT7nBTBMwPBW2 +el4U6h6+tXh2GJG1Eb/1nnECgYAydVb0THOx7rWNkNUGggc/++why61M6kYy6j2T +cj05BW+f2tkCBoctpcTI83BZb53yO8g4RS2yMqNirGKN2XspwmTqEjzbhv0KLt4F +X4GyWOoU0nFksXiLIFpOaQWSwWG7KJWrfGJ9kWXR0Xxsfl5QLoDCuNCsn3t4d43T +K7phlwKBgHDzF+50+/Wez3YHCy2a/HgSbHCpLQjkknvgwkOh1z7YitYBUm72HP8Z +Ge6b4wEfNuBdlZll/y9BQQOZJLFvJTE5t51X9klrkGrOb+Ftwr7eI/H5xgcadI52 +tPYglR5fjuRF/wnt3oX9JlQ2RtSbs+3naXH8JoherHaqNn8UpH0t +-----END RSA PRIVATE KEY----- +)EOF"; + +// The server's public certificate which must be shared +const char server_cert[] PROGMEM = R"EOF( +-----BEGIN CERTIFICATE----- +MIIDTzCCAjcCCQDPXvMRYOpeuDANBgkqhkiG9w0BAQsFADCBpjESMBAGA1UEAwwJ +MTI3LjAuMC4xMQswCQYDVQQGEwJVUzElMCMGA1UECgwcTXkgT3duIENlcnRpZmlj +YXRlIEF1dGhvcml0eTEUMBIGA1UECAwLQXJkdWlub0xhbmQxFTATBgNVBAcMDEFy +ZHVpbm9WaWxsZTEVMBMGA1UECgwMRVNQODI2NlVzZXJzMRgwFgYDVQQLDA9FU1A4 +MjY2LUFyZHVpbm8wHhcNMTgwMzE0MDQwMDAwWhcNMjkwMjI0MDQwMDAwWjAsMRYw +FAYDVQQKDA1NeSBTZXJ2ZXIgT3JnMRIwEAYDVQQDDAkxMjcuMC4wLjMwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCxE1VO+o/75hSHw2ttfATzfFWwOpwH +cXvrFxc0oUMRF9V3Y/8u/IOi8JyhC1PRQJGA9zFGnz3gS48f9G7pnYu7zmxX/+Lt +x7bu1zZZEVJ+6GVHEc8slYfFOXUIR9en+jCQQJDgwHenDoSRzXqJMQ+J7NMb5P6Z +D8GSIjBwgroIyWlH0Mh5hROq/R/02tR8CN0GuqUQ7aCmAW8sWuQMRTae0Ahzho84 +/vIM7Kb/HN/LpOIbxajueWHoz3vFNkZ9sQ8If2tkOOjmKy3KbUfMXk/4I6r45NI+ +xyMUZeiL3vyjqFnBruLPGiuDiHlrmUaeyFG7YaX5ht9rXg7Cciu//IKzAgMBAAEw +DQYJKoZIhvcNAQELBQADggEBAEnG+FNyNCOkBvzHiUpHHpScxZqM2f+XDcewJgeS +L6HkYEDIZZDNnd5gduSvkHpdJtWgsvJ7dJZL40w7Ba5sxpZHPIgKJGl9hzMkG+aA +z5GMkjys9h2xpQZx9KL3q7G6A+C0bll7ODZlwBtY07CFMykT4Mp2oMRrQKRucMSV +AB1mKujLAnMRKJ3NM89RQJH4GYiRps9y/HvM5lh7EIK/J0/nEZeJxY5hJngskPKb +oPPdmkR97kaQnll4KNsC3owVlHVU2fMftgYkgQLzyeWgzcNa39AF3B6JlcOzNyQY +seoK24dHmt6tWmn/sbxX7Aa6TL/4mVlFoOgcaTJyVaY/BrY= +-----END CERTIFICATE----- +)EOF"; + +#else // USE_EC is defined + +#pragma message("SSL: Elliptic curve IS used in this example") + +const char server_cert[] PROGMEM = R"EOF( +-----BEGIN CERTIFICATE----- +MIIB0zCCAXqgAwIBAgIJALANi2eTiGD/MAoGCCqGSM49BAMCMEUxCzAJBgNVBAYT +AkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRn +aXRzIFB0eSBMdGQwHhcNMTkwNjExMjIyOTU2WhcNMjAwNjEwMjIyOTU2WjBFMQsw +CQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJu +ZXQgV2lkZ2l0cyBQdHkgTHRkMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAExIkZ +w7zjk6TGcScff1PAehuEGmKZTf8VfnkjyJH0IbBgZibZ+qwYGBEnkz4KpKv7TkHo +W+j7F5EMcLcSrUIpy6NTMFEwHQYDVR0OBBYEFI6A0f+g0HyxUT6xrbVmRU79urbj +MB8GA1UdIwQYMBaAFI6A0f+g0HyxUT6xrbVmRU79urbjMA8GA1UdEwEB/wQFMAMB +Af8wCgYIKoZIzj0EAwIDRwAwRAIgWvy7ofQTGZMNqxUfe4gjtkU+C9AkQtaOMW2U +5xFFSvcCICvcGrQpoi7tRTq8xsXFmr8MYWgQTpVAtj6opXMQct/l +-----END CERTIFICATE----- +)EOF"; + +// The server's private key which must be kept secret +const char server_private_key[] PROGMEM = R"EOF( +-----BEGIN EC PARAMETERS----- +BggqhkjOPQMBBw== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIKyLR9/NT7ZdWM+2rklehveuk+jyIHJ+P8ZUQ392HOYvoAoGCCqGSM49 +AwEHoUQDQgAExIkZw7zjk6TGcScff1PAehuEGmKZTf8VfnkjyJH0IbBgZibZ+qwY +GBEnkz4KpKv7TkHoW+j7F5EMcLcSrUIpyw== +-----END EC PRIVATE KEY----- +)EOF"; + +#endif // USE_EC is defined diff --git a/libraries/ESP8266httpUpdate/examples/httpUpdate/httpUpdate.ino b/libraries/ESP8266httpUpdate/examples/httpUpdate/httpUpdate.ino index f245d2363d..b9da7f8cb4 100644 --- a/libraries/ESP8266httpUpdate/examples/httpUpdate/httpUpdate.ino +++ b/libraries/ESP8266httpUpdate/examples/httpUpdate/httpUpdate.ino @@ -23,17 +23,11 @@ ESP8266WiFiMulti WiFiMulti; void setup() { Serial.begin(115200); - // Serial.setDebugOutput(true); + // Serial.setDebugOutput(false); - Serial.println(); - Serial.println(); Serial.println(); - for (uint8_t t = 4; t > 0; t--) { - Serial.printf("[SETUP] WAIT %d...\n", t); - Serial.flush(); - delay(1000); - } + ESPhttpUpdate.setClientTimeout(2000); // default was 8000 WiFi.mode(WIFI_STA); WiFiMulti.addAP(APSSID, APPSK); diff --git a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp index df709bdd2f..af45852e5f 100755 --- a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp +++ b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp @@ -83,6 +83,16 @@ HTTPUpdateResult ESP8266HTTPUpdate::update(WiFiClient& client, const String& hos return handleUpdate(http, currentVersion, false); } +HTTPUpdateResult ESP8266HTTPUpdate::update(HTTPClient& httpClient, const String& currentVersion) +{ + return handleUpdate(httpClient, currentVersion, false); +} + +HTTPUpdateResult ESP8266HTTPUpdate::updateFS(HTTPClient& httpClient, const String& currentVersion) +{ + return handleUpdate(httpClient, currentVersion, true); +} + /** * return error code as int * @return int error code @@ -208,6 +218,9 @@ HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String& DEBUG_HTTP_UPDATE("[httpUpdate] Server header:\n"); DEBUG_HTTP_UPDATE("[httpUpdate] - code: %d\n", code); DEBUG_HTTP_UPDATE("[httpUpdate] - len: %d\n", len); + if(code != HTTP_CODE_OK) { + DEBUG_HTTP_UPDATE("[httpUpdate] - payload: %s\n", http.getString().c_str()); + } String md5; if (_md5Sum.length()) { @@ -222,6 +235,7 @@ HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String& DEBUG_HTTP_UPDATE("[httpUpdate] ESP8266 info:\n"); DEBUG_HTTP_UPDATE("[httpUpdate] - free Space: %d\n", ESP.getFreeSketchSpace()); DEBUG_HTTP_UPDATE("[httpUpdate] - current Sketch Size: %d\n", ESP.getSketchSize()); + DEBUG_HTTP_UPDATE("[httpUpdate] - current Sketch MD5: %s\n", ESP.getSketchMD5().c_str()); if(currentVersion && currentVersion[0] != 0x00) { DEBUG_HTTP_UPDATE("[httpUpdate] - current version: %s\n", currentVersion.c_str() ); @@ -427,6 +441,115 @@ bool ESP8266HTTPUpdate::runUpdate(Stream& in, uint32_t size, const String& md5, return true; } +/** + * @brief Get avialable firmware version from update server + * @author Holger Mueller + * @date 2023-08-03 + * + * @param client WiFiClient to use (see HTTPClient::begin) + * @param host Update host name or IP (see HTTPClient::begin) + * @param port Port on host (see HTTPClient::begin) + * @param uri Update URI on server (see HTTPClient::begin) + * @param current_version Current firmware version + * @param available_version Firmware version available on update server + * @return ESP8266HTTPUpdate::HTTPUpdateResult, HTTP_UPDATE_OK in case of success + */ +HTTPUpdateResult ESP8266HTTPUpdate::getAvailableVersion(WiFiClient& client, const String& host, uint16_t port, const String& uri, const String& current_version, String& available_version) { + HTTPUpdateResult ret = HTTP_UPDATE_FAILED; + HTTPClient http; + http.begin(client, host, port, uri); + + // use HTTP/1.0 for update since the update handler not support any transfer Encoding + http.useHTTP10(true); + http.setTimeout(_httpClientTimeout); + http.setFollowRedirects(_followRedirects); + http.setUserAgent(F("ESP8266-http-Update")); + http.addHeader(F("x-ESP8266-Chip-ID"), String(ESP.getChipId())); + http.addHeader(F("x-ESP8266-STA-MAC"), WiFi.macAddress()); + http.addHeader(F("x-ESP8266-AP-MAC"), WiFi.softAPmacAddress()); + http.addHeader(F("x-ESP8266-free-space"), String(ESP.getFreeSketchSpace())); + http.addHeader(F("x-ESP8266-sketch-size"), String(ESP.getSketchSize())); + http.addHeader(F("x-ESP8266-sketch-md5"), String(ESP.getSketchMD5())); + http.addHeader(F("x-ESP8266-chip-size"), String(ESP.getFlashChipRealSize())); + http.addHeader(F("x-ESP8266-sdk-version"), ESP.getSdkVersion()); + + http.addHeader(F("x-ESP8266-mode"), F("version")); + + if (current_version && current_version[0] != 0x00) { + http.addHeader(F("x-ESP8266-version"), current_version); + } + + if (!_user.isEmpty() && !_password.isEmpty()) { + http.setAuthorization(_user.c_str(), _password.c_str()); + } + + if (!_auth.isEmpty()) { + http.setAuthorization(_auth.c_str()); + } + + const char* headerkeys[] = {"x-MD5", "x-version"}; + size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*); + + // track these headers + http.collectHeaders(headerkeys, headerkeyssize); + + int code = http.GET(); + + if (code <= 0) { + DEBUG_HTTP_UPDATE("[httpUpdate] HTTP error: %s\n", http.errorToString(code).c_str()); + _setLastError(code); + http.end(); + return HTTP_UPDATE_FAILED; + } + + DEBUG_HTTP_UPDATE("[httpUpdate] Header read fin.\n"); + DEBUG_HTTP_UPDATE("[httpUpdate] Server header:\n"); + DEBUG_HTTP_UPDATE("[httpUpdate] - code: %d\n", code); + DEBUG_HTTP_UPDATE("[httpUpdate] - len: %d\n", http.getSize()); + if (code != HTTP_CODE_OK) { + DEBUG_HTTP_UPDATE("[httpUpdate] - payload: %s\n", http.getString().c_str()); + } + + switch (code) { + case HTTP_CODE_OK: ///< OK (check for version) + if (http.hasHeader("x-version")) { + available_version = http.header("x-version"); + ret = HTTP_UPDATE_OK; + DEBUG_HTTP_UPDATE("[httpUpdate] - current version: %s\n", current_version.c_str()); + DEBUG_HTTP_UPDATE("[httpUpdate] - server version: %s\n", available_version.c_str()); + } else { + _setLastError(HTTP_UE_SERVER_NOT_REPORT_VERSION); + ret = HTTP_UPDATE_FAILED; + DEBUG_HTTP_UPDATE("[httpUpdate] Server did not respond with a firmware version\n"); + } + if (http.hasHeader("x-MD5")) { + DEBUG_HTTP_UPDATE("[httpUpdate] - current Sketch MD5: %s\n", ESP.getSketchMD5().c_str()); + DEBUG_HTTP_UPDATE("[httpUpdate] - server Sketch MD5: %s\n", http.header("x-MD5").c_str()); + } + break; + case HTTP_CODE_NOT_FOUND: + _setLastError(HTTP_UE_SERVER_FILE_NOT_FOUND); + ret = HTTP_UPDATE_FAILED; + break; + case HTTP_CODE_FORBIDDEN: + _setLastError(HTTP_UE_SERVER_FORBIDDEN); + ret = HTTP_UPDATE_FAILED; + break; + case HTTP_CODE_UNAUTHORIZED: + _setLastError(HTTP_UE_SERVER_UNAUTHORIZED); + ret = HTTP_UPDATE_FAILED; + break; + default: + _setLastError(HTTP_UE_SERVER_WRONG_HTTP_CODE); + ret = HTTP_UPDATE_FAILED; + DEBUG_HTTP_UPDATE("[httpUpdate] HTTP Code is (%d)\n", code); + break; + } + + http.end(); + return ret; +} + #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_HTTPUPDATE) ESP8266HTTPUpdate ESPhttpUpdate; #endif diff --git a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h index 6c6b140f39..28e90bad23 100755 --- a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h +++ b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h @@ -43,16 +43,18 @@ #endif /// note we use HTTP client errors too so we start at 100 -//TODO - in v3.0.0 make this an enum -constexpr int HTTP_UE_TOO_LESS_SPACE = (-100); -constexpr int HTTP_UE_SERVER_NOT_REPORT_SIZE = (-101); -constexpr int HTTP_UE_SERVER_FILE_NOT_FOUND = (-102); -constexpr int HTTP_UE_SERVER_FORBIDDEN = (-103); -constexpr int HTTP_UE_SERVER_WRONG_HTTP_CODE = (-104); -constexpr int HTTP_UE_SERVER_FAULTY_MD5 = (-105); -constexpr int HTTP_UE_BIN_VERIFY_HEADER_FAILED = (-106); -constexpr int HTTP_UE_BIN_FOR_WRONG_FLASH = (-107); -constexpr int HTTP_UE_SERVER_UNAUTHORIZED = (-108); +enum HTTPUpdateError { + HTTP_UE_SERVER_NOT_REPORT_VERSION = -109, // server did not respond with a firmware version + HTTP_UE_SERVER_UNAUTHORIZED, // -108 + HTTP_UE_BIN_FOR_WRONG_FLASH, // -107 + HTTP_UE_BIN_VERIFY_HEADER_FAILED, // -106 + HTTP_UE_SERVER_FAULTY_MD5, // -105 + HTTP_UE_SERVER_WRONG_HTTP_CODE, // -104 + HTTP_UE_SERVER_FORBIDDEN, // -103 + HTTP_UE_SERVER_FILE_NOT_FOUND, // -102 + HTTP_UE_SERVER_NOT_REPORT_SIZE, // -101 + HTTP_UE_TOO_LESS_SPACE // -100 +}; enum HTTPUpdateResult { HTTP_UPDATE_FAILED, @@ -120,6 +122,10 @@ class ESP8266HTTPUpdate t_httpUpdate_return update(WiFiClient& client, const String& host, uint16_t port, const String& uri = "/", const String& currentVersion = ""); t_httpUpdate_return updateFS(WiFiClient& client, const String& url, const String& currentVersion = ""); + t_httpUpdate_return update(HTTPClient& httpClient, const String& currentVersion = ""); + t_httpUpdate_return updateFS(HTTPClient& httpClient, const String& currentVersion = ""); + + t_httpUpdate_return getAvailableVersion(WiFiClient& client, const String& host, uint16_t port, const String& uri, const String& current_version, String& available_version); // Notification callbacks void onStart(HTTPUpdateStartCB cbOnStart) { _cbStart = cbOnStart; } @@ -130,6 +136,9 @@ class ESP8266HTTPUpdate int getLastError(void); String getLastErrorString(void); + void setClientTimeout(int timeout) { + _httpClientTimeout = timeout; + } protected: t_httpUpdate_return handleUpdate(HTTPClient& http, const String& currentVersion, bool spiffs = false); bool runUpdate(Stream& in, uint32_t size, const String& md5, int command = U_FLASH); @@ -148,10 +157,9 @@ class ESP8266HTTPUpdate String _password; String _auth; String _md5Sum; -private: int _httpClientTimeout; followRedirects_t _followRedirects = HTTPC_DISABLE_FOLLOW_REDIRECTS; - +private: // Callbacks HTTPUpdateStartCB _cbStart; HTTPUpdateEndCB _cbEnd; diff --git a/libraries/ESP8266mDNS/examples/OTA-mDNS-LittleFS/OTA-mDNS-LittleFS.ino b/libraries/ESP8266mDNS/examples/OTA-mDNS-LittleFS/OTA-mDNS-LittleFS.ino index d94d96077c..78d4aa6c30 100644 --- a/libraries/ESP8266mDNS/examples/OTA-mDNS-LittleFS/OTA-mDNS-LittleFS.ino +++ b/libraries/ESP8266mDNS/examples/OTA-mDNS-LittleFS/OTA-mDNS-LittleFS.ino @@ -47,7 +47,7 @@ const char *ap_default_psk = APPSK; ///< Default PSK. /// @} /// Uncomment the next line for verbose output over UART. -//#define SERIAL_VERBOSE +// #define SERIAL_VERBOSE /** @brief Read WiFi connection information from file system. diff --git a/libraries/ESP8266mDNS/src/LEAmDNS.cpp b/libraries/ESP8266mDNS/src/LEAmDNS.cpp index 68d71efc4a..75862644f5 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS.cpp +++ b/libraries/ESP8266mDNS/src/LEAmDNS.cpp @@ -27,10 +27,15 @@ #include "ESP8266mDNS.h" #include "LEAmDNS_Priv.h" -#include // LwipIntf::stateUpCB() +#include #include #include +// should be defined at build time +#ifndef ARDUINO_BOARD_ID +#define ARDUINO_BOARD_ID "generic" +#endif + namespace esp8266 { @@ -40,16 +45,6 @@ namespace esp8266 namespace MDNSImplementation { -/** - STRINGIZE -*/ -#ifndef STRINGIZE -#define STRINGIZE(x) #x -#endif -#ifndef STRINGIZE_VALUE_OF -#define STRINGIZE_VALUE_OF(x) STRINGIZE(x) -#endif - /** INTERFACE */ @@ -59,7 +54,7 @@ namespace MDNSImplementation */ MDNSResponder::MDNSResponder(void) : m_pServices(0), m_pUDPContext(0), m_pcHostname(0), m_pServiceQueries(0), - m_fnServiceTxtCallback(0) + m_fnServiceTxtCallback(0), m_bLwipCb(false), m_bRestarting(false) { } @@ -94,15 +89,34 @@ namespace MDNSImplementation bResult = _restart(); } - LwipIntf::stateUpCB( - [this](netif* intf) - { - (void)intf; - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( - PSTR("[MDNSResponder] new Interface '%c%c' is UP! restarting\n"), intf->name[0], - intf->name[1])); - _restart(); + if (bResult && !m_bLwipCb) + { + bool bCallback = LwipIntf::statusChangeCB( + [this](netif*) + { + if (m_bRestarting) + { + return; + } + + m_bRestarting = true; + schedule_function( + [this]() + { + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] begin: restarting " + "after interface status changed\n"));); + _restart(); + m_bRestarting = false; + }); + }); + DEBUG_EX_ERR(if (!bCallback) { + DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] begin: FAILED LwipIntf::statusChangeCB!\n")); }); + m_bLwipCb = bCallback; + } + DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] begin: FAILED for '%s'!\n"), (p_pcHostname ?: "-")); @@ -1258,7 +1272,7 @@ namespace MDNSImplementation { if ((!addServiceTxt(hService, "tcp_check", "no")) || (!addServiceTxt(hService, "ssh_upload", "no")) - || (!addServiceTxt(hService, "board", STRINGIZE_VALUE_OF(ARDUINO_BOARD))) + || (!addServiceTxt(hService, "board", ARDUINO_BOARD_ID)) || (!addServiceTxt(hService, "auth_upload", (p_bAuthUpload) ? "yes" : "no"))) { removeService(hService); @@ -1284,7 +1298,7 @@ namespace MDNSImplementation // Join multicast group(s) for (netif* pNetIf = netif_list; pNetIf; pNetIf = pNetIf->next) { - if (netif_is_up(pNetIf)) + if (netif_is_up(pNetIf) && IPAddress(pNetIf->ip_addr).isSet()) { #ifdef MDNS_IP4_SUPPORT ip_addr_t multicast_addr_V4 = DNS_MQUERY_IPV4_GROUP_INIT; @@ -1340,7 +1354,7 @@ namespace MDNSImplementation for (netif* pNetIf = netif_list; pNetIf; pNetIf = pNetIf->next) { - if (netif_is_up(pNetIf)) + if (netif_is_up(pNetIf) && IPAddress(pNetIf->ip_addr).isSet()) { bResult = true; diff --git a/libraries/ESP8266mDNS/src/LEAmDNS.h b/libraries/ESP8266mDNS/src/LEAmDNS.h index 92c688421f..3fc4dd98da 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS.h +++ b/libraries/ESP8266mDNS/src/LEAmDNS.h @@ -132,11 +132,6 @@ namespace esp8266 namespace MDNSImplementation { -// this should be defined at build time -#ifndef ARDUINO_BOARD -#define ARDUINO_BOARD "generic" -#endif - #define MDNS_IP4_SUPPORT #if LWIP_IPV6 //#define MDNS_IP6_SUPPORT @@ -497,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 @@ -1179,6 +1173,8 @@ namespace MDNSImplementation stcMDNSServiceQuery* m_pServiceQueries; MDNSDynamicServiceTxtCallbackFunc m_fnServiceTxtCallback; stcProbeInformation m_HostProbeInformation; + bool m_bLwipCb; + bool m_bRestarting; /** CONTROL **/ /* MAINTENANCE */ diff --git a/libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp b/libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp index 3d79f05ebc..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; } @@ -1395,11 +1395,18 @@ namespace MDNSImplementation { if ((bResult = _sendHostProbe())) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( - PSTR("[MDNSResponder] _updateProbeStatus: Did sent host probe\n\n"));); - m_HostProbeInformation.m_Timeout.reset(MDNS_PROBE_DELAY); - ++m_HostProbeInformation.m_u8SentCount; + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Did sent " + "host probe to all links \n\n"));); + } + else + { + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Did not " + "sent host probe to all links\n\n"));); } + m_HostProbeInformation.m_Timeout.reset(MDNS_PROBE_DELAY); + ++m_HostProbeInformation.m_u8SentCount; } else // Probing finished { @@ -1422,23 +1429,22 @@ namespace MDNSImplementation else if ((ProbingStatus_Done == m_HostProbeInformation.m_ProbingStatus) && (m_HostProbeInformation.m_Timeout.expired())) { - if ((bResult = _announce(true, false))) // Don't announce services here - { - ++m_HostProbeInformation.m_u8SentCount; + _announce(true, false); // Don't announce services here - if (MDNS_ANNOUNCE_COUNT > m_HostProbeInformation.m_u8SentCount) - { - m_HostProbeInformation.m_Timeout.reset(MDNS_ANNOUNCE_DELAY); - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( - PSTR("[MDNSResponder] _updateProbeStatus: Announcing host (%d).\n\n"), - m_HostProbeInformation.m_u8SentCount);); - } - else - { - m_HostProbeInformation.m_Timeout.resetToNeverExpires(); - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( - PSTR("[MDNSResponder] _updateProbeStatus: Done host announcing.\n\n"));); - } + ++m_HostProbeInformation.m_u8SentCount; + + if (MDNS_ANNOUNCE_COUNT > m_HostProbeInformation.m_u8SentCount) + { + m_HostProbeInformation.m_Timeout.reset(MDNS_ANNOUNCE_DELAY); + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _updateProbeStatus: Announcing host (%d).\n\n"), + m_HostProbeInformation.m_u8SentCount);); + } + else + { + m_HostProbeInformation.m_Timeout.resetToNeverExpires(); + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _updateProbeStatus: Done host announcing.\n\n"));); } } @@ -1464,12 +1470,21 @@ namespace MDNSImplementation if ((bResult = _sendServiceProbe(*pService))) { DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( - PSTR("[MDNSResponder] _updateProbeStatus: Did sent service probe " + PSTR("[MDNSResponder] _updateProbeStatus: Did sent service probe to " + "all links " "(%u)\n\n"), (pService->m_ProbeInformation.m_u8SentCount + 1));); - pService->m_ProbeInformation.m_Timeout.reset(MDNS_PROBE_DELAY); - ++pService->m_ProbeInformation.m_u8SentCount; } + else + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _updateProbeStatus: Did not sent service probe " + "to all links" + "(%u)\n\n"), + (pService->m_ProbeInformation.m_u8SentCount + 1));); + } + pService->m_ProbeInformation.m_Timeout.reset(MDNS_PROBE_DELAY); + ++pService->m_ProbeInformation.m_u8SentCount; } else // Probing finished { @@ -1495,28 +1510,27 @@ namespace MDNSImplementation else if ((ProbingStatus_Done == pService->m_ProbeInformation.m_ProbingStatus) && (pService->m_ProbeInformation.m_Timeout.expired())) { - if ((bResult = _announceService(*pService))) // Announce service - { - ++pService->m_ProbeInformation.m_u8SentCount; + _announceService(*pService); // Announce service - if (MDNS_ANNOUNCE_COUNT > pService->m_ProbeInformation.m_u8SentCount) - { - pService->m_ProbeInformation.m_Timeout.reset(MDNS_ANNOUNCE_DELAY); - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( - PSTR("[MDNSResponder] _updateProbeStatus: Announcing service %s.%s.%s " - "(%d)\n\n"), - (pService->m_pcName ?: m_pcHostname), pService->m_pcService, - pService->m_pcProtocol, pService->m_ProbeInformation.m_u8SentCount);); - } - else - { - pService->m_ProbeInformation.m_Timeout.resetToNeverExpires(); - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Done " - "service announcing for %s.%s.%s\n\n"), - (pService->m_pcName ?: m_pcHostname), - pService->m_pcService, pService->m_pcProtocol);); - } + ++pService->m_ProbeInformation.m_u8SentCount; + + if (MDNS_ANNOUNCE_COUNT > pService->m_ProbeInformation.m_u8SentCount) + { + pService->m_ProbeInformation.m_Timeout.reset(MDNS_ANNOUNCE_DELAY); + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _updateProbeStatus: Announcing service %s.%s.%s " + "(%d)\n\n"), + (pService->m_pcName ?: m_pcHostname), pService->m_pcService, + pService->m_pcProtocol, pService->m_ProbeInformation.m_u8SentCount);); + } + else + { + pService->m_ProbeInformation.m_Timeout.resetToNeverExpires(); + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Done " + "service announcing for %s.%s.%s\n\n"), + (pService->m_pcName ?: m_pcHostname), + pService->m_pcService, pService->m_pcProtocol);); } } } @@ -2107,7 +2121,7 @@ namespace MDNSImplementation } // IP4 flagged pIP4Address = pNextIP4Address; // Next - } // while + } // while #endif #ifdef MDNS_IP6_SUPPORT // IP6Address (from AAAA) @@ -2171,7 +2185,7 @@ namespace MDNSImplementation } // IP6 flagged pIP6Address = pNextIP6Address; // Next - } // while + } // while #endif pSQAnswer = pNextSQAnswer; } @@ -2214,7 +2228,7 @@ namespace MDNSImplementation stcMDNS_RRDomain reverseIP4Domain; for (netif* pNetIf = netif_list; pNetIf; pNetIf = pNetIf->next) { - if (netif_is_up(pNetIf)) + if (netif_is_up(pNetIf) && IPAddress(pNetIf->ip_addr).isSet()) { if ((_buildDomainForReverseIP4(pNetIf->ip_addr, reverseIP4Domain)) && (p_RRHeader.m_Domain == reverseIP4Domain)) diff --git a/libraries/ESP8266mDNS/src/LEAmDNS_Structs.cpp b/libraries/ESP8266mDNS/src/LEAmDNS_Structs.cpp index 977a924394..637f62869a 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); } @@ -2172,9 +2162,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/ESP8266mDNS/src/LEAmDNS_Transfer.cpp b/libraries/ESP8266mDNS/src/LEAmDNS_Transfer.cpp index 5074b74cfb..ed58f1b86f 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS_Transfer.cpp +++ b/libraries/ESP8266mDNS/src/LEAmDNS_Transfer.cpp @@ -122,7 +122,7 @@ namespace MDNSImplementation for (netif* pNetIf = netif_list; pNetIf; pNetIf = pNetIf->next) { - if (netif_is_up(pNetIf)) + if (netif_is_up(pNetIf) && IPAddress(pNetIf->ip_addr).isSet()) { IPAddress fromIPAddress; // fromIPAddress = _getResponseMulticastInterface(); @@ -1766,11 +1766,13 @@ namespace MDNSImplementation stcMDNS_RRAttributes attributes(DNS_RRTYPE_AAAA, ((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet - bool bResult = ((_writeMDNSHostDomain(m_pcHostname, false, p_rSendParameter)) && // esp8266.local - (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS - (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_HOST_TTL), p_rSendParameter)) && // TTL - (_write16(MDNS_IP6_SIZE, p_rSendParameter)) && // RDLength - (false /*TODO: IP6 version of: _udpAppendBuffer((uint32_t)p_IPAddress, MDNS_IP4_SIZE)*/)); // RData + bool bResult + = ((_writeMDNSHostDomain(m_pcHostname, false, p_rSendParameter)) && // esp8266.local + (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS + (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_HOST_TTL), p_rSendParameter)) + && // TTL + (_write16(MDNS_IP6_SIZE, p_rSendParameter)) && // RDLength + (false /*TODO: IP6 version of: _udpAppendBuffer((uint32_t)p_IPAddress, MDNS_IP4_SIZE)*/)); // RData DEBUG_EX_ERR(if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_AAAA: FAILED!\n")); @@ -1855,7 +1857,7 @@ namespace MDNSImplementation p_rSendParameter.m_u16Offset)) && (_writeMDNSRRDomain(hostDomain, p_rSendParameter))) // Host, eg. esp8266.local - // Cache available for domain + // Cache available for domain : ((MDNS_DOMAIN_COMPRESS_MARK > ((u16CachedDomainOffset >> 8) & ~MDNS_DOMAIN_COMPRESS_MARK)) && // Valid offset diff --git a/libraries/FSTools/FSTools.cpp b/libraries/FSTools/FSTools.cpp new file mode 100644 index 0000000000..c18e968b76 --- /dev/null +++ b/libraries/FSTools/FSTools.cpp @@ -0,0 +1,254 @@ +#include "FSTools.h" +#include "LittleFS.h" +#include +#include + + + +#if defined(DEBUG_ESP_CORE) +#define FSTOOLSDEBUG(_1, ...) { DEBUG_ESP_PORT.printf_P( PSTR(_1),##__VA_ARGS__); } +#else +#define FSTOOLSDEBUG(...) {} +#endif + + +FSTools::FSTools() +{ + +} + +FSTools::~FSTools() +{ + reset(); +} + + +bool FSTools::attemptToMountFS(fs::FS & fs) +{ + LittleFSConfig littleFSCfg(false); + SPIFFSConfig SPIFFSCfg(false); + // try to apply the "safe" no format config to the FS... doesn't matter which.. just need one to apply correctly.. + if (!fs.setConfig(littleFSCfg) && ! fs.setConfig(SPIFFSCfg)) + { + return false; + } + return fs.begin(); +} + + +bool FSTools::mountAlternativeFS(FST::FS_t type, const FST::layout & layout, bool keepMounted) +{ + FSConfig * pCfg{nullptr}; + LittleFSConfig littleFSCfg(false); + SPIFFSConfig SPIFFSCfg(false); + reset(); + + switch (type) + { + case FST::SPIFFS : + { + _pFS.reset(new FS(FSImplPtr(new spiffs_impl::SPIFFSImpl(_getStartAddr(layout), _getSize(layout), layout.page, layout.block, 5)))); + pCfg = &SPIFFSCfg; + break; + } + case FST::LITTLEFS : + { + _pFS.reset(new FS(FSImplPtr(new littlefs_impl::LittleFSImpl(_getStartAddr(layout), _getSize(layout), layout.page, layout.block, 5)))); + pCfg = &littleFSCfg; + break; + } + }; + + if (_pFS && pCfg && _pFS->setConfig(*pCfg) && _pFS->begin()) + { + if (!keepMounted) + { + _pFS->end(); + } + _mounted = true; + _layout = &layout; + return true; + } + + if (_pFS) + { + _pFS.reset(); + } + _mounted = false; + return false; +}; + + +bool FSTools::mounted() +{ + return _mounted; +}; + + +bool FSTools::moveFS(fs::FS & destinationFS) +{ + uint32_t sourceFileCount = 0; + uint32_t sourceByteTotal = 0; + bool result = false; + + if (!_mounted || !_pFS) + { + FSTOOLSDEBUG("Source FS not mounted\n"); + return false; + } + + uint32_t startSector = (ESP.getSketchSize() + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); + uint32_t lowestFSStart = 0x40300000; + + if (_layout) + { + lowestFSStart = _layout->startAddr; + FSTOOLSDEBUG("_layout->startADDR = 0x%08x\n", _layout->startAddr); + } + + uint32_t endSector = lowestFSStart - 0x40200000; + uint32_t tempFSsize = endSector - startSector; + + FSTOOLSDEBUG("TempFS: start: %u, end: %u, size: %u, sketchSize = %u, _FS_start = %u\n", startSector, endSector, tempFSsize, ESP.getSketchSize(), (uint32_t)&_FS_start); + + fileListIterator(*_pFS, "/", [&sourceFileCount, &sourceByteTotal, this](File & f) + { + if (f) + { + sourceFileCount++; + sourceByteTotal += f.size(); + +#ifdef DEBUG_ESP_CORE + _dumpFileInfo(f); +#endif + + } + }); + + FSTOOLSDEBUG("%u Files Found Total Size = %u\n", sourceFileCount, sourceByteTotal); + FSTOOLSDEBUG("Size of dummy FS = %u\n", tempFSsize); + + FS tempFS = FS(FSImplPtr(new littlefs_impl::LittleFSImpl(startSector, tempFSsize, FS_PHYS_PAGE, FS_PHYS_BLOCK, 5))); + + if (tempFS.format() && tempFS.begin()) + { + if (_copyFS(*_pFS, tempFS)) + { + FSTOOLSDEBUG("Files copied to temp File System\n"); + reset(); + if (destinationFS.format() && destinationFS.begin()) // must format then mount the new FS + { + if (_copyFS(tempFS, destinationFS)) + { + FSTOOLSDEBUG("Files copied back to new FS\n"); + result = true; + } + } + else + { + FSTOOLSDEBUG("Error Mounting\n"); + } + } + else + { + FSTOOLSDEBUG("Copy Failed\n"); + } + tempFS.end(); + } + else + { + FSTOOLSDEBUG("Failed to begin() TempFS\n"); + } + return result; +}; + +void FSTools::reset() +{ + _mounted = false; + _layout = nullptr; + if (_pFS) + { + _pFS->end(); + _pFS.reset(); + } +} + +void FSTools::fileListIterator(FS & fs, const char * dirName, FST::FileCb Cb) +{ + Dir dir = fs.openDir(dirName); + while (dir.next()) + { + if (dir.isFile()) + { + File f = dir.openFile("r"); + if (Cb) + { + Cb(f); + } + } + else + { + fileListIterator(fs, dir.fileName().c_str(), Cb); + } + } +} + + +uint32_t FSTools::_getStartAddr(const FST::layout & layout) +{ + return (layout.startAddr - 0x40200000); +} + +uint32_t FSTools::_getSize(const FST::layout & layout) +{ + return (layout.endAddr - layout.startAddr); +} + +#ifdef DEBUG_ESP_CORE +void FSTools::_dumpFileInfo(File & f) +{ + if (f) + { + DEBUG_ESP_PORT.printf_P(PSTR(" File: %-30s [%8uB]\n"), f.fullName(), f.size()); + } +} +#endif + +bool FSTools::_copyFS(FS & sourceFS, FS & destFS) +{ + uint32_t sourceFileCount = 0; + uint32_t sourceByteTotal = 0; + + fileListIterator(sourceFS, "/", [&sourceFileCount, &sourceByteTotal](File & f) + { + if (f) + { + sourceFileCount++; + sourceByteTotal += f.size(); + } + }); + + size_t count = 0; + fileListIterator(sourceFS, "/", [&count, &destFS](File & sourceFile) + { + if (sourceFile) + { + File destFile = destFS.open(sourceFile.fullName(), "w"); + if (destFile) + { + destFile.setTimeout(5000); // this value was chosen empirically as it failed with default timeout. + size_t written = destFile.write(sourceFile); + if (written == sourceFile.size()) + { + count++; + } + } + destFile.close(); + sourceFile.close(); + yield(); + } + }); + + return (count == sourceFileCount); + +} diff --git a/libraries/FSTools/FSTools.h b/libraries/FSTools/FSTools.h new file mode 100644 index 0000000000..4722a1742b --- /dev/null +++ b/libraries/FSTools/FSTools.h @@ -0,0 +1,91 @@ +#pragma once + +#include +#include +#include +/* + + A temporary FS is made between the END of the sketch... and the start of the partition you try to mount, to maximise the available space for copying the FS. + The WORST case this is at 0x40300000 which is for a 3m FS on 4m flash.. leaving 460Kb for copying. + +*/ + + + +namespace FST +{ + + struct layout + { + constexpr layout(uint32_t s, uint32_t e, uint32_t p, uint32_t b) : startAddr(s), endAddr(e), page(p), block(b) {}; + const uint32_t startAddr{0}; + const uint32_t endAddr{0}; + const uint32_t page{0}; + const uint32_t block{0}; + }; + + enum FS_t : uint8_t + { + SPIFFS, + LITTLEFS + }; + + static constexpr layout layout_512k32 = { 0x40273000, 0x4027B000, 0x100, 0x1000 }; + static constexpr layout layout_512k64 = { 0x4026B000, 0x4027B000, 0x100, 0x1000 }; + static constexpr layout layout_512k128 = { 0x4025B000, 0x4027B000, 0x100, 0x1000 }; + + static constexpr layout layout_1m64 = { 0x402EB000, 0x402FB000, 0x100, 0x1000 }; + static constexpr layout layout_1m128 = { 0x402DB000, 0x402FB000, 0x100, 0x1000 }; + static constexpr layout layout_1m144 = { 0x402D7000, 0x402FB000, 0x100, 0x1000 }; + static constexpr layout layout_1m160 = { 0x402D3000, 0x402FB000, 0x100, 0x1000 }; + static constexpr layout layout_1m192 = { 0x402CB000, 0x402FB000, 0x100, 0x1000 }; + static constexpr layout layout_1m256 = { 0x402BB000, 0x402FB000, 0x100, 0x1000 }; + static constexpr layout layout_1m512 = { 0x4027B000, 0x402FB000, 0x100, 0x2000 }; + + static constexpr layout layout_2m64 = { 0x403F0000, 0x403FB000, 0x100, 0x1000 }; + static constexpr layout layout_2m128 = { 0x403E0000, 0x403FB000, 0x100, 0x1000 }; + static constexpr layout layout_2m256 = { 0x403C0000, 0x403FB000, 0x100, 0x1000 }; + static constexpr layout layout_2m512 = { 0x40380000, 0x403FA000, 0x100, 0x2000 }; + static constexpr layout layout_2m1m = { 0x40300000, 0x403FA000, 0x100, 0x2000 }; + + static constexpr layout layout_4m1m = { 0x40500000, 0x405FA000, 0x100, 0x2000 }; + static constexpr layout layout_4m2m = { 0x40400000, 0x405FA000, 0x100, 0x2000 }; + static constexpr layout layout_4m3m = { 0x40300000, 0x405FA000, 0x100, 0x2000 }; + + static constexpr layout layout_8m6m = { 0x40400000, 0x409FA000, 0x100, 0x2000 }; + static constexpr layout layout_8m7m = { 0x40300000, 0x409FA000, 0x100, 0x2000 }; + + static constexpr layout layout_16m14m = { 0x40400000, 0x411FA000, 0x100, 0x2000 }; + static constexpr layout layout_16m15m = { 0x40300000, 0x411FA000, 0x100, 0x2000 }; + + typedef std::function FileCb; + +}; + + +class FSTools +{ +public: + + FSTools(); + ~FSTools(); + bool attemptToMountFS(fs::FS & fs); + bool mountAlternativeFS(FST::FS_t type, const FST::layout & layout, bool keepMounted = false); + bool mounted(); + bool moveFS(fs::FS & destinationFS); + void reset(); + void fileListIterator(FS & fs, const char * dirName, FST::FileCb Cb); + +private: + uint32_t _getStartAddr(const FST::layout & layout); + uint32_t _getSize(const FST::layout & layout); +#ifdef DEBUG_ESP_CORE + void _dumpFileInfo(File & f); +#endif + bool _copyFS(FS & sourceFS, FS & destFS); + + std::unique_ptr _pFS; + bool _mounted{false}; + const FST::layout * _layout{nullptr}; + +}; \ No newline at end of file diff --git a/libraries/FSTools/examples/Basic_example/Basic_example.ino b/libraries/FSTools/examples/Basic_example/Basic_example.ino new file mode 100644 index 0000000000..efa8b5740d --- /dev/null +++ b/libraries/FSTools/examples/Basic_example/Basic_example.ino @@ -0,0 +1,130 @@ +/* + + This sketch will convert SPIFFS partitions to LittleFS on ESP8266 + + Change the `TARGET_FS_LAYOUT` to the partition layout that you want target + ie what you are trying to copy from. + + Include in the sketch whatever you want the destination to be, in this case LittleFS, + but it could be SPIFFS to convert back if need be. + + How it works: It creates a LittleFS partition between the end of the sketch and the + start of whatever filesystem you set as target. This has IMPORTANT implications for the + amount of data you can move!!! eg a 4Mb flash module with a 3Mb SPIFFS partition only leaves + about 450k for the temp file system, so if you have more data than that on your 3Mb SPIFFS it + will fail. + +*/ + + + +#include +#include +#include +#include +#include +#include + + +#define TARGET_FS_LAYOUT FST::layout_4m3m + +FSTools fstools; + +#ifndef STASSID +#define STASSID "xxxx" +#define STAPSK "xxxx" +#endif + +const char* ssid = STASSID; +const char* password = STAPSK; + + +bool migrateFS() { + if (!fstools.attemptToMountFS(LittleFS)) { // Attempts to mount LittleFS without autoformat... + Serial.println(F("Default FS not found")); + if (fstools.mountAlternativeFS(FST::SPIFFS /* FST::LITTLEFS */, TARGET_FS_LAYOUT, true)) { + Serial.println(F("Alternative found")); + if (fstools.moveFS(LittleFS)) { + Serial.println(F("FileSystem Moved New FS contents:")); + fstools.fileListIterator(LittleFS, "/", [](File& f) { + Serial.printf_P(PSTR(" File: %-30s [%8uB]\n"), f.fullName(), f.size()); + }); + return true; + } + } + } + return false; +} + + +void initWiFiOTA() { + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + while (WiFi.waitForConnectResult() != WL_CONNECTED) { + Serial.println("Connection Failed! Rebooting..."); + delay(5000); + ESP.restart(); + } + + ArduinoOTA.onStart([]() { + String type; + if (ArduinoOTA.getCommand() == U_FLASH) { + type = "sketch"; + } else { // U_FS + type = "filesystem"; + } + + // NOTE: if updating FS this would be the place to unmount FS using FS.end() + Serial.println("Start updating " + type); + }); + ArduinoOTA.onEnd([]() { + Serial.println("\nEnd"); + }); + ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { + Serial.printf("Progress: %u%%\r", (progress / (total / 100))); + }); + ArduinoOTA.onError([](ota_error_t error) { + Serial.printf("Error[%u]: ", error); + if (error == OTA_AUTH_ERROR) { + Serial.println("Auth Failed"); + } else if (error == OTA_BEGIN_ERROR) { + Serial.println("Begin Failed"); + } else if (error == OTA_CONNECT_ERROR) { + Serial.println("Connect Failed"); + } else if (error == OTA_RECEIVE_ERROR) { + Serial.println("Receive Failed"); + } else if (error == OTA_END_ERROR) { + Serial.println("End Failed"); + } + }); + ArduinoOTA.begin(); +} + +void setup() { + + WiFi.persistent(false); + WiFi.disconnect(true); + Serial.begin(115200); + + Serial.println(); + Serial.printf("SDK Version: %s\n", ESP.getSdkVersion()); + Serial.println("Core Version: " + ESP.getCoreVersion()); + Serial.println("Full Version: " + ESP.getFullVersion()); + + Serial.printf("Sketch size: %u\n", ESP.getSketchSize()); + Serial.printf("Free size: %u\n", ESP.getFreeSketchSpace()); + + Serial.println("Booting"); + + migrateFS(); // MUST call this before calling your own begin(); + + initWiFiOTA(); + + Serial.println("Ready"); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); +} + +void loop() { + ArduinoOTA.handle(); +} diff --git a/libraries/FSTools/examples/custom_FS_example/custom_FS_example.ino b/libraries/FSTools/examples/custom_FS_example/custom_FS_example.ino new file mode 100644 index 0000000000..3dc2e804fc --- /dev/null +++ b/libraries/FSTools/examples/custom_FS_example/custom_FS_example.ino @@ -0,0 +1,137 @@ +/* + + This sketch will convert SPIFFS partitions to a custom FS on ESP8266 + + Change the `TARGET_FS_LAYOUT` to the partition layout that you want target + ie what you are trying to copy from. + + This ksetch shows how to create a FS different to the one provided for by the sketch defaults. + This is useful if you need to use an intermediate sketch to move the FS but need to maintain the + sketch size limit of say 512kb. + + How it works: It creates a LittleFS partition between the end of the sketch and the + start of whatever filesystem you set as target. This has IMPORTANT implications for the + amount of data you can move!!! eg a 4Mb flash module with a 3Mb SPIFFS partition only leaves + about 450k for the temp file system, so if you have more data than that on your 3Mb SPIFFS it + will fail. + +*/ + + + +#include +#include +#include +#include +#include +#include + + +#define TARGET_FS_LAYOUT FST::layout_4m3m + +const uint32_t startSector = FST::layout_4m1m.startAddr - 0x40200000; +const uint32_t tempFSsize = FST::layout_4m1m.endAddr - FST::layout_4m1m.startAddr; + +fs::FS LittleFS_Different = FS(FSImplPtr(new littlefs_impl::LittleFSImpl(startSector, tempFSsize, FS_PHYS_PAGE, FS_PHYS_BLOCK, 5))); + + +FSTools fstools; + +#ifndef STASSID +#define STASSID "xxxx" +#define STAPSK "xxxx" +#endif + +const char* ssid = STASSID; +const char* password = STAPSK; + + +bool migrateFS() { + if (!fstools.attemptToMountFS(LittleFS_Different)) { // Attempts to mount LittleFS without autoformat... + Serial.println(F("Default FS not found")); + if (fstools.mountAlternativeFS(FST::SPIFFS /* FST::LITTLEFS */, TARGET_FS_LAYOUT, true)) { + Serial.println(F("Alternative found")); + if (fstools.moveFS(LittleFS_Different)) { + Serial.println(F("FileSystem Moved New FS contents:")); + fstools.fileListIterator(LittleFS_Different, "/", [](File& f) { + Serial.printf_P(PSTR(" File: %-30s [%8uB]\n"), f.fullName(), f.size()); + }); + return true; + } + } + } + return false; +} + + +void initWiFiOTA() { + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + while (WiFi.waitForConnectResult() != WL_CONNECTED) { + Serial.println("Connection Failed! Rebooting..."); + delay(5000); + ESP.restart(); + } + + ArduinoOTA.onStart([]() { + String type; + if (ArduinoOTA.getCommand() == U_FLASH) { + type = "sketch"; + } else { // U_FS + type = "filesystem"; + } + + // NOTE: if updating FS this would be the place to unmount FS using FS.end() + Serial.println("Start updating " + type); + }); + ArduinoOTA.onEnd([]() { + Serial.println("\nEnd"); + }); + ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { + Serial.printf("Progress: %u%%\r", (progress / (total / 100))); + }); + ArduinoOTA.onError([](ota_error_t error) { + Serial.printf("Error[%u]: ", error); + if (error == OTA_AUTH_ERROR) { + Serial.println("Auth Failed"); + } else if (error == OTA_BEGIN_ERROR) { + Serial.println("Begin Failed"); + } else if (error == OTA_CONNECT_ERROR) { + Serial.println("Connect Failed"); + } else if (error == OTA_RECEIVE_ERROR) { + Serial.println("Receive Failed"); + } else if (error == OTA_END_ERROR) { + Serial.println("End Failed"); + } + }); + ArduinoOTA.begin(); +} + +void setup() { + + WiFi.persistent(false); + WiFi.disconnect(true); + Serial.begin(115200); + + Serial.println(); + Serial.printf("SDK Version: %s\n", ESP.getSdkVersion()); + Serial.println("Core Version: " + ESP.getCoreVersion()); + Serial.println("Full Version: " + ESP.getFullVersion()); + + Serial.printf("Sketch size: %u\n", ESP.getSketchSize()); + Serial.printf("Free size: %u\n", ESP.getFreeSketchSpace()); + + Serial.println("Booting"); + + migrateFS(); // MUST call this before calling your own begin(); + + initWiFiOTA(); + + Serial.println("Ready"); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); +} + +void loop() { + ArduinoOTA.handle(); +} diff --git a/libraries/FSTools/package.json b/libraries/FSTools/package.json new file mode 100644 index 0000000000..d10f43fc87 --- /dev/null +++ b/libraries/FSTools/package.json @@ -0,0 +1,10 @@ +{ + "name": "FSTools", + "keywords": "SPIFFS LittleFS", + "description": "A library that manages convertion between SPIFFS and LITTLEFS as well as mounting partitions outside of sketch default.", + "homepage": "", + "author": "sticilface", + "version": "1.0.0", + "frameworks": "arduino", + "platforms": "esp8266" + } \ No newline at end of file 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/examples/SpeedTest/SpeedTest.ino b/libraries/LittleFS/examples/SpeedTest/SpeedTest.ino index 5b0870b37b..c3dd6b2c1f 100644 --- a/libraries/LittleFS/examples/SpeedTest/SpeedTest.ino +++ b/libraries/LittleFS/examples/SpeedTest/SpeedTest.ino @@ -8,8 +8,8 @@ // WARNING: The filesystem will be formatted at the start of the test! #define TESTFS LittleFS -//#define TESTFS SPIFFS -//#define TESTFS SDFS +// #define TESTFS SPIFFS +// #define TESTFS SDFS // How large of a file to test #define TESTSIZEKB 512 diff --git a/libraries/LittleFS/lib/littlefs b/libraries/LittleFS/lib/littlefs index 1a59954ec6..6a53d76e90 160000 --- a/libraries/LittleFS/lib/littlefs +++ b/libraries/LittleFS/lib/littlefs @@ -1 +1 @@ -Subproject commit 1a59954ec64ca168828a15242cc6de94ac75f9d1 +Subproject commit 6a53d76e90af33f0656333c1db09bd337fa75d23 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/Netdump/examples/Netdump/Netdump.ino b/libraries/Netdump/examples/Netdump/Netdump.ino index 37210a8a0a..35ffc6b493 100644 --- a/libraries/Netdump/examples/Netdump/Netdump.ino +++ b/libraries/Netdump/examples/Netdump/Netdump.ino @@ -4,7 +4,7 @@ #include #include #include -//#include +// #include #include #include 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/SD/src/SD.cpp b/libraries/SD/src/SD.cpp index a3f0431dd8..45302fb8a7 100644 --- a/libraries/SD/src/SD.cpp +++ b/libraries/SD/src/SD.cpp @@ -1,5 +1,14 @@ #include "SD.h" +static_assert(__builtin_strcmp(SDClassFileMode(FILE_READ), "r") == 0, ""); +static_assert(__builtin_strcmp(SDClassFileMode(FILE_WRITE), "a+") == 0, ""); + +static_assert(__builtin_strcmp(SDClassFileMode(O_RDONLY), "r") == 0, ""); +static_assert(__builtin_strcmp(SDClassFileMode(O_WRONLY), "w+") == 0, ""); +static_assert(__builtin_strcmp(SDClassFileMode(O_RDWR), "w+") == 0, ""); +static_assert(__builtin_strcmp(SDClassFileMode(O_WRONLY | O_APPEND), "a") == 0, ""); +static_assert(__builtin_strcmp(SDClassFileMode(O_RDWR | O_APPEND), "a+") == 0, ""); + #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SD) SDClass SD; #endif diff --git a/libraries/SD/src/SD.h b/libraries/SD/src/SD.h index 73e9e76f2b..8511dfe584 100644 --- a/libraries/SD/src/SD.h +++ b/libraries/SD/src/SD.h @@ -24,11 +24,44 @@ #include #include +// Avoid type ambiguity, force u8 instead of untyped literal +// ref. #6106 as to why we add APPEND to WRITE + +inline constexpr uint8_t SDClassFileRead { FILE_READ }; #undef FILE_READ -#define FILE_READ ((uint8_t)O_READ) -#undef FILE_WRITE -#define FILE_WRITE ((uint8_t)(O_READ | O_WRITE | O_CREAT | O_APPEND)) +#define FILE_READ SDClassFileRead +inline constexpr uint8_t SDClassFileWrite { FILE_WRITE | O_APPEND }; +#undef FILE_WRITE +#define FILE_WRITE SDClassFileWrite + +static inline constexpr const char* SDClassFileMode(uint8_t mode) { + bool read = false; + bool write = false; + + switch (mode & O_ACCMODE) { + case O_RDONLY: + read = true; + break; + case O_WRONLY: + write = true; + break; + case O_RDWR: + read = true; + write = true; + break; + } + + const bool append = (mode & O_APPEND) > 0; + + if ( read && !write ) { return "r"; } + else if ( !read && write && !append ) { return "w+"; } + else if ( !read && write && append ) { return "a"; } + else if ( read && write && !append ) { return "w+"; } // may be a bug in FS::mode interpretation, "r+" seems proper + else if ( read && write && append ) { return "a+"; } + + return "r"; +} class SDClass { public: @@ -45,7 +78,7 @@ class SDClass { } fs::File open(const char *filename, uint8_t mode = FILE_READ) { - return SDFS.open(filename, getMode(mode)); + return SDFS.open(filename, SDClassFileMode(mode)); } fs::File open(const char *filename, const char *mode) { @@ -158,18 +191,6 @@ class SDClass { } private: - const char *getMode(uint8_t mode) { - bool read = (mode & O_READ) ? true : false; - bool write = (mode & O_WRITE) ? true : false; - bool append = (mode & O_APPEND) ? true : false; - if ( read & !write ) { return "r"; } - else if ( !read & write & !append ) { return "w+"; } - else if ( !read & write & append ) { return "a"; } - else if ( read & write & !append ) { return "w+"; } // may be a bug in FS::mode interpretation, "r+" seems proper - else if ( read & write & append ) { return "a+"; } - else { return "r"; } - } - static time_t wrapperTimeCB(void) { extern void (*__SD__userDateTimeCB)(uint16_t*, uint16_t*); if (__SD__userDateTimeCB) { diff --git a/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp index 93bb027486..2650998b69 100644 --- a/libraries/SPI/SPI.cpp +++ b/libraries/SPI/SPI.cpp @@ -515,9 +515,11 @@ void SPIClass::writePattern(const uint8_t * data, uint8_t size, uint32_t repeat) } } //End orig - setDataBits(repeatRem * 8); - SPI1CMD |= SPIBUSY; - while(SPI1CMD & SPIBUSY) {} + if (repeatRem) { + setDataBits(repeatRem * 8); + SPI1CMD |= SPIBUSY; + while(SPI1CMD & SPIBUSY) {} + } SPI1U = SPIUMOSI | SPIUDUPLEX | SPIUSSE; } diff --git a/libraries/Servo/src/Servo.cpp b/libraries/Servo/src/Servo.cpp index 69ed476d84..1d19f62683 100644 --- a/libraries/Servo/src/Servo.cpp +++ b/libraries/Servo/src/Servo.cpp @@ -58,7 +58,7 @@ Servo::~Servo() { uint8_t Servo::attach(int pin) { - return attach(pin, DEFAULT_MIN_PULSE_WIDTH, DEFAULT_MAX_PULSE_WIDTH); + return attach(pin, _minUs, _maxUs); } uint8_t Servo::attach(int pin, uint16_t minUs, uint16_t maxUs) @@ -94,7 +94,6 @@ void Servo::detach() delay(REFRESH_INTERVAL / 1000); // long enough to complete active period under all circumstances. stopWaveform(_pin); _attached = false; - _valueUs = DEFAULT_NEUTRAL_PULSE_WIDTH; } } diff --git a/libraries/Servo/src/Servo.h b/libraries/Servo/src/Servo.h index 9efb7b2c0d..d40939c2aa 100644 --- a/libraries/Servo/src/Servo.h +++ b/libraries/Servo/src/Servo.h @@ -29,7 +29,8 @@ // // attach(pin) - Attaches a servo motor to an i/o pin. // attach(pin, min, max) - Attaches to a pin setting min and max values in microseconds -// default min is 1000, max is 2000 +// attach(pin, min, max, value) - Attaches to a pin setting min, max, and current values in microseconds +// default min is 1000, max is 2000, and value is 1500. // // write() - Sets the servo angle in degrees. (invalid angle that is valid as pulse in microseconds is treated as microseconds) // writeMicroseconds() - Sets the servo pulse width in microseconds diff --git a/libraries/SoftwareSerial b/libraries/SoftwareSerial index eb4b29074b..bcfd6d10e6 160000 --- a/libraries/SoftwareSerial +++ b/libraries/SoftwareSerial @@ -1 +1 @@ -Subproject commit eb4b29074b75eacac3585bf84b5495b8f80a92cf +Subproject commit bcfd6d10e6a45a0d07705d08728f293defe9cc1d 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/Ticker/examples/TickerFunctional/TickerFunctional.ino b/libraries/Ticker/examples/TickerFunctional/TickerFunctional.ino index a023adfdd8..2d0673a17a 100644 --- a/libraries/Ticker/examples/TickerFunctional/TickerFunctional.ino +++ b/libraries/Ticker/examples/TickerFunctional/TickerFunctional.ino @@ -13,9 +13,11 @@ public: ExampleClass(int pin, int duration) : _pin(pin), _duration(duration) { pinMode(_pin, OUTPUT); - _myTicker.attach_ms(_duration, std::bind(&ExampleClass::classBlink, this)); + _myTicker.attach_ms(_duration, + [this]() { + classBlink(); + }); } - ~ExampleClass(){}; int _pin, _duration; Ticker _myTicker; @@ -53,7 +55,7 @@ void setup() { scheduledTicker.attach_ms_scheduled(100, scheduledBlink); pinMode(LED4, OUTPUT); - parameterTicker.attach_ms(100, std::bind(parameterBlink, LED4)); + parameterTicker.attach_ms(100, parameterBlink, LED4); pinMode(LED5, OUTPUT); lambdaTicker.attach_ms(100, []() { diff --git a/libraries/Ticker/src/Ticker.cpp b/libraries/Ticker/src/Ticker.cpp index dca4435dc2..8c45ffed8b 100644 --- a/libraries/Ticker/src/Ticker.cpp +++ b/libraries/Ticker/src/Ticker.cpp @@ -23,49 +23,127 @@ #include "eagle_soc.h" #include "osapi.h" +#include #include "Ticker.h" -Ticker::Ticker() - : _timer(nullptr) {} +// ETSTimer is part of the instance, and we don't have any state besides +// the things required for the callback. Allow copies and moves, but +// disable any member copies and default-init + detach() instead. Ticker::~Ticker() { detach(); } -void Ticker::_attach_ms(uint32_t milliseconds, bool repeat, callback_with_arg_t callback, void* arg) +Ticker::Ticker(const Ticker&) { - if (_timer) - { +} + +Ticker& Ticker::operator=(const Ticker&) +{ + detach(); + return *this; +} + +Ticker::Ticker(Ticker&& other) noexcept +{ + other.detach(); +} + +Ticker& Ticker::operator=(Ticker&& other) noexcept +{ + other.detach(); + detach(); + return *this; +} + +void Ticker::_attach(Ticker::Milliseconds milliseconds, bool repeat) +{ + if (_timer) { os_timer_disarm(_timer); + } else { + _timer = &_timer_internal; } - else - { - _timer = &_etsTimer; + + os_timer_setfn(_timer, + [](void* ptr) { + reinterpret_cast(ptr)->_static_callback(); + }, this); + + _repeat = repeat; + + // whenever duration excedes this limit, make timer repeatable N times + // in case it is really repeatable, it will reset itself and continue as usual + size_t total = 0; + if (milliseconds > DurationMax) { + total = 1; + while (milliseconds > DurationMax) { + total *= 2; + milliseconds /= 2; + } + _tick.reset(new callback_tick_t{ + .total = total, + .count = 0, + }); + repeat = true; } - os_timer_setfn(_timer, callback, arg); - os_timer_arm(_timer, milliseconds, repeat); + os_timer_arm(_timer, milliseconds.count(), repeat); } void Ticker::detach() { - if (!_timer) - return; - - os_timer_disarm(_timer); - _timer = nullptr; - _callback_function = nullptr; + if (_timer) { + os_timer_disarm(_timer); + _timer = nullptr; + _tick.reset(nullptr); + _callback = std::monostate{}; + } } bool Ticker::active() const { - return _timer; + return _timer != nullptr; } -void Ticker::_static_callback(void* arg) +void Ticker::_static_callback() { - Ticker* _this = reinterpret_cast(arg); - if (_this && _this->_callback_function) - _this->_callback_function(); + if (_tick) { + ++_tick->count; + if (_tick->count < _tick->total) { + return; + } + } + + // it is technically allowed to call either schedule or detach + // *during* callback execution. allow both to happen + decltype(_callback) tmp; + std::swap(tmp, _callback); + + std::visit([](auto&& callback) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + callback.func(callback.arg); + } else if constexpr (std::is_same_v) { + callback(); + } + }, tmp); + + // ...and move ourselves back only when object is in a valid state + // * ticker was not detached, zeroing timer pointer + // * nothing else replaced callback variant + if ((_timer == nullptr) || !std::holds_alternative(_callback)) { + return; + } + + std::swap(tmp, _callback); + + if (_repeat) { + if (_tick) { + _tick->count = 0; + } + return; + } + + detach(); } diff --git a/libraries/Ticker/src/Ticker.h b/libraries/Ticker/src/Ticker.h index 791ff94567..0b61487540 100644 --- a/libraries/Ticker/src/Ticker.h +++ b/libraries/Ticker/src/Ticker.h @@ -19,140 +19,217 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef TICKER_H -#define TICKER_H +#pragma once +#include #include +#include +#include + +#include #include #include class Ticker { public: - Ticker(); + // Our helper type to support any callable object + // In case of a lambda with bound variable(s), it will be destroyed + // either when the timer expires or detach() is called + using callback_function_t = std::function; + + // Native SDK type, simple function with void* argument + using callback_with_arg_t = void(*)(void*); + + // Helper type to allow type coercion on function argument + // Only works with a function pointer. Argument *must not* be larger than the size of the `void*` + template + using remove_cvref_t = typename std::remove_cv_t< + typename std::remove_reference_t>; + + template > + using callback_with_typed_arg_t = void(*)(Y); + + Ticker() = default; ~Ticker(); - typedef void (*callback_with_arg_t)(void*); - typedef std::function callback_function_t; + Ticker(const Ticker&); + Ticker& operator=(const Ticker&); + + Ticker(Ticker&&) noexcept; + Ticker& operator=(Ticker&&) noexcept; // callback will be called at following loop() after ticker fires void attach_scheduled(float seconds, callback_function_t callback) { - _callback_function = [callback]() { schedule_function(callback); }; - _attach_ms(1000UL * seconds, true); + _callback = [callback]() { + schedule_function(callback); + }; + _attach(Seconds(seconds), true); } // callback will be called in SYS ctx when ticker fires void attach(float seconds, callback_function_t callback) { - _callback_function = std::move(callback); - _attach_ms(1000UL * seconds, true); + _callback = std::move(callback); + _attach(Seconds(seconds), true); } // callback will be called at following loop() after ticker fires void attach_ms_scheduled(uint32_t milliseconds, callback_function_t callback) { - _callback_function = [callback]() { schedule_function(callback); }; - _attach_ms(milliseconds, true); + _callback = [callback]() { + schedule_function(callback); + }; + _attach(Milliseconds(milliseconds), true); } // callback will be called at following yield() after ticker fires void attach_ms_scheduled_accurate(uint32_t milliseconds, callback_function_t callback) { - _callback_function = [callback]() { schedule_recurrent_function_us([callback]() { callback(); return false; }, 0); }; - _attach_ms(milliseconds, true); + _callback = [callback]() { + schedule_recurrent_function_us([callback]() { + callback(); + return false; + }, 0); + }; + _attach(Milliseconds(milliseconds), true); } // callback will be called in SYS ctx when ticker fires void attach_ms(uint32_t milliseconds, callback_function_t callback) { - _callback_function = std::move(callback); - _attach_ms(milliseconds, true); + _callback = std::move(callback); + _attach(Milliseconds(milliseconds), true); } - // callback will be called in SYS ctx when ticker fires - template - void attach(float seconds, void (*callback)(TArg), TArg arg) + // callback will still be called in SYS ctx when ticker fires + template + void attach(float seconds, Func func, Arg arg) { -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wcast-function-type" - static_assert(sizeof(TArg) <= sizeof(void*), "attach() callback argument size must be <= sizeof(void*)"); - _attach_ms(1000UL * seconds, true, reinterpret_cast(callback), reinterpret_cast(arg)); -#pragma GCC diagnostic pop + _callback = make_callback_ptr(func, arg); + _attach(Seconds(seconds), true); } - // callback will be called in SYS ctx when ticker fires - template - void attach_ms(uint32_t milliseconds, void (*callback)(TArg), TArg arg) + // callback will still be called in SYS ctx when ticker fires + template + void attach_ms(uint32_t milliseconds, Func func, Arg arg) { -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wcast-function-type" - static_assert(sizeof(TArg) <= sizeof(void*), "attach() callback argument size must be <= sizeof(void*)"); - _attach_ms(milliseconds, true, reinterpret_cast(callback), reinterpret_cast(arg)); -#pragma GCC diagnostic pop + _callback = make_callback_ptr(func, arg); + _attach(Milliseconds(milliseconds), true); } // callback will be called at following loop() after ticker fires void once_scheduled(float seconds, callback_function_t callback) { - _callback_function = [callback]() { schedule_function(callback); }; - _attach_ms(1000UL * seconds, false); + _callback = [callback]() { schedule_function(callback); }; + _attach(Seconds(seconds), false); } // callback will be called in SYS ctx when ticker fires void once(float seconds, callback_function_t callback) { - _callback_function = std::move(callback); - _attach_ms(1000UL * seconds, false); + _callback = std::move(callback); + _attach(Seconds(seconds), false); } // callback will be called at following loop() after ticker fires void once_ms_scheduled(uint32_t milliseconds, callback_function_t callback) { - _callback_function = [callback]() { schedule_function(callback); }; - _attach_ms(milliseconds, false); + _callback = [callback]() { schedule_function(callback); }; + _attach(Milliseconds(milliseconds), false); } // callback will be called in SYS ctx when ticker fires void once_ms(uint32_t milliseconds, callback_function_t callback) { - _callback_function = std::move(callback); - _attach_ms(milliseconds, false); + _callback = std::move(callback); + _attach(Milliseconds(milliseconds), false); } // callback will be called in SYS ctx when ticker fires - template - void once(float seconds, void (*callback)(TArg), TArg arg) + template + void once(float seconds, Func func, Arg arg) { - static_assert(sizeof(TArg) <= sizeof(void*), "attach() callback argument size must be <= sizeof(void*)"); - _attach_ms(1000UL * seconds, false, reinterpret_cast(callback), reinterpret_cast(arg)); + _callback = make_callback_ptr(func, arg); + _attach(Seconds(seconds), false); } // callback will be called in SYS ctx when ticker fires - template - void once_ms(uint32_t milliseconds, void (*callback)(TArg), TArg arg) + template + void once_ms(uint32_t milliseconds, Func func, Arg arg) { - static_assert(sizeof(TArg) <= sizeof(void*), "attach() callback argument size must be <= sizeof(void*)"); - _attach_ms(milliseconds, false, reinterpret_cast(callback), reinterpret_cast(arg)); + _callback = make_callback_ptr(func, arg); + _attach(Milliseconds(milliseconds), false); } + // if active(), disables currently running timer void detach(); + bool active() const; + explicit operator bool() const { + return active(); + } + protected: - static void _static_callback(void* arg); - void _attach_ms(uint32_t milliseconds, bool repeat, callback_with_arg_t callback, void* arg); - void _attach_ms(uint32_t milliseconds, bool repeat) + // internals use this as duration + using Milliseconds = std::chrono::duration>; + + // we allow a floating point as input as well + // float -> u32 has some precision issues, though + using Seconds = std::chrono::duration>; + + // NONOS SDK timer object duration cannot be longer than 6870947 (0x68D7A3) + // when that's the case, we split execution into multiple 'ticks' + static constexpr auto DurationMax = Milliseconds(6870947); + + struct callback_tick_t { - _attach_ms(milliseconds, repeat, _static_callback, this); + uint32_t total = 0; + uint32_t count = 0; + }; + + void _static_callback(); + + void _attach(Milliseconds milliseconds, bool repeat); + void _attach(Seconds seconds, bool repeat) + { + _attach(std::chrono::duration_cast(seconds), repeat); } - ETSTimer* _timer; - callback_function_t _callback_function = nullptr; + std::unique_ptr _tick; + bool _repeat = false; + + ETSTimer* _timer = nullptr; private: - ETSTimer _etsTimer; -}; + struct callback_ptr_t + { + callback_with_arg_t func; + void* arg; + }; + + // original implementation inluded type coersion of integer values that would fit into uintptr_t + // to avoid writing these in our every method, use a generic type that automatically converts it + // (XXX it is a weird hack, though, consider removing this in the future and prever void* instead) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type" + template > + static callback_ptr_t make_callback_ptr(callback_with_typed_arg_t func, T arg) { + static_assert(sizeof(Y) <= sizeof(void*), ""); + return callback_ptr_t{ + .func = reinterpret_cast(func), + .arg = reinterpret_cast(arg), + }; + } +#pragma GCC diagnostic pop + using callback_data_t = std::variant< + std::monostate, + callback_ptr_t, + callback_function_t>; -#endif //TICKER_H + callback_data_t _callback; + ETSTimer _timer_internal{}; +}; diff --git a/libraries/esp8266/examples/GlobalBuildOptions/GlobalBuildOptions.ino b/libraries/esp8266/examples/GlobalBuildOptions/GlobalBuildOptions.ino new file mode 100644 index 0000000000..2bc18751e1 --- /dev/null +++ b/libraries/esp8266/examples/GlobalBuildOptions/GlobalBuildOptions.ino @@ -0,0 +1,31 @@ +/* + * Showcase the use of embedded build options and global defines through a specially named .h file. + * Sketch file name followed by ".globals.h", "GlobalBuildOptions.ino.globals.h" + * + * Example from https://arduino-esp8266.readthedocs.io/en/latest/faq/a06-global-build-options.html + * + * Note, we do not "#include" the special file "GlobalBuildOptions.ino.globals.h". + * The prebuild script will make it available to all modules. + * + * To track the new sketch name when saving this sketch to a new location and + * name, remember to update the global .h file name. + */ + +#include // has prototype for umm_free_heap_size_min() + +void setup() { + Serial.begin(115200); + delay(200); + +#ifdef MYTITLE1 + Serial.printf("\r\n" MYTITLE1 MYTITLE2 "\r\n"); +#else + Serial.println("ERROR: MYTITLE1 not present"); +#endif + +#if defined(UMM_STATS_FULL) + Serial.printf("Heap Low Watermark %u\r\n", umm_free_heap_size_min()); +#endif +} + +void loop() {} diff --git a/libraries/esp8266/examples/GlobalBuildOptions/GlobalBuildOptions.ino.globals.h b/libraries/esp8266/examples/GlobalBuildOptions/GlobalBuildOptions.ino.globals.h new file mode 100644 index 0000000000..b51b879f7f --- /dev/null +++ b/libraries/esp8266/examples/GlobalBuildOptions/GlobalBuildOptions.ino.globals.h @@ -0,0 +1,39 @@ +/*@create-file:build.opt@ + // An embedded build.opt file using a "C" block comment. The starting signature + // must be on a line by itself. The closing block comment pattern should be on a + // line by itself. Each line within the block comment will be space trimmed and + // written to build.opt, skipping blank lines and lines starting with '//', '*' + // or '#'. + -DMYTITLE1="\"Running on \"" + * this line is ignored + *@create-file:build.opt@ + # this line is ignored + -Og + // -fanalyzer + -DUMM_STATS_FULL=1 +*/ + +#ifndef GLOBALBUILDOPTIONS_INO_GLOBALS_H +#define GLOBALBUILDOPTIONS_INO_GLOBALS_H + +#if !defined(__ASSEMBLER__) +// Defines kept away from assembler modules +// i.e. Defines for .cpp, .ino, .c ... modules +#endif + +#if defined(__cplusplus) +// Defines kept private to .cpp and .ino modules +//#pragma message("__cplusplus has been seen") +#define MYTITLE2 "Empty" +#endif + +#if !defined(__cplusplus) && !defined(__ASSEMBLER__) +// Defines kept private to .c modules +#define MYTITLE2 "~Full" +#endif + +#if defined(__ASSEMBLER__) +// Defines kept private to assembler modules +#endif + +#endif diff --git a/libraries/esp8266/examples/HwdtStackDump/HwdtStackDump.ino b/libraries/esp8266/examples/HwdtStackDump/HwdtStackDump.ino index babd15a9c3..f9c692ccf0 100644 --- a/libraries/esp8266/examples/HwdtStackDump/HwdtStackDump.ino +++ b/libraries/esp8266/examples/HwdtStackDump/HwdtStackDump.ino @@ -47,8 +47,6 @@ extern "C" { //////////////////////////////////////////////////////////////////// void setup(void) { - WiFi.persistent(false); // w/o this a flash write occurs at every boot - WiFi.mode(WIFI_OFF); Serial.begin(115200); delay(20); // This delay helps when using the 'Modified Serial monitor' otherwise it is not needed. Serial.println(); @@ -56,16 +54,20 @@ void setup(void) { Serial.println(F("The Hardware Watchdog Timer Demo is starting ...")); Serial.println(); +#ifdef DEMO_THUNK // This allows us to test dumping a BearSSL stack after HWDT. stack_thunk_add_ref(); thunk_ets_uart_printf("Using Thunk Stack to print this line.\n\n"); +#endif +#ifdef DEMO_WIFI // We don't need this for this example; however, starting WiFi uses a little // more of the SYS stack. WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); Serial.println(F("A WiFi connection attempt has been started.")); Serial.println(); +#endif // #define DEMO_NOEXTRA4K #ifdef DEMO_NOEXTRA4K diff --git a/libraries/esp8266/examples/HwdtStackDump/HwdtStackDump.ino.globals.h b/libraries/esp8266/examples/HwdtStackDump/HwdtStackDump.ino.globals.h new file mode 100644 index 0000000000..cb624cacec --- /dev/null +++ b/libraries/esp8266/examples/HwdtStackDump/HwdtStackDump.ino.globals.h @@ -0,0 +1,51 @@ +/*@create-file:build.opt:debug@ +// For this block to work, you must have +// `mkbuildoptglobals.extra_flags={build.debug_port}` in `platform.local.txt` +// Or move contents to the block with the signature "@create-file:build.opt@" + + +// Removing the optimization for "sibling and tail recursive calls" will clear +// up some gaps in the stack decoder report. Preserves stack frames created at +// each level as you call down to the next. +-fno-optimize-sibling-calls + + +// Adds a pointer toward the end of the stack frame that points to the beginning +// of the stack frame. The stack dump will annotate the line where it occurs +// with a `<` mark. +-fno-omit-frame-pointer + + +// Options for HWDT Stack Dump (hwdt_app_entry.cpp) + +// Alter the UART serial speed used for printing the Hardware WDT reset stack +// dump. Without this option on an HWDT reset, the existing default speed of +// 115200 bps will be used. If you are using this default speed, you can skip +// this option. Note this option only changes the speed while the stack dump is +// printing. Prior settings are restored. +// -DDEBUG_ESP_HWDT_UART_SPEED=19200 +// -DDEBUG_ESP_HWDT_UART_SPEED=74880 +// -DDEBUG_ESP_HWDT_UART_SPEED=115200 +// -DDEBUG_ESP_HWDT_UART_SPEED=230400 + +// HWDT Stack Dump defaults to print a simple introduction to let you know the +// tool is active and in the build. At power-on, this may not be viewable on +// some devices. Use the DEBUG_ESP_HWDT_UART_SPEED option above to improve. +// Or uncomment line below to turn off greeting +// -DDEBUG_ESP_HWDT_PRINT_GREETING=0 + +// Demos +-DDEMO_THUNK=1 +// -DDEMO_NOEXTRA4K=1 +-DDEMO_WIFI=1 +*/ + +/*@create-file:build.opt@ +// -fno-optimize-sibling-calls +// -fno-omit-frame-pointer + +// Demos +-DDEMO_THUNK=1 +// -DDEMO_NOEXTRA4K=1 +-DDEMO_WIFI=1 +*/ diff --git a/libraries/esp8266/examples/IramReserve/ProcessKey.ino b/libraries/esp8266/examples/IramReserve/ProcessKey.ino index 4c98899cf4..9e76aac874 100644 --- a/libraries/esp8266/examples/IramReserve/ProcessKey.ino +++ b/libraries/esp8266/examples/IramReserve/ProcessKey.ino @@ -1,6 +1,7 @@ #include void crashMeIfYouCan(void) __attribute__((weak)); int divideA_B(int a, int b); +int divideA_B_bp(int a, int b); int* nullPointer = NULL; diff --git a/libraries/esp8266/examples/LowPowerDemo/LowPowerDemo.ino b/libraries/esp8266/examples/LowPowerDemo/LowPowerDemo.ino index 9a6fbc5bfd..27dbb77184 100644 --- a/libraries/esp8266/examples/LowPowerDemo/LowPowerDemo.ino +++ b/libraries/esp8266/examples/LowPowerDemo/LowPowerDemo.ino @@ -42,7 +42,7 @@ #include #include // WiFiState structure details -//#define DEBUG // prints WiFi connection info to serial, uncomment if you want WiFi messages +// #define DEBUG // prints WiFi connection info to serial, uncomment if you want WiFi messages #ifdef DEBUG #define DEBUG_PRINTLN(x) Serial.println(x) #define DEBUG_PRINT(x) Serial.print(x) @@ -56,8 +56,8 @@ // uncomment one of the two lines below for your LED connection (optional) #define LED 5 // D1/GPIO5 external LED for modules with built-in LEDs so it doesn't add amperage -//#define LED 2 // D4/GPIO2 LED for ESP-01,07 modules; D4 is LED_BUILTIN on most other modules -// you can use LED_BUILTIN, but it adds to the measured amperage by 0.3mA to 6mA. +// #define LED 2 // D4/GPIO2 LED for ESP-01,07 modules; D4 is LED_BUILTIN on most other modules +// you can use LED_BUILTIN, but it adds to the measured amperage by 0.3mA to 6mA. ADC_MODE(ADC_VCC); // allows you to monitor the internal VCC level; it varies with WiFi load // don't connect anything to the analog input pin(s)! @@ -72,7 +72,7 @@ IPAddress dns1(0, 0, 0, 0); IPAddress dns2(0, 0, 0, 0); uint32_t timeout = 30E3; // 30 second timeout on the WiFi connection -//#define TESTPOINT // used to track the timing of several test cycles (optional) +// #define TESTPOINT // used to track the timing of several test cycles (optional) #ifdef TESTPOINT #define testPointPin 4 // D2/GPIO4, you can use any pin that supports interrupts #define testPoint_HIGH digitalWrite(testPointPin, HIGH) @@ -128,7 +128,7 @@ void setup() { // Read previous resets (Deep Sleeps) from RTC memory, if any uint32_t crcOfData = crc32((uint8_t*)&nv->rtcData.rstCount, sizeof(nv->rtcData.rstCount)); - if ((crcOfData = nv->rtcData.crc32) && (resetCause == "Deep-Sleep Wake")) { + if ((crcOfData == nv->rtcData.crc32) && (resetCause == "Deep-Sleep Wake")) { resetCount = nv->rtcData.rstCount; // read the previous reset count resetCount++; } diff --git a/libraries/esp8266/examples/NTP-TZ-DST/NTP-TZ-DST.ino b/libraries/esp8266/examples/NTP-TZ-DST/NTP-TZ-DST.ino index 6388c1a9cf..8a9ea0e82c 100644 --- a/libraries/esp8266/examples/NTP-TZ-DST/NTP-TZ-DST.ino +++ b/libraries/esp8266/examples/NTP-TZ-DST/NTP-TZ-DST.ino @@ -31,11 +31,11 @@ // check for your nearest city in TZ.h // espressif headquarter TZ -//#define MYTZ TZ_Asia_Shanghai +// #define MYTZ TZ_Asia_Shanghai // example for "Not Only Whole Hours" timezones: // Kolkata/Calcutta is shifted by 30mn -//#define MYTZ TZ_Asia_Kolkata +// #define MYTZ TZ_Asia_Kolkata // example of a timezone with a variable Daylight-Saving-Time: // demo: watch automatic time adjustment on Summer/Winter change (DST) diff --git a/libraries/esp8266/examples/SerialStress/SerialStress.ino b/libraries/esp8266/examples/SerialStress/SerialStress.ino index 4a8b6e4f45..f491ba04e5 100644 --- a/libraries/esp8266/examples/SerialStress/SerialStress.ino +++ b/libraries/esp8266/examples/SerialStress/SerialStress.ino @@ -1,17 +1,16 @@ /* Serial read/write/verify/benchmark - Using internal loopback - Using SoftwareSerial library for logging + Using Serial0 for internal loopback + Using Serial1 for logging Sketch meant for debugging only Released to public domain */ #include -#include -#define SSBAUD 115200 // logger on console for humans +#define LOGBAUD 115200 // logger on console for humans #define BAUD 3000000 // hardware serial stress test #define BUFFER_SIZE 4096 // may be useless to use more than 2*SERIAL_SIZE_RX #define SERIAL_SIZE_RX 1024 // Serial.setRxBufferSize() @@ -21,6 +20,9 @@ #define TIMEOUT 5000 #define DEBUG(x...) // x +#define READING_PIN 4 +#define TIMEOUT_PIN 5 + uint8_t buf[BUFFER_SIZE]; uint8_t temp[BUFFER_SIZE]; bool reading = true; @@ -51,18 +53,18 @@ void error(const char* what) { void setup() { pinMode(LED_BUILTIN, OUTPUT); + pinMode(READING_PIN, INPUT); + pinMode(TIMEOUT_PIN, INPUT); + Serial.begin(BAUD); Serial.swap(); // RX=GPIO13 TX=GPIO15 Serial.setRxBufferSize(SERIAL_SIZE_RX); - // using HardwareSerial0 pins, - // so we can still log to the regular usbserial chips - SoftwareSerial* ss = new SoftwareSerial(3, 1); - ss->begin(SSBAUD); - ss->enableIntTx(false); - logger = ss; + Serial1.begin(LOGBAUD); // RX=NONE TX=GPIO2 + logger = &Serial1; + logger->println(); - logger->printf("\n\nOn Software Serial for logging\n"); + logger->printf("\n\nOn Serial1 for logging\n"); int baud = Serial.baudRate(); logger->printf(ESP.getFullVersion().c_str()); @@ -79,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; @@ -140,15 +141,13 @@ void loop() { timeout = (last_ms = now_ms) + TIMEOUT; } - if (logger->available()) switch (logger->read()) { - case 's': - logger->println("now stopping reading, keeping writing"); - reading = false; - break; - case 't': - testReadBytesTimeout ^= FAKE_INCREASED_AVAILABLE; - logger->printf("testing readBytes timeout: %d\n", !!testReadBytesTimeout); - break; - default:; - } + if (reading && (digitalRead(READING_PIN) == 0)) { + logger->println("now stopping reading, keeping writing"); + reading = false; + } + + if (digitalRead(TIMEOUT_PIN) == 0) { + testReadBytesTimeout ^= FAKE_INCREASED_AVAILABLE; + logger->printf("testing readBytes timeout: %d\n", !!testReadBytesTimeout); + } } 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/esp8266/examples/TestEspApi/TestEspApi.ino b/libraries/esp8266/examples/TestEspApi/TestEspApi.ino index 903306a1dd..38e443f3ca 100644 --- a/libraries/esp8266/examples/TestEspApi/TestEspApi.ino +++ b/libraries/esp8266/examples/TestEspApi/TestEspApi.ino @@ -15,8 +15,8 @@ extern "C" { } #endif -// Set up output serial port (could be a SoftwareSerial -// if really wanted). +// Set up output on the first serial port +// (can be any Stream, if needed) Stream& ehConsolePort(Serial); diff --git a/libraries/lwIP_Ethernet/examples/EthClient/EthClient.ino b/libraries/lwIP_Ethernet/examples/EthClient/EthClient.ino new file mode 100644 index 0000000000..169930f6ef --- /dev/null +++ b/libraries/lwIP_Ethernet/examples/EthClient/EthClient.ino @@ -0,0 +1,91 @@ +/* + This sketch establishes a TCP connection to a "quote of the day" service. + It sends a "hello" message, and then prints received data. + + This is Ethernet version of: + https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266WiFi/examples/WiFiClient/WiFiClient.ino +*/ + +#include + +Wiznet5500lwIP eth(/*SS*/ 16); // <== adapt to your hardware + +const char* host = "djxmmx.net"; +const uint16_t port = 17; + +void setup() { + Serial.begin(115200); + + Serial.println("\nEthernet\n"); + + // 1. Currently when no default is set, esp8266-Arduino uses the first + // DHCP client interface receiving a valid address and gateway to + // become the new lwIP default interface. + // 2. Otherwise - when using static addresses - lwIP for every packets by + // defaults selects automatically the best suited output interface + // matching the destination address. If several interfaces match, + // the first one is picked. On esp8266/Arduno: WiFi interfaces are + // checked first. + // 3. Or, use `::setDefault()` to force routing through this interface. + // eth.setDefault(); // default route set through this interface + + if (!ethInitDHCP(eth)) { + Serial.printf("no hardware found\n"); + while (1) { + delay(1000); + } + } + + while (!eth.connected()) { + Serial.printf("."); + delay(1000); + } + + Serial.printf("Ethernet: IP Address: %s\n", + eth.localIP().toString().c_str()); +} + +void loop() { + + Serial.print("connecting to "); + Serial.print(host); + Serial.print(':'); + Serial.println(port); + + Serial.printf("Link sense: %d (detectable: %d)\n", eth.isLinked(), eth.isLinkDetectable()); + + // Use WiFiClient class to create TCP connections + // (this class could have been named TCPClient) + WiFiClient client; + if (!client.connect(host, port)) { + Serial.println("connection failed"); + delay(5000); + return; + } + + // This will send a string to the server + Serial.println("sending data to server"); + if (client.connected()) { client.println("hello from ESP8266"); } + + // wait for data to be available + unsigned long timeout = millis(); + while (client.available() == 0) { + if (millis() - timeout > 5000) { + Serial.println(">>> Client Timeout !"); + client.stop(); + delay(60000); + return; + } + } + + // Read all the lines of the reply from server and print them to Serial + Serial.println("receiving from remote server"); + client.sendAll(Serial); // this peer closes once all data are sent + + // Close the connection + Serial.println(); + Serial.println("closing connection"); + client.stop(); + + delay(300000); // execute once every 5 minutes, don't flood remote service +} diff --git a/libraries/lwIP_Ethernet/examples/EthClientStatic/EthClientStatic.ino b/libraries/lwIP_Ethernet/examples/EthClientStatic/EthClientStatic.ino new file mode 100644 index 0000000000..01e93231b1 --- /dev/null +++ b/libraries/lwIP_Ethernet/examples/EthClientStatic/EthClientStatic.ino @@ -0,0 +1,92 @@ +/* + This sketch establishes a TCP connection to a "quote of the day" service. + It sends a "hello" message, and then prints received data. + + This is Ethernet version of: + https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266WiFi/examples/WiFiClient/WiFiClient.ino +*/ + +#include + +#define LOCAL_IP IPAddress(192, 168, 0, 233) +#define LOCAL_GW IPAddress(192, 168, 0, 254) // <== adapt to your network +#define LOCAL_MASK IPAddress(255, 255, 255, 0) +#define DNS IPAddress(8, 8, 8, 8) + +Wiznet5500lwIP eth(/*SS*/ 16); // <== adapt to your hardware + +const char* host = "djxmmx.net"; +const uint16_t port = 17; + +void setup() { + Serial.begin(115200); + + Serial.println("\nEthernet\n"); + + // 1. Currently when no default is set, esp8266-Arduino uses the first + // DHCP client interface receiving a valid address and gateway to + // become the new lwIP default interface. + // 2. Otherwise - when using static addresses - lwIP for every packets by + // defaults selects automatically the best suited output interface + // matching the destination address. If several interfaces match, + // the first one is picked. On esp8266/Arduno: WiFi interfaces are + // checked first. + // 3. Or, use `::setDefault()` to force routing through this interface. + // eth.setDefault(true); // default route set through this interface + + if (!ethInitStatic(eth, LOCAL_IP, LOCAL_GW, LOCAL_MASK, DNS)) { + // enabling debug message will show the real cause + Serial.printf("no hardware found or bad network configuration\n"); + while (1) { + delay(1000); + } + } + + Serial.printf("Ethernet: IP Address: %s\n", + eth.localIP().toString().c_str()); +} + +void loop() { + + Serial.print("connecting to "); + Serial.print(host); + Serial.print(':'); + Serial.println(port); + + // Use WiFiClient class to create TCP connections + // (this class could have been named TCPClient) + WiFiClient client; + if (!client.connect(host, port)) { + Serial.println("connection failed"); + delay(5000); + return; + } + + // This will send a string to the server + Serial.println("sending data to server"); + if (client.connected()) { + client.println("hello from ESP8266"); + } + + // wait for data to be available + unsigned long timeout = millis(); + while (client.available() == 0) { + if (millis() - timeout > 5000) { + Serial.println(">>> Client Timeout !"); + client.stop(); + delay(60000); + return; + } + } + + // Read all the lines of the reply from server and print them to Serial + Serial.println("receiving from remote server"); + client.sendAll(Serial); // this peer closes once all data are sent + + // Close the connection + Serial.println(); + Serial.println("closing connection"); + client.stop(); + + delay(600000); // execute once every 10 minutes, don't flood remote service +} diff --git a/libraries/lwIP_Ethernet/examples/EthSSLValidation/EthSSLValidation.ino b/libraries/lwIP_Ethernet/examples/EthSSLValidation/EthSSLValidation.ino new file mode 100644 index 0000000000..2f8c36876c --- /dev/null +++ b/libraries/lwIP_Ethernet/examples/EthSSLValidation/EthSSLValidation.ino @@ -0,0 +1,57 @@ +// Example of the different modes of the X.509 validation options +// in the WiFiClientBearSSL object +// +// Mar 2018 by Earle F. Philhower, III +// Released to the public domain +// +// This is Ethernet version of: +// https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino + +#include + +Wiznet5500lwIP eth(/*SS*/ 16); // <== adapt to your hardware + +#define setup forgetMe +#include <../../libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino> +#undef setup + +void setup() { + Serial.begin(115200); + Serial.println(); + Serial.println(); + + Serial.println("\nEthernet\n"); + + // 1. Currently when no default route is set, esp8266-Arduino uses the first + // DHCP client interface receiving a valid address and gateway to + // become the new lwIP default interface. + // 2. Otherwise - when using static addresses - lwIP for every packets by + // defaults selects automatically the best suited output interface + // matching the destination address. If several interfaces match, + // the first one is picked. On esp8266/Arduno: WiFi interfaces are + // checked first. + // 3. Or, use `::setDefault()` to force routing through this interface. + // eth.setDefault(true); // default route set through this interface + + if (!ethInitDHCP(eth)) { + Serial.printf("no hardware found\n"); + while (1) + delay(1000); + } + + while (!eth.connected()) { + Serial.printf("."); + delay(1000); + } + + Serial.printf("Ethernet: IP Address: %s\n", + eth.localIP().toString().c_str()); + + fetchNoConfig(); + fetchInsecure(); + fetchFingerprint(); + fetchSelfSigned(); + fetchKnownKey(); + fetchCertAuthority(); + fetchFaster(); +} diff --git a/libraries/lwIP_Ethernet/examples/Eth_mDNS_Clock/Eth_mDNS_Clock.ino b/libraries/lwIP_Ethernet/examples/Eth_mDNS_Clock/Eth_mDNS_Clock.ino new file mode 100644 index 0000000000..41509ea18f --- /dev/null +++ b/libraries/lwIP_Ethernet/examples/Eth_mDNS_Clock/Eth_mDNS_Clock.ino @@ -0,0 +1,282 @@ +/* + ESP8266 mDNS responder clock + + This example demonstrates two features of the LEA MDNSResponder: + 1. The host and service domain negotiation process that ensures + the uniqueness of the finally chosen host and service domain name. + 2. The dynamic MDNS service TXT feature + + A 'clock' service in announced via the MDNS responder and the current + time is set as a TXT item (eg. 'curtime=Mon Oct 15 19:54:35 2018'). + The time value is updated every second! + + The ESP is initially announced to clients as 'esp8266.local', if this host domain + is already used in the local network, another host domain is negotiated. Keep an + eye to the serial output to learn the final host domain for the clock service. + The service itself is is announced as 'host domain'._espclk._tcp.local. + As the service uses port 80, a very simple HTTP server is installed also to deliver + a small web page containing a greeting and the current time (not updated). + The web server code is taken nearly 1:1 from the 'mDNS_Web_Server.ino' example. + Point your browser to 'host domain'.local to see this web page. + + Instructions: + - Flash the sketch to the ESP8266 board + - Install host software: + - For Linux, install Avahi (http://avahi.org/). + - For Windows, install Bonjour (http://www.apple.com/support/bonjour/). + - For Mac OSX and iOS support is built in through Bonjour already. + - Use a MDNS/Bonjour browser like 'Discovery' to find the clock service in your local + network and see the current time updates. + + This is the Ethernet version of: + https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266mDNS/examples/LEAmDNS/mDNS_Clock +*/ + + +#include +#include +#include +#include +#include +#include +#include + +Wiznet5500lwIP eth(/*SS*/ 16); // <== adapt to your hardware + +/* + Global defines and vars +*/ + +#define TIMEZONE_OFFSET 1 // CET +#define DST_OFFSET 1 // CEST +#define UPDATE_CYCLE (1 * 1000) // every second + +#define SERVICE_PORT 80 // HTTP port + +#ifndef STASSID +#define STASSID "your-ssid" +#define STAPSK "your-password" +#endif + +const char* ssid = STASSID; +const char* password = STAPSK; + +char* pcHostDomain = 0; // Negotiated host domain +bool bHostDomainConfirmed = false; // Flags the confirmation of the host domain +MDNSResponder::hMDNSService hMDNSService = 0; // The handle of the clock service in the MDNS responder + +// HTTP server at port 'SERVICE_PORT' will respond to HTTP requests +ESP8266WebServer server(SERVICE_PORT); + +/* + getTimeString +*/ +const char* getTimeString(void) { + + static char acTimeString[32]; + time_t now = time(nullptr); + ctime_r(&now, acTimeString); + size_t stLength; + while (((stLength = strlen(acTimeString))) && ('\n' == acTimeString[stLength - 1])) { + acTimeString[stLength - 1] = 0; // Remove trailing line break... + } + return acTimeString; +} + + +/* + setClock + + Set time via NTP +*/ +void setClock(void) { + configTime((TIMEZONE_OFFSET * 3600), (DST_OFFSET * 3600), "pool.ntp.org", "time.nist.gov", "time.windows.com"); + + Serial.print("Waiting for NTP time sync: "); + time_t now = time(nullptr); // Secs since 01.01.1970 (when uninitialized starts with (8 * 3600 = 28800) + while (now < 8 * 3600 * 2) { // Wait for realistic value + delay(500); + Serial.print("."); + now = time(nullptr); + } + Serial.println(""); + Serial.printf("Current time: %s\n", getTimeString()); +} + + +/* + setStationHostname +*/ +bool setStationHostname(const char* p_pcHostname) { + + if (p_pcHostname) { + WiFi.hostname(p_pcHostname); + Serial.printf("setDeviceHostname: Station hostname is set to '%s'\n", p_pcHostname); + } + return true; +} + + +/* + MDNSDynamicServiceTxtCallback + + Add a dynamic MDNS TXT item 'ct' to the clock service. + The callback function is called every time, the TXT items for the clock service + are needed. + This can be triggered by calling MDNS.announce(). + +*/ +void MDNSDynamicServiceTxtCallback(const MDNSResponder::hMDNSService p_hService) { + Serial.println("MDNSDynamicServiceTxtCallback"); + + if (hMDNSService == p_hService) { + Serial.printf("Updating curtime TXT item to: %s\n", getTimeString()); + MDNS.addDynamicServiceTxt(p_hService, "curtime", getTimeString()); + } +} + + +/* + MDNSProbeResultCallback + + Probe result callback for the host domain. + If the domain is free, the host domain is set and the clock service is + added. + If the domain is already used, a new name is created and the probing is + restarted via p_pMDNSResponder->setHostname(). + +*/ +void hostProbeResult(String p_pcDomainName, bool p_bProbeResult) { + + Serial.println("MDNSProbeResultCallback"); + Serial.printf("MDNSProbeResultCallback: Host domain '%s.local' is %s\n", p_pcDomainName.c_str(), (p_bProbeResult ? "free" : "already USED!")); + if (true == p_bProbeResult) { + // Set station hostname + setStationHostname(pcHostDomain); + + if (!bHostDomainConfirmed) { + // Hostname free -> setup clock service + bHostDomainConfirmed = true; + + if (!hMDNSService) { + // Add a 'clock.tcp' service to port 'SERVICE_PORT', using the host domain as instance domain + hMDNSService = MDNS.addService(0, "espclk", "tcp", SERVICE_PORT); + if (hMDNSService) { + // Add a simple static MDNS service TXT item + MDNS.addServiceTxt(hMDNSService, "port#", SERVICE_PORT); + // Set the callback function for dynamic service TXTs + MDNS.setDynamicServiceTxtCallback(MDNSDynamicServiceTxtCallback); + } + } + } + } else { + // Change hostname, use '-' as divider between base name and index + if (MDNSResponder::indexDomain(pcHostDomain, "-", 0)) { + MDNS.setHostname(pcHostDomain); + } else { + Serial.println("MDNSProbeResultCallback: FAILED to update hostname!"); + } + } +} + + +/* + handleHTTPClient +*/ + +void handleHTTPRequest() { + Serial.println(""); + Serial.println("HTTP Request"); + + // Get current time + time_t now = time(nullptr); + struct tm timeinfo; + gmtime_r(&now, &timeinfo); + + String s; + + s = "\r\nHello from "; + s += WiFi.hostname() + " at " + WiFi.localIP().toString(); + // Simple addition of the current time + s += "\r\nCurrent time is: "; + s += getTimeString(); + // done :-) + s += "\r\n\r\n"; + Serial.println("Sending 200"); + server.send(200, "text/html", s); +} + +/* + setup +*/ +void setup(void) { + Serial.begin(115200); + + Serial.println("\nEthernet\n"); + + // 1. Currently when no default is set, esp8266-Arduino uses the first + // DHCP client interface receiving a valid address and gateway to + // become the new lwIP default interface. + // 2. Otherwise - when using static addresses - lwIP for every packets by + // defaults selects automatically the best suited output interface + // matching the destination address. If several interfaces match, + // the first one is picked. On esp8266/Arduno: WiFi interfaces are + // checked first. + // 3. Or, use `::setDefault()` to force routing through this interface. + // eth.setDefault(); // default route set through this interface + + if (!ethInitDHCP(eth)) { + Serial.printf("no hardware found\n"); + while (1) { + delay(1000); + } + } + + while (!eth.connected()) { + Serial.printf("."); + delay(1000); + } + + Serial.printf("Ethernet: IP Address: %s\n", + eth.localIP().toString().c_str()); + + // Sync clock + setClock(); + + // Setup MDNS responder + MDNS.setHostProbeResultCallback(hostProbeResult); + // Init the (currently empty) host domain string with 'esp8266' + if ((!MDNSResponder::indexDomain(pcHostDomain, 0, "esp8266")) || (!MDNS.begin(pcHostDomain))) { + Serial.println("Error setting up MDNS responder!"); + while (1) { // STOP + delay(1000); + } + } + Serial.println("MDNS responder started"); + + // Setup HTTP server + server.on("/", handleHTTPRequest); + server.begin(); + Serial.println("HTTP server started"); +} + +/* + loop +*/ +void loop(void) { + + // Check if a request has come in + server.handleClient(); + // Allow MDNS processing + MDNS.update(); + + static esp8266::polledTimeout::periodicMs timeout(UPDATE_CYCLE); + if (timeout.expired()) { + + if (hMDNSService) { + // Just trigger a new MDNS announcement, this will lead to a call to + // 'MDNSDynamicServiceTxtCallback', which will update the time TXT item + MDNS.announce(); + } + } +} diff --git a/libraries/lwIP_Ethernet/examples/LegacyAdvancedChatServer/LegacyAdvancedChatServer.ino b/libraries/lwIP_Ethernet/examples/LegacyAdvancedChatServer/LegacyAdvancedChatServer.ino new file mode 100644 index 0000000000..74b580308f --- /dev/null +++ b/libraries/lwIP_Ethernet/examples/LegacyAdvancedChatServer/LegacyAdvancedChatServer.ino @@ -0,0 +1,126 @@ +/* + Advanced Chat Server + + A more advanced server that distributes any incoming messages + to all connected clients but the client the message comes from. + To use, telnet to your device's IP address and type. + You can see the client's input in the serial monitor as well. + Using an Arduino Wiznet Ethernet shield. + + Circuit: + * Ethernet Wiznet5500/Wiznet5100/ENC28J60 on esp8266 + + created 18 Dec 2009 + by David A. Mellis + modified 9 Apr 2012 + by Tom Igoe + redesigned to make use of operator== 25 Nov 2013 + by Norbert Truchsess + + */ + +// specific to esp8266 w/lwIP +#include +ArduinoWiznet5500lwIP Ethernet(/*SS*/ 16); // <== adapt to your hardware +// ArduinoWiznet5100lwIP Ethernet(/*SS*/ 16); // <== adapt to your hardware +// ArduinoENC28J60lwIP Ethernet(/*SS*/ 16); // <== adapt to your hardware + + + +// Enter a MAC address and IP address for your controller below. +// The IP address will be dependent on your local network. +// gateway and subnet are optional: +byte notNeededButAllowed_mac[] = { + 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED +}; +byte* mac = nullptr; // automatic mac +IPAddress ip(192, 168, 1, 177); +IPAddress myDns(192, 168, 1, 1); +IPAddress gateway(192, 168, 1, 1); +IPAddress subnet(255, 255, 0, 0); + + +// telnet defaults to port 23 +EthernetServer server(23); + +EthernetClient clients[8]; + +void setup() { + // You can use Ethernet.init(pin) to configure the CS pin + // Ethernet.init(10); // Most Arduino shields + // Ethernet.init(5); // MKR ETH shield + // Ethernet.init(0); // Teensy 2.0 + // Ethernet.init(20); // Teensy++ 2.0 + // Ethernet.init(15); // ESP8266 with Adafruit Featherwing Ethernet + // Ethernet.init(33); // ESP32 with Adafruit Featherwing Ethernet + // // esp8266 w/lwIP: SS set in Ethernet constructor + + // initialize the Ethernet device + Ethernet.begin(mac, ip, myDns, gateway, subnet); + + // Open serial communications and wait for port to open: + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + // Check for Ethernet hardware present + if (Ethernet.hardwareStatus() == EthernetNoHardware) { + Serial.println("Ethernet shield was not found. Sorry, can't run without hardware. :("); + while (true) { + delay(1); // do nothing, no point running without Ethernet hardware + } + } + if (Ethernet.linkStatus() == LinkOFF) { + Serial.println("Ethernet cable is not connected."); + } + + // start listening for clients + server.begin(); + + Serial.print("Chat server address:"); + Serial.println(Ethernet.localIP()); +} + +void loop() { + // check for any new client connecting, and say hello (before any incoming data) + EthernetClient newClient = server.accept(); + if (newClient) { + for (byte i = 0; i < 8; i++) { + if (!clients[i]) { + Serial.print("We have a new client #"); + Serial.println(i); + newClient.print("Hello, client number: "); + newClient.println(i); + // Once we "accept", the client is no longer tracked by EthernetServer + // so we must store it into our list of clients + clients[i] = newClient; + break; + } + } + } + + // check for incoming data from all clients + for (byte i = 0; i < 8; i++) { + if (clients[i] && clients[i].available() > 0) { + // read bytes from a client + byte buffer[80]; + int count = clients[i].read(buffer, 80); + // write the bytes to all other connected clients + for (byte j = 0; j < 8; j++) { + if (j != i && clients[j].connected()) { + clients[j].write(buffer, count); + } + } + } + } + + // stop any clients which disconnect + for (byte i = 0; i < 8; i++) { + if (clients[i] && !clients[i].connected()) { + Serial.print("disconnect client #"); + Serial.println(i); + clients[i].stop(); + } + } +} diff --git a/libraries/lwIP_Ethernet/examples/LegacyChatServer/LegacyChatServer.ino b/libraries/lwIP_Ethernet/examples/LegacyChatServer/LegacyChatServer.ino new file mode 100644 index 0000000000..1c6ee9e2a5 --- /dev/null +++ b/libraries/lwIP_Ethernet/examples/LegacyChatServer/LegacyChatServer.ino @@ -0,0 +1,105 @@ +/* + Chat Server + + A simple server that distributes any incoming messages to all + connected clients. To use, telnet to your device's IP address and type. + You can see the client's input in the serial monitor as well. + Using an Arduino Wiznet Ethernet shield. + + Circuit: + * Ethernet Wiznet5500/Wiznet5100/ENC28J60 on esp8266 + + created 18 Dec 2009 + by David A. Mellis + modified 9 Apr 2012 + by Tom Igoe + + */ + + +// specific to esp8266 w/lwIP +#include +ArduinoWiznet5500lwIP Ethernet(/*SS*/ 16); // <== adapt to your hardware +// ArduinoWiznet5100lwIP Ethernet(/*SS*/ 16); // <== adapt to your hardware +// ArduinoENC28J60lwIP Ethernet(/*SS*/ 16); // <== adapt to your hardware + + + +// Enter a MAC address and IP address for your controller below. +// The IP address will be dependent on your local network. +// gateway and subnet are optional: +byte notNeededButAllowed_mac[] = { + 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED +}; +byte* mac = nullptr; // automatic mac +IPAddress ip(192, 168, 1, 177); +IPAddress myDns(192, 168, 1, 1); +IPAddress gateway(192, 168, 1, 1); +IPAddress subnet(255, 255, 0, 0); + + +// telnet defaults to port 23 +EthernetServer server(23); +bool alreadyConnected = false; // whether or not the client was connected previously + +void setup() { + // You can use Ethernet.init(pin) to configure the CS pin + // Ethernet.init(10); // Most Arduino shields + // Ethernet.init(5); // MKR ETH shield + // Ethernet.init(0); // Teensy 2.0 + // Ethernet.init(20); // Teensy++ 2.0 + // Ethernet.init(15); // ESP8266 with Adafruit Featherwing Ethernet + // Ethernet.init(33); // ESP32 with Adafruit Featherwing Ethernet + // // esp8266 w/lwIP: SS set in Ethernet constructor + + // initialize the ethernet device + Ethernet.begin(mac, ip, myDns, gateway, subnet); + + // Open serial communications and wait for port to open: + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + // Check for Ethernet hardware present + if (Ethernet.hardwareStatus() == EthernetNoHardware) { + Serial.println("Ethernet shield was not found. Sorry, can't run without hardware. :("); + while (true) { + delay(1); // do nothing, no point running without Ethernet hardware + } + } + if (Ethernet.linkStatus() == LinkOFF) { + Serial.println("Ethernet cable is not connected."); + } + + // start listening for clients + server.begin(); + + Serial.print("Chat server address:"); + Serial.println(Ethernet.localIP()); +} + +void loop() { + // wait for a new client: + EthernetClient client = server.available(); + + // when the client sends the first byte, say hello: + if (client) { + if (!alreadyConnected) { + // clear out the input buffer: + client.flush(); + Serial.println("We have a new client"); + client.println("Hello, client!"); + alreadyConnected = true; + } + + if (client.available() > 0) { + // read the bytes incoming from the client: + char thisChar = client.read(); + // echo the bytes back to the client: + server.write(thisChar); + // echo the bytes to the server as well: + Serial.write(thisChar); + } + } +} diff --git a/libraries/lwIP_Ethernet/examples/LegacyDhcpAddressPrinter/LegacyDhcpAddressPrinter.ino b/libraries/lwIP_Ethernet/examples/LegacyDhcpAddressPrinter/LegacyDhcpAddressPrinter.ino new file mode 100644 index 0000000000..e7ab75075b --- /dev/null +++ b/libraries/lwIP_Ethernet/examples/LegacyDhcpAddressPrinter/LegacyDhcpAddressPrinter.ino @@ -0,0 +1,101 @@ +/* + DHCP-based IP printer + + This sketch uses the DHCP extensions to the Ethernet library + to get an IP address via DHCP and print the address obtained. + using an Arduino Wiznet Ethernet shield. + + Circuit: + * Ethernet Wiznet5500/Wiznet5100/ENC28J60 on esp8266 + + created 12 April 2011 + modified 9 Apr 2012 + by Tom Igoe + modified 02 Sept 2015 + by Arturo Guadalupi + + */ + +// specific to esp8266 w/lwIP +#include +ArduinoWiznet5500lwIP Ethernet(/*SS*/ 16); // <== adapt to your hardware +// ArduinoWiznet5100lwIP Ethernet(/*SS*/ 16); // <== adapt to your hardware +// ArduinoENC28J60lwIP Ethernet(/*SS*/ 16); // <== adapt to your hardware + + +// Enter a MAC address for your controller below. +// Newer Ethernet shields have a MAC address printed on a sticker on the shield +byte notNeededButAllowed_mac[] = { + 0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x02 +}; +byte* mac = nullptr; // automatic mac + +void setup() { + // You can use Ethernet.init(pin) to configure the CS pin + // Ethernet.init(10); // Most Arduino shields + // Ethernet.init(5); // MKR ETH shield + // Ethernet.init(0); // Teensy 2.0 + // Ethernet.init(20); // Teensy++ 2.0 + // Ethernet.init(15); // ESP8266 with Adafruit Featherwing Ethernet + // Ethernet.init(33); // ESP32 with Adafruit Featherwing Ethernet + // // esp8266 w/lwIP: SS set in Ethernet constructor + + + // Open serial communications and wait for port to open: + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + // start the Ethernet connection: + Serial.println("Initialize Ethernet with DHCP:"); + if (Ethernet.begin(mac) == 0) { + Serial.println("Failed to configure Ethernet using DHCP"); + if (Ethernet.hardwareStatus() == EthernetNoHardware) { + Serial.println("Ethernet shield was not found. Sorry, can't run without hardware. :("); + } else if (Ethernet.linkStatus() == LinkOFF) { + Serial.println("Ethernet cable is not connected."); + } + // no point in carrying on, so do nothing forevermore: + while (true) { + delay(1); + } + } + // print your local IP address: + Serial.print("My IP address: "); + Serial.println(Ethernet.localIP()); +} + +void loop() { + switch (Ethernet.maintain()) { + case 1: + // renewed fail + Serial.println("Error: renewed fail"); + break; + + case 2: + // renewed success + Serial.println("Renewed success"); + // print your local IP address: + Serial.print("My IP address: "); + Serial.println(Ethernet.localIP()); + break; + + case 3: + // rebind fail + Serial.println("Error: rebind fail"); + break; + + case 4: + // rebind success + Serial.println("Rebind success"); + // print your local IP address: + Serial.print("My IP address: "); + Serial.println(Ethernet.localIP()); + break; + + default: + // nothing happened + break; + } +} diff --git a/libraries/lwIP_Ethernet/examples/LegacyUDPSendReceiveString/LegacyUDPSendReceiveString.ino b/libraries/lwIP_Ethernet/examples/LegacyUDPSendReceiveString/LegacyUDPSendReceiveString.ino new file mode 100644 index 0000000000..bee4a1f801 --- /dev/null +++ b/libraries/lwIP_Ethernet/examples/LegacyUDPSendReceiveString/LegacyUDPSendReceiveString.ino @@ -0,0 +1,156 @@ +/* + UDPSendReceiveString: + This sketch receives UDP message strings, prints them to the serial port + and sends an "acknowledge" string back to the sender + + A Processing sketch is included at the end of file that can be used to send + and received messages for testing with a computer. + + Circuit: + * Ethernet Wiznet5500/Wiznet5100/ENC28J60 on esp8266 + + created 21 Aug 2010 + by Michael Margolis + + This code is in the public domain. + */ + +// specific to esp8266 w/lwIP +#include +ArduinoWiznet5500lwIP Ethernet(/*SS*/ 16); // <== adapt to your hardware +// ArduinoWiznet5100lwIP Ethernet(/*SS*/ 16); // <== adapt to your hardware +// ArduinoENC28J60lwIP Ethernet(/*SS*/ 16); // <== adapt to your hardware + +// Enter a MAC address for your controller below. +// Newer Ethernet shields have a MAC address printed on a sticker on the shield +byte notNeededButAllowed_mac[] = { + 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED +}; +byte* mac = nullptr; // automatic mac +IPAddress ip(192, 168, 1, 177); + +unsigned int localPort = 8888; // local port to listen on + +// buffers for receiving and sending data +char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; // buffer to hold incoming packet, +char ReplyBuffer[] = "acknowledged"; // a string to send back + +// An EthernetUDP instance to let us send and receive packets over UDP +EthernetUDP Udp; + +void setup() { + // You can use Ethernet.init(pin) to configure the CS pin + // Ethernet.init(10); // Most Arduino shields + // Ethernet.init(5); // MKR ETH shield + // Ethernet.init(0); // Teensy 2.0 + // Ethernet.init(20); // Teensy++ 2.0 + // Ethernet.init(15); // ESP8266 with Adafruit Featherwing Ethernet + // Ethernet.init(33); // ESP32 with Adafruit Featherwing Ethernet + // // esp8266 w/lwIP: SS set in Ethernet constructor + + // Open serial communications and wait for port to open: + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + // start the Ethernet + // Ethernet.begin(mac, ip); + if (Ethernet.begin(mac) == 0) { + Serial.println("Failed to configure Ethernet using DHCP"); + while (true) { + delay(1000); // do nothing, no point running without Ethernet hardware + } + } + + // Check for Ethernet hardware present + if (Ethernet.hardwareStatus() == EthernetNoHardware) { + Serial.println("Ethernet shield was not found. Sorry, can't run without hardware. :("); + while (true) { + delay(1000); // do nothing, no point running without Ethernet hardware + } + } + if (Ethernet.linkStatus() == LinkOFF) { + Serial.println("Ethernet cable is not connected."); + } + + Serial.println("Started and waiting"); + Serial.print("IP Address: "); + Serial.println(Ethernet.localIP()); + Serial.print("UDP port: "); + Serial.println(localPort); + + // start UDP + Udp.begin(localPort); +} + +void loop() { + // if there's data available, read a packet + int packetSize = Udp.parsePacket(); + if (packetSize) { + Serial.print("Received packet of size "); + Serial.println(packetSize); + Serial.print("From "); + IPAddress remote = Udp.remoteIP(); + for (int i = 0; i < 4; i++) { + Serial.print(remote[i], DEC); + if (i < 3) { + Serial.print("."); + } + } + Serial.print(", port "); + Serial.println(Udp.remotePort()); + + // read the packet into packetBufffer + Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE); + Serial.println("Contents:"); + Serial.println(packetBuffer); + + // send a reply to the IP address and port that sent us the packet we received + Udp.beginPacket(Udp.remoteIP(), Udp.remotePort()); + Udp.write(ReplyBuffer); + Udp.endPacket(); + } + delay(10); +} + + +/* + Processing sketch to run with this example + ===================================================== + + // Processing UDP example to send and receive string data from Arduino + // press any key to send the "Hello Arduino" message + + + import hypermedia.net.*; + + UDP udp; // define the UDP object + + + void setup() { + udp = new UDP( this, 6000 ); // create a new datagram connection on port 6000 + //udp.log( true ); // <-- printout the connection activity + udp.listen( true ); // and wait for incoming message + } + + void draw() + { + } + + void keyPressed() { + String ip = "192.168.1.177"; // the remote IP address + int port = 8888; // the destination port + + udp.send("Hello World", ip, port ); // the message to send + + } + + void receive( byte[] data ) { // <-- default handler + //void receive( byte[] data, String ip, int port ) { // <-- extended handler + + for(int i=0; i < data.length; i++) + print(char(data[i])); + println(); + } + */ diff --git a/libraries/lwIP_Ethernet/library.properties b/libraries/lwIP_Ethernet/library.properties new file mode 100644 index 0000000000..0c38f07ef0 --- /dev/null +++ b/libraries/lwIP_Ethernet/library.properties @@ -0,0 +1,10 @@ +name=lwIP_Ethernet +version=1 +author=esp8266/Arduino +maintainer=esp8266/Arduino +sentence=Helper for ethernet drivers +paragraph=Example repository for Ethernet drivers +category=Communication +url=https://github.com/esp8266/Arduino +architectures=esp8266 +dot_a_linkage=true diff --git a/libraries/lwIP_Ethernet/src/EthernetCompat.h b/libraries/lwIP_Ethernet/src/EthernetCompat.h new file mode 100644 index 0000000000..9ebd75ca15 --- /dev/null +++ b/libraries/lwIP_Ethernet/src/EthernetCompat.h @@ -0,0 +1,127 @@ + +#pragma once + +#include +#include +#include +#include + +using EthernetUDP = WiFiUDP; +using EthernetClient = WiFiClient; +using EthernetServer = ArduinoWiFiServer; + +enum +{ + DHCP_CHECK_NONE = 0, + DHCP_CHECK_RENEW_FAIL = 1, + DHCP_CHECK_RENEW_OK = 2, + DHCP_CHECK_REBIND_FAIL = 3, + DHCP_CHECK_REBIND_OK = 4, +}; + +enum HardwareStatus +{ + EthernetNoHardware, + EthernetHardwareFound, +}; + +template +class ArduinoEthernet: public LwipIntfDev +{ +public: + ArduinoEthernet(int8_t cs = SS, SPIClass& spi = SPI, int8_t intr = -1) : + LwipIntfDev(cs, spi, intr) + { + _hardwareStatus = EthernetNoHardware; + } + + // Arduino-Ethernet API compatibility, order can be either: + // mac, ip, gateway, netmask, dns (esp8266 or natural order) + // mac, ip, dns, gateway, netmask (Arduino legacy) + boolean begin(const uint8_t* macAddress, IPAddress local_ip = IPADDR_NONE, + IPAddress arg1 = IPADDR_NONE, IPAddress arg2 = IPADDR_NONE, + IPAddress arg3 = IPADDR_NONE) + { + if (local_ip.isSet() && local_ip.isV4()) + { + // setting auto values using arduino ordering of parameters + if (arg1 == IPADDR_NONE) // else dns or gw + { + arg1 = local_ip; + arg1[3] = 1; + } + if (arg2 == IPADDR_NONE) // else gw or mask + { + arg2 = local_ip; + arg2[3] = 1; + } + // if arg2 is mask (esp ordering), let DNS IP unconfigured + if (arg3 == IPADDR_NONE && arg2[0] != 255) // else mask or dns + { + arg3 = IPAddress(255, 255, 255, 0); + } + } + SPI4EthInit(); // Arduino Ethernet self-initializes SPI + bool ret = true; + if (local_ip.isSet()) + ret = LwipIntfDev::config(local_ip, arg1, arg2, arg3); + if (ret) + { + ret = LwipIntfDev::begin(macAddress); + if (!local_ip.isSet()) + { + // Arduino API waits for DHCP answer + while (!LwipIntfDev::connected()) + { + delay(100); + } + } + } + + if (ret) + { + _hardwareStatus = EthernetHardwareFound; + } + + return ret; + } + + void end() + { + ip_addr_copy(LwipIntfDev::_netif.ip_addr, + ip_addr_any); // to allow DHCP at next begin + LwipIntfDev::end(); + } + + HardwareStatus hardwareStatus() const + { + return _hardwareStatus; + } + + int maintain() const + { + 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; +}; + +using ArduinoWiznet5500lwIP = ArduinoEthernet; +using ArduinoWiznet5100lwIP = ArduinoEthernet; +using ArduinoENC28J60lwIP = ArduinoEthernet; diff --git a/libraries/lwIP_Ethernet/src/LwipEthernet.cpp b/libraries/lwIP_Ethernet/src/LwipEthernet.cpp new file mode 100644 index 0000000000..1db0ba1335 --- /dev/null +++ b/libraries/lwIP_Ethernet/src/LwipEthernet.cpp @@ -0,0 +1,15 @@ + +#include +#include + +#ifndef ETHERNET_SPI_CLOCK_DIV +#define ETHERNET_SPI_CLOCK_DIV SPI_CLOCK_DIV4 // 4MHz (SPI.h) +#endif + +void SPI4EthInit() +{ + SPI.begin(); + SPI.setClockDivider(ETHERNET_SPI_CLOCK_DIV); + SPI.setBitOrder(MSBFIRST); + SPI.setDataMode(SPI_MODE0); +} diff --git a/libraries/lwIP_Ethernet/src/LwipEthernet.h b/libraries/lwIP_Ethernet/src/LwipEthernet.h new file mode 100644 index 0000000000..855465bfba --- /dev/null +++ b/libraries/lwIP_Ethernet/src/LwipEthernet.h @@ -0,0 +1,53 @@ + +#include // tcp API +#include + +#include +#include +#include + +// One of them is to be declared in the main sketch +// and passed to ethInitDHCP() or ethInitStatic(): +// Wiznet5500lwIP eth(CSPIN); +// Wiznet5100lwIP eth(CSPIN); +// ENC28J60lwIP eth(CSPIN); + +void SPI4EthInit(); + +template +bool ethInitDHCP(EthImpl& eth) +{ + SPI4EthInit(); + + if (!eth.begin()) + { + // hardware not responding + DEBUGV("ethInitDHCP: hardware not responding\n"); + return false; + } + + return true; +} + +template +bool ethInitStatic(EthImpl& eth, IPAddress IP, IPAddress gateway, IPAddress netmask, IPAddress dns1, + IPAddress dns2 = IPADDR_NONE) +{ + SPI4EthInit(); + + if (!eth.config(IP, gateway, netmask, dns1, dns2)) + { + // invalid arguments + DEBUGV("ethInitStatic: invalid arguments\n"); + return false; + } + + if (!eth.begin()) + { + // hardware not responding + DEBUGV("ethInitStatic: hardware not responding\n"); + return false; + } + + return true; +} diff --git a/libraries/lwIP_PPP/examples/PPPServer/PPPServer.ino b/libraries/lwIP_PPP/examples/PPPServer/PPPServer.ino index f5e93eeb4b..da15844ff3 100644 --- a/libraries/lwIP_PPP/examples/PPPServer/PPPServer.ino +++ b/libraries/lwIP_PPP/examples/PPPServer/PPPServer.ino @@ -19,9 +19,7 @@ #include #include #include -#include #include -#include #ifndef STASSID #define STASSID "your-ssid" @@ -37,8 +35,8 @@ #define RX 13 // d1mini D7 #define TX 15 // d1mini D8 -SoftwareSerial ppplink(RX, TX); -HardwareSerial& logger = Serial; +HardwareSerial& ppplink = Serial; +HardwareSerial& logger = Serial1; PPPServer ppp(&ppplink); void PPPConnectedCallback(netif* nif) { @@ -75,7 +73,8 @@ void setup() { logger.printf("\nSTA: %s (dns: %s / %s)\n", WiFi.localIP().toString().c_str(), WiFi.dnsIP(0).toString().c_str(), WiFi.dnsIP(1).toString().c_str()); ppplink.begin(PPPLINKBAUD); - ppplink.enableIntTx(true); + ppplink.swap(); // RX=GPIO13 TX=GPIO15 + logger.println(); logger.printf("\n\nhey, trying to be a PPP server here\n\n"); logger.printf("Now try this on your linux host:\n\n"); diff --git a/libraries/lwIP_enc28j60/src/utility/enc28j60.cpp b/libraries/lwIP_enc28j60/src/utility/enc28j60.cpp index 979d88e9ea..0ee3096486 100644 --- a/libraries/lwIP_enc28j60/src/utility/enc28j60.cpp +++ b/libraries/lwIP_enc28j60/src/utility/enc28j60.cpp @@ -106,6 +106,7 @@ void serial_printf(const char* fmt, ...) #define MACONX_BANK 0x02 #define MACON1 0x00 +#define MACSTAT1 0x01 #define MACON3 0x02 #define MACON4 0x03 #define MABBIPG 0x04 @@ -113,6 +114,16 @@ void serial_printf(const char* fmt, ...) #define MAIPGH 0x07 #define MAMXFLL 0x0a #define MAMXFLH 0x0b +#define MACON2 0x10 +#define MACSTAT2 0x11 +#define MICMD 0x12 +#define MIREGADR 0x14 +#define MIRDL 0x18 +#define MIRDH 0x19 + +/* MICMD Register Bit Definitions */ +#define MICMD_MIISCAN 0x02 +#define MICMD_MIIRD 0x01 #define MACON1_TXPAUS 0x08 #define MACON1_RXPAUS 0x04 @@ -135,6 +146,9 @@ void serial_printf(const char* fmt, ...) #define MISTAT 0x0a #define EREVID 0x12 +/* MISTAT Register Bit Definitions */ +#define MISTAT_BUSY 0x01 + #define EPKTCNT_BANK 0x01 #define ERXFCON 0x18 #define EPKTCNT 0x19 @@ -384,7 +398,9 @@ bool ENC28J60::reset(void) /* Wait for OST */ PRINTF("waiting for ESTAT_CLKRDY\n"); - while ((readreg(ESTAT) & ESTAT_CLKRDY) == 0) { }; + while ((readreg(ESTAT) & ESTAT_CLKRDY) == 0) + { + }; PRINTF("ESTAT_CLKRDY\n"); setregbank(ERXTX_BANK); @@ -720,3 +736,26 @@ uint16_t ENC28J60::readFrameData(uint8_t* buffer, uint16_t framesize) return _len; } + +uint16_t ENC28J60::phyread(uint8_t reg) +{ + // ( https://github.com/JAndrassy/EthernetENC/tree/master/src/utility/enc28j60.h ) + + setregbank(MACONX_BANK); + writereg(MIREGADR, reg); + writereg(MICMD, MICMD_MIIRD); + // wait until the PHY read completes + while (readreg(MISTAT) & MISTAT_BUSY) + { + delayMicroseconds(15); + } + writereg(MICMD, 0); + return (readreg(MIRDL) | readreg(MIRDH) << 8); +} + +bool ENC28J60::isLinked() +{ + // ( https://github.com/JAndrassy/EthernetENC/tree/master/src/utility/enc28j60.h ) + + return !!(phyread(MACSTAT2) & 0x400); +} diff --git a/libraries/lwIP_enc28j60/src/utility/enc28j60.h b/libraries/lwIP_enc28j60/src/utility/enc28j60.h index 07f71f96ff..e6c65d7759 100644 --- a/libraries/lwIP_enc28j60/src/utility/enc28j60.h +++ b/libraries/lwIP_enc28j60/src/utility/enc28j60.h @@ -79,6 +79,21 @@ class ENC28J60 */ virtual uint16_t readFrame(uint8_t* buffer, uint16_t bufsize); + /** + Check physical link + @return true when physical link is up + */ + bool isLinked(); + + /** + Report whether ::isLinked() API is implemented + @return true when ::isLinked() API is implemented + */ + constexpr bool isLinkDetectable() const + { + return true; + } + protected: static constexpr bool interruptIsPossible() { @@ -133,6 +148,8 @@ class ENC28J60 // Previously defined in contiki/core/sys/clock.h void clock_delay_usec(uint16_t dt); + uint16_t phyread(uint8_t reg); + uint8_t _bank; int8_t _cs; SPIClass& _spi; diff --git a/libraries/lwIP_w5100/library.properties b/libraries/lwIP_w5100/library.properties index f0296a9265..b74c5ba56e 100644 --- a/libraries/lwIP_w5100/library.properties +++ b/libraries/lwIP_w5100/library.properties @@ -1,4 +1,4 @@ -name=lwIP_w5500 +name=lwIP_w5100 version=1 author=Nicholas Humfrey maintainer=esp8266/Arduino diff --git a/libraries/lwIP_w5100/src/utility/w5100.h b/libraries/lwIP_w5100/src/utility/w5100.h index 6e1f2c35cd..9ed1e4cdab 100644 --- a/libraries/lwIP_w5100/src/utility/w5100.h +++ b/libraries/lwIP_w5100/src/utility/w5100.h @@ -79,6 +79,24 @@ class Wiznet5100 */ uint16_t readFrame(uint8_t* buffer, uint16_t bufsize); + /** + Check physical link + @return true when physical link is up + */ + bool isLinked() const + { + return true; //XXX TODO + } + + /** + Report whether ::isLinked() API is implemented + @return true when ::isLinked() API is implemented + */ + constexpr bool isLinkDetectable() const + { + return false; + } + protected: static constexpr bool interruptIsPossible() { diff --git a/libraries/lwIP_w5500/examples/TCPClient/TCPClient.ino b/libraries/lwIP_w5500/examples/TCPClient/TCPClient.ino deleted file mode 100644 index f2ba967592..0000000000 --- a/libraries/lwIP_w5500/examples/TCPClient/TCPClient.ino +++ /dev/null @@ -1,91 +0,0 @@ -/* - This sketch establishes a TCP connection to a "quote of the day" service. - It sends a "hello" message, and then prints received data. -*/ - -#include -#include -// or #include -// or #include - -#include // WiFiClient (-> TCPClient) - -const char* host = "djxmmx.net"; -const uint16_t port = 17; - -using TCPClient = WiFiClient; - -#define CSPIN 16 // wemos/lolin/nodemcu D0 -Wiznet5500lwIP eth(CSPIN); - -void setup() { - Serial.begin(115200); - - SPI.begin(); - SPI.setClockDivider(SPI_CLOCK_DIV4); // 4 MHz? - SPI.setBitOrder(MSBFIRST); - SPI.setDataMode(SPI_MODE0); - eth.setDefault(); // use ethernet for default route - if (!eth.begin()) { - Serial.println("ethernet hardware not found ... sleeping"); - while (1) { delay(1000); } - } else { - Serial.print("connecting ethernet"); - while (!eth.connected()) { - Serial.print("."); - delay(1000); - } - } - Serial.println(); - Serial.print("ethernet IP address: "); - Serial.println(eth.localIP()); -} - -void loop() { - static bool wait = false; - - Serial.print("connecting to "); - Serial.print(host); - Serial.print(':'); - Serial.println(port); - - TCPClient client; - if (!client.connect(host, port)) { - Serial.println("connection failed"); - delay(5000); - return; - } - - // This will send a string to the server - Serial.println("sending data to server"); - if (client.connected()) { client.println("hello from ESP8266"); } - - // wait for data to be available - unsigned long timeout = millis(); - while (client.available() == 0) { - if (millis() - timeout > 5000) { - Serial.println(">>> Client Timeout !"); - client.stop(); - delay(60000); - return; - } - } - - // Read all the lines of the reply from server and print them to Serial - Serial.println("receiving from remote server"); - // not testing 'client.connected()' since we do not need to send data here - while (client.available()) { - char ch = static_cast(client.read()); - Serial.print(ch); - } - - // Close the connection - Serial.println(); - Serial.println("closing connection"); - client.stop(); - - if (wait) { - delay(300000); // execute once every 5 minutes, don't flood remote service - } - wait = true; -} diff --git a/libraries/lwIP_w5500/src/utility/w5500.h b/libraries/lwIP_w5500/src/utility/w5500.h index f7babb51e9..7e8058fdb1 100644 --- a/libraries/lwIP_w5500/src/utility/w5500.h +++ b/libraries/lwIP_w5500/src/utility/w5500.h @@ -79,6 +79,24 @@ class Wiznet5500 */ uint16_t readFrame(uint8_t* buffer, uint16_t bufsize); + /** + Check physical link + @return true when physical link is up + */ + bool isLinked() + { + return wizphy_getphylink() == PHY_LINK_ON; + } + + /** + Report whether ::isLinked() API is implemented + @return true when ::isLinked() API is implemented + */ + constexpr bool isLinkDetectable() const + { + return true; + } + protected: static constexpr bool interruptIsPossible() { diff --git a/package.json b/package.json index cec683a941..d85d07b684 100644 --- a/package.json +++ b/package.json @@ -2,5 +2,5 @@ "name": "framework-arduinoespressif8266", "description": "Arduino Wiring-based Framework (ESP8266 Core)", "url": "https://github.com/esp8266/Arduino", - "version": "3.1.0-dev" + "version": "3.2.0-dev" } diff --git a/package/README.md b/package/README.md index dfad8f5d9d..b2d64ef76e 100644 --- a/package/README.md +++ b/package/README.md @@ -73,7 +73,7 @@ Here is a rough overview of the effective release process. See the section below * Since most changes are integrated into master using squash-rebase policy (i.e. one commit per PR), `git log --oneline` gives a good overview of changes in the release. - * Prepare release notes in Markdown format. + * Prepare release notes in Markdown format. Either use the `git log --oneline` output and sort through it manually, or use Github draft release and press 'Generate release notes' (see https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes) * For changes that are breaking, duplicate those changes and put the duplicate lines into a separate group called Breaking Changes. That group should go at the top of the Changelog. The original lines for the breaking changes should be marked by appending "(Breaking change)" to the line. Example: @@ -99,11 +99,11 @@ Here is a rough overview of the effective release process. See the section below - Documentation - Boards - * Not all commit descriptions which come from `git log` will explain changes well. Reword items as necessary, with the goal that a general user of this project should be able to understand what the change is related to. Preserve references to PRs or issues (`#XXXX`). + * Not all commit descriptions which come from `git log` or PR titles will explain changes well. Reword items as necessary, with the goal that a general user of this project should be able to understand what the change is related to. Preserve references to PRs or issues (`#XXXX`). * Aggregate minor fixes (e.g. typos, small documentation changes) in a few items. Focus on preparing a good overview of the release for the users, rather than mentioning every change. - * When done, put release notes into a private [Gist](https://gist.github.com) or [firepad](https://demo.firepad.io) and send the link to other maintainers for review. + * When done, put release notes into a private [Gist](https://gist.github.com) or [HedgeDoc note](https://hedgedoc.org/) and send the link to other maintainers for review. The following points assume work in a direct clone of the repository, and not in a personal fork. @@ -111,8 +111,8 @@ The following points assume work in a direct clone of the repository, and not in * [platform.txt](https://github.com/esp8266/Arduino/blob/master/platform.txt) and [package.json](https://github.com/esp8266/Arduino/blob/master/package.json): update `version` to the release E.g. `3.0.0`, - * [cores/esp8266/TZ.h](https://github.com/esp8266/Arduino/blob/master/cores/esp8266/TZ.h): import the latest database with the following shell command:\ - `$ cd tools; sh TZupdate.sh` + * [cores/esp8266/TZ.h](https://github.com/esp8266/Arduino/blob/master/cores/esp8266/TZ.h): import the latest database:\ + `$ pip install -U tzdata; python tools/format_tzdata.py --output cores/esp8266/TZ.h` * Update SSL/TLS certificates and public keys in examples:\ `$ cd tools; sh certsUpdate.sh` @@ -141,13 +141,13 @@ The following points assume work in a direct clone of the repository, and not in 8. Check that the new (draft) release has been created (no editing at this point!), see https://github.com/esp8266/Arduino/releases. -9. Check that the boards manager package .zip file has been successfully uploaded as a release artifact. +9. Check that the boards manager package .zip file has been successfully uploaded as a release asset. 10. Check that the package index downloaded from https://arduino.esp8266.com/stable/package_esp8266com_index.json contains an entry for the new version (it may not be the first one). 11. Navigate to release list in Github here https://github.com/esp8266/Arduino/releases, press "Edit" button to edit release description, paste release notes, and publish it. -12. In the issue tracker, remove "staged-for-release" label for all issues which have it, and close them. Close the milestone associated with the released version (the milestone should be empty per point 2 above) +12. Close the milestone associated with the released version (the milestone should be empty per point 1 above) 13. Check that https://arduino-esp8266.readthedocs.io/en/latest/ has a new doc build for the new tag, and that "stable" points to that build. If a new build did not trigger, log into readthedoc's home here https://readthedocs.org/ (account must have been added to project as maintainer) and trigger it manually. @@ -173,7 +173,7 @@ The following points assume work in a direct clone of the repository, and not in * [platform.txt](https://github.com/esp8266/Arduino/blob/master/platform.txt) * [package.json](https://github.com/esp8266/Arduino/blob/master/package.json) - * [TZ.h](https://github.com/esp8266/Arduino/blob/master/cores/esp8266/TZ.h) (<= `cd tools; sh ./TZupdate.sh`) + * [TZ.h](https://github.com/esp8266/Arduino/blob/master/cores/esp8266/TZ.h) (<= `pip install -U tzdata; python tools/format_tzdata.py --output cores/esp8266/TZ.h`) * Certificates (<= `cd tools; sh certsUpdate.sh`) - [ ] 5. Wait until the release notes have been checked by other maintainers (can be changed afterwards anyway) @@ -186,13 +186,13 @@ The following points assume work in a direct clone of the repository, and not in - [ ] 8. Check that the new (draft) release has been created (no editing at this point!), see https://github.com/esp8266/Arduino/releases. -- [ ] 9. Check that the boards manager package .zip file has been successfully uploaded as a release artifact. +- [ ] 9. Check that the boards manager package .zip file has been successfully uploaded as a release asset. - [ ] 10. Check that the package index downloaded from https://arduino.esp8266.com/stable/package_esp8266com_index.json contains an entry for the new version (it may not be the first one). - [ ] 11. Navigate to [release list in Github](https://github.com/esp8266/Arduino/releases), press "Edit" button to edit release description, paste release notes, and publish it. -- [ ] 12. In the issue tracker, remove "staged-for-release" label for all issues which have it, and close them. Close the milestone associated with the released version (the milestone should be empty per point 1 above) +- [ ] 12. Close the milestone associated with the released version (the milestone should be empty per point 1 above) - [ ] 13. Check that https://arduino-esp8266.readthedocs.io/en/latest/ has a new doc build for the new tag, and that "stable" points to that build. If a new build did not trigger, log into readthedoc's home here https://readthedocs.org/ (account must have been added to project as maintainer) and trigger it manually. @@ -202,3 +202,35 @@ The following points assume work in a direct clone of the repository, and not in * In main README.md go to "Latest release" section, change version number in the readthedocs link to the version which was just released, and verify that all links work. --------------COPY ABOVE THIS LINE-------------- ``` + +## Updating a SSH deploy key + +A SSH private/public key pair is required to update the master JSON (the final step of the release process). Sometimes GitHub will expire one side or the other of that key, and a new one will need to be regenerated and installed in the https://github.com/esp8266/esp8266.github.io (JSON) and https://github.com/esp8266/Arduino (core) repos. + +1. Generate a new public/private SSH key pair with an empty passphrase: +```console +$ ssh-keygen -f deploy_key -t ed25519 -N '' -C earlephilhower@yahoo.com (**replace with your GH user account email**) +Generating public/private ed25519 key pair. +Your identification has been saved in deploy_key +Your public key has been saved in deploy_key.pub +The key fingerprint is: +... +``` + +2. Copy the contents of `deploy_key.pub` to the clipboard: +```console +$ cat deploy_key.pub +ssh-ed25519 AAA..... earlephilhower@yahoo.com +``` + +3. Install the deploy key for esp8266.github.io repository. Go to https://github.com/esp8266/esp8266.github.io and the `Settings->Deploy Keys` and `Add deploy key`. Paste the (public key) string into the box and select `Allow writes` and hit OK. + +4. Convert the `deploy_key` private key to a 1-line base64 representation and copy it to the clipboard. +```console +$ base64 -w 0 < deploy_key && echo "" +yEvYm..... (**note this must be one single long line, hence the "-w 0"**) +``` + +5. Install the private key to the Core repo. Go to https://github.com/esp8266/Arduino and select `Settings->Secrets->Actions` and add or update a `Repository secret` called `GHCI_DEPLOY_KEY`. Paste the 1-line base64 contents of your clipboard to the box and hit OK. + +6. If the release failed in the `Update master JSON file` action, from the GitHub web interface run the `Actions->Release XXX->Re-run failed jobs` to re-run it and check its output. diff --git a/package/build_boards_manager_package.sh b/package/build_boards_manager_package.sh index e5d4e58aa0..40050f4437 100755 --- a/package/build_boards_manager_package.sh +++ b/package/build_boards_manager_package.sh @@ -1,6 +1,5 @@ #!/bin/bash - if [ ! -z "${manualversion}" ]; then # manual-made release based on $manualversion @@ -72,13 +71,16 @@ mkdir -p ${outdir} # Some files should be excluded from the package cat << EOF > exclude.txt .git +.git-blame-ignore-revs +.github .gitignore .gitmodules -.travis.yml -package +ISSUE_TEMPLATE.md doc +package EOF # Also include all files which are ignored by git +# TODO: .gitattributes helper for the above? git ls-files --other --directory >> exclude.txt # Now copy files to $outdir rsync -a --exclude-from 'exclude.txt' ${srcdir}/ ${outdir}/ diff --git a/package/package_esp8266com_index.template.json b/package/package_esp8266com_index.template.json index 7acbccae9d..3c8d1bed39 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 1.0" + }, { "name": "Phoenix 1.0" }, @@ -77,6 +80,9 @@ { "name": "LOLIN(WEMOS) D1 R2 & mini" }, + { + "name": "LOLIN(WEMOS) D1 ESP-WROOM-02" + }, { "name": "LOLIN(WEMOS) D1 mini (clone)" }, @@ -132,22 +138,22 @@ "toolsDependencies": [ { "packager": "esp8266", - "version": "3.0.5-gcc10.3-aff7f1b", + "version": "3.2.0-gcc10.3-c791b74", "name": "xtensa-lx106-elf-gcc" }, { "packager": "esp8266", - "version": "3.0.5-gcc10.3-aff7f1b", + "version": "3.2.0-gcc10.3-c791b74", "name": "mkspiffs" }, { "packager": "esp8266", - "version": "3.0.5-gcc10.3-aff7f1b", + "version": "3.2.0-gcc10.3-c791b74", "name": "mklittlefs" }, { "packager": "esp8266", - "version": "3.7.2-post1", + "version": "3.7.2-post2", "name": "python3" } ], @@ -158,222 +164,222 @@ ], "tools": [ { - "version": "3.7.2-post1", + "version": "3.7.2-post2", "name": "python3", "systems": [ { "host": "x86_64-mingw32", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/python3-3.7.2.post1-embed-win32v2a.zip", + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/python3-3.7.2.post1-embed-win32v2a.zip", "archiveFileName": "python3-3.7.2.post1-embed-win32v2a.zip", "checksum": "SHA-256:f57cb2daf86176d2929e7c58990c2ac32554e3219d454dcac10e464ddda35bf2", "size": "6428926" }, { "host": "i686-mingw32", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/python3-3.7.2.post1-embed-win32v2a.zip", + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/python3-3.7.2.post1-embed-win32v2a.zip", "archiveFileName": "python3-3.7.2.post1-embed-win32v2a.zip", "checksum": "SHA-256:f57cb2daf86176d2929e7c58990c2ac32554e3219d454dcac10e464ddda35bf2", "size": "6428926" }, { "host": "aarch64-linux-gnu", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/python3-via-env.tar.gz", + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/python3-via-env.tar.gz", "archiveFileName": "python3-via-env.tar.gz", - "checksum": "SHA-256:c9237bfe0f62842d7187a39495baa4a7e3ab8b87c0b433614294b023cf0bc0f3", - "size": "292" + "checksum": "SHA-256:a7a9905887703a0c862356918b7a9b9ca6968a696d53a15a7cdb7f1655cecf3f", + "size": "331" }, { "host": "arm-linux-gnueabihf", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/python3-via-env.tar.gz", + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/python3-via-env.tar.gz", "archiveFileName": "python3-via-env.tar.gz", - "checksum": "SHA-256:c9237bfe0f62842d7187a39495baa4a7e3ab8b87c0b433614294b023cf0bc0f3", - "size": "292" + "checksum": "SHA-256:a7a9905887703a0c862356918b7a9b9ca6968a696d53a15a7cdb7f1655cecf3f", + "size": "331" }, { "host": "i686-pc-linux-gnu", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/python3-via-env.tar.gz", + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/python3-via-env.tar.gz", "archiveFileName": "python3-via-env.tar.gz", - "checksum": "SHA-256:c9237bfe0f62842d7187a39495baa4a7e3ab8b87c0b433614294b023cf0bc0f3", - "size": "292" + "checksum": "SHA-256:a7a9905887703a0c862356918b7a9b9ca6968a696d53a15a7cdb7f1655cecf3f", + "size": "331" }, { "host": "x86_64-apple-darwin", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/python3-macosx-portable.tar.gz", + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/python3-macosx-portable.tar.gz", "archiveFileName": "python3-macosx-portable.tar.gz", "checksum": "SHA-256:01a5bf1fa264c6f04cfaadf4c6e9f6caaacb6833ef40104dfbe953fcdb9bca1c", "size": "25494144" }, { "host": "x86_64-pc-linux-gnu", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/python3-via-env.tar.gz", + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/python3-via-env.tar.gz", "archiveFileName": "python3-via-env.tar.gz", - "checksum": "SHA-256:c9237bfe0f62842d7187a39495baa4a7e3ab8b87c0b433614294b023cf0bc0f3", - "size": "292" + "checksum": "SHA-256:a7a9905887703a0c862356918b7a9b9ca6968a696d53a15a7cdb7f1655cecf3f", + "size": "331" } ] }, { - "version": "3.0.5-gcc10.3-aff7f1b", + "version": "3.2.0-gcc10.3-c791b74", "name": "xtensa-lx106-elf-gcc", "systems": [ { "host": "aarch64-linux-gnu", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.5-gcc10.3/aarch64-linux-gnu.xtensa-lx106-elf-aff7f1b.211127.tar.gz", - "archiveFileName": "aarch64-linux-gnu.xtensa-lx106-elf-aff7f1b.211127.tar.gz", - "checksum": "SHA-256:5a8c1feac38153c552cf65490f6620f9d8ead142dd175b490ca33fea253cd338", - "size": "71331616" + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/aarch64-linux-gnu.xtensa-lx106-elf-c791b74.230224.tar.gz", + "archiveFileName": "aarch64-linux-gnu.xtensa-lx106-elf-c791b74.230224.tar.gz", + "checksum": "SHA-256:c8833744f1419eed60a3b7bc0e06b72eb94e4ab2cb13daa1a011d5e5663ea04f", + "size": "72905094" }, { "host": "arm-linux-gnueabihf", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.5-gcc10.3/arm-linux-gnueabihf.xtensa-lx106-elf-aff7f1b.211127.tar.gz", - "archiveFileName": "arm-linux-gnueabihf.xtensa-lx106-elf-aff7f1b.211127.tar.gz", - "checksum": "SHA-256:da09017926db49001993fe524dfe2b54256dbb6c096c45be22d41c0956383dbc", - "size": "67424242" + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/arm-linux-gnueabihf.xtensa-lx106-elf-c791b74.230224.tar.gz", + "archiveFileName": "arm-linux-gnueabihf.xtensa-lx106-elf-c791b74.230224.tar.gz", + "checksum": "SHA-256:d378d91c63200d4007d1af9e0f4f622b60f5d1c67dd81e63e3c20ddfb14bc3d0", + "size": "68111989" }, { "host": "i686-pc-linux-gnu", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.5-gcc10.3/i686-linux-gnu.xtensa-lx106-elf-aff7f1b.211127.tar.gz", - "archiveFileName": "i686-linux-gnu.xtensa-lx106-elf-aff7f1b.211127.tar.gz", - "checksum": "SHA-256:24506fac6befb7c9c7af9c3df5e3a2d3aa29cc524d5c99ac5ed45b4ce742ffcb", - "size": "74508325" + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/i686-linux-gnu.xtensa-lx106-elf-c791b74.230224.tar.gz", + "archiveFileName": "i686-linux-gnu.xtensa-lx106-elf-c791b74.230224.tar.gz", + "checksum": "SHA-256:26fc73e2047c6e1d563db5ba56318b0e099cec5824d17744aac6f9031f104802", + "size": "76912811" }, { "host": "i686-mingw32", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.5-gcc10.3/i686-w64-mingw32.xtensa-lx106-elf-aff7f1b.211127.zip", - "archiveFileName": "i686-w64-mingw32.xtensa-lx106-elf-aff7f1b.211127.zip", - "checksum": "SHA-256:c9c416ac5f12bd689eeda4cbfd853f8e71a0882e4fc3e4181846a316b02f9e15", - "size": "71730017" + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/i686-w64-mingw32.xtensa-lx106-elf-c791b74.230224.zip", + "archiveFileName": "i686-w64-mingw32.xtensa-lx106-elf-c791b74.230224.zip", + "checksum": "SHA-256:0b4199eff915f33498413c8a852038f6c50bb87adf22579920fa4972a2cc15f0", + "size": "73750173" }, { "host": "x86_64-apple-darwin", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.5-gcc10.3/x86_64-apple-darwin14.xtensa-lx106-elf-aff7f1b.211127.tar.gz", - "archiveFileName": "x86_64-apple-darwin14.xtensa-lx106-elf-aff7f1b.211127.tar.gz", - "checksum": "SHA-256:0e227a672d8e02ead8f3bb257482747b129e6bcd8ba7356f377627722d7c1d26", - "size": "75920517" + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/x86_64-apple-darwin14.xtensa-lx106-elf-c791b74.230224.tar.gz", + "archiveFileName": "x86_64-apple-darwin14.xtensa-lx106-elf-c791b74.230224.tar.gz", + "checksum": "SHA-256:59c9890ac51cfdd687e072e310f86e3aa2da549a02fa4d1dcda7f9bc2dffb0fe", + "size": "77742495" }, { "host": "x86_64-pc-linux-gnu", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.5-gcc10.3/x86_64-linux-gnu.xtensa-lx106-elf-aff7f1b.211127.tar.gz", - "archiveFileName": "x86_64-linux-gnu.xtensa-lx106-elf-aff7f1b.211127.tar.gz", - "checksum": "SHA-256:f79f9ff34e56547380bc0d26e1384117920d55110bc83a4c69dd7f7bc6be4191", - "size": "75025973" + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/x86_64-linux-gnu.xtensa-lx106-elf-c791b74.230224.tar.gz", + "archiveFileName": "x86_64-linux-gnu.xtensa-lx106-elf-c791b74.230224.tar.gz", + "checksum": "SHA-256:8ddcb9935dfdc88f9742bc3319c6dc01eb17618a785bb3474f0e52f00adf49cc", + "size": "76312800" }, { "host": "x86_64-mingw32", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.5-gcc10.3/x86_64-w64-mingw32.xtensa-lx106-elf-aff7f1b.211127.zip", - "archiveFileName": "x86_64-w64-mingw32.xtensa-lx106-elf-aff7f1b.211127.zip", - "checksum": "SHA-256:26139f6180819ffd62ab45e3c8bc12353b7212e57328dba81c0e43dee59d932b", - "size": "75703204" + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/x86_64-w64-mingw32.xtensa-lx106-elf-c791b74.230224.zip", + "archiveFileName": "x86_64-w64-mingw32.xtensa-lx106-elf-c791b74.230224.zip", + "checksum": "SHA-256:41f0198b25a99aeeb410d5b978453295a46e2d844a60d5a9d245590a095e4ce4", + "size": "76928412" } ] }, { - "version": "3.0.5-gcc10.3-aff7f1b", + "version": "3.2.0-gcc10.3-c791b74", "name": "mkspiffs", "systems": [ { "host": "aarch64-linux-gnu", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.5-gcc10.3/aarch64-linux-gnu.mkspiffs-7fefeac.211127.tar.gz", - "archiveFileName": "aarch64-linux-gnu.mkspiffs-7fefeac.211127.tar.gz", - "checksum": "SHA-256:515c0f2d05a69920b31d5d3e31b4eca0507dab95016128e5c76a51fb543ee2b9", - "size": "51253" + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/aarch64-linux-gnu.mkspiffs-7fefeac.230224.tar.gz", + "archiveFileName": "aarch64-linux-gnu.mkspiffs-7fefeac.230224.tar.gz", + "checksum": "SHA-256:035d881e771d9024f9864e86112496d5b4a3d2d4adc4bb8b9d867ae5682f0b2b", + "size": "65628" }, { "host": "arm-linux-gnueabihf", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.5-gcc10.3/arm-linux-gnueabihf.mkspiffs-7fefeac.211127.tar.gz", - "archiveFileName": "arm-linux-gnueabihf.mkspiffs-7fefeac.211127.tar.gz", - "checksum": "SHA-256:36028127bc615cf5e63e83101f9609a28d3a98e45d44fb8b8d5dd5a7bd2f3e77", - "size": "44269" + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/arm-linux-gnueabihf.mkspiffs-7fefeac.230224.tar.gz", + "archiveFileName": "arm-linux-gnueabihf.mkspiffs-7fefeac.230224.tar.gz", + "checksum": "SHA-256:1fcc4997a0d10857c5df5702254f103a82ccad180566754f6545e9c58707856c", + "size": "56970" }, { "host": "i686-pc-linux-gnu", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.5-gcc10.3/i686-linux-gnu.mkspiffs-7fefeac.211127.tar.gz", - "archiveFileName": "i686-linux-gnu.mkspiffs-7fefeac.211127.tar.gz", - "checksum": "SHA-256:57a03875802c8ba74368389ae204ce9e3230d2de4720bd53e2cc3dbf2e0c538e", - "size": "54516" + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/i686-linux-gnu.mkspiffs-7fefeac.230224.tar.gz", + "archiveFileName": "i686-linux-gnu.mkspiffs-7fefeac.230224.tar.gz", + "checksum": "SHA-256:be5c656b971b842d4041562aefecf305842b91c3d812e9c1265006958f8ef033", + "size": "72646" }, { "host": "i686-mingw32", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.5-gcc10.3/i686-w64-mingw32.mkspiffs-7fefeac.211127.zip", - "archiveFileName": "i686-w64-mingw32.mkspiffs-7fefeac.211127.zip", - "checksum": "SHA-256:02c83b06f4f1ff7b30b91caeaf024015f34c61cce4085435b0fc7789a40cf0ba", - "size": "338192" + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/i686-w64-mingw32.mkspiffs-7fefeac.230224.zip", + "archiveFileName": "i686-w64-mingw32.mkspiffs-7fefeac.230224.zip", + "checksum": "SHA-256:96bd5d88b9aa0e126dddab6ab19d27049def8a6b341b0345d8b7577fbbbc6188", + "size": "349360" }, { "host": "x86_64-apple-darwin", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.5-gcc10.3/x86_64-apple-darwin14.mkspiffs-7fefeac.211127.tar.gz", - "archiveFileName": "x86_64-apple-darwin14.mkspiffs-7fefeac.211127.tar.gz", - "checksum": "SHA-256:20878d5208367561d085640e74b7f239c9218d2758f7fe8cea07a60d3b105c18", - "size": "368772" + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/x86_64-apple-darwin14.mkspiffs-7fefeac.230224.tar.gz", + "archiveFileName": "x86_64-apple-darwin14.mkspiffs-7fefeac.230224.tar.gz", + "checksum": "SHA-256:3d5dc573f46b726dc38d3971dfe70500e818b78230f7d531d4370b779fef3710", + "size": "380164" }, { "host": "x86_64-pc-linux-gnu", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.5-gcc10.3/x86_64-linux-gnu.mkspiffs-7fefeac.211127.tar.gz", - "archiveFileName": "x86_64-linux-gnu.mkspiffs-7fefeac.211127.tar.gz", - "checksum": "SHA-256:1e527eb56a90dee09a102baf1bae8e1356e83ea0c8e20d250a5321200ab6d319", - "size": "52647" + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/x86_64-linux-gnu.mkspiffs-7fefeac.230224.tar.gz", + "archiveFileName": "x86_64-linux-gnu.mkspiffs-7fefeac.230224.tar.gz", + "checksum": "SHA-256:ec6f989c7a494a24106b4701f4252e5bce1ddb10fc6137ce8ef336fdbce4a1fb", + "size": "66699" }, { "host": "x86_64-mingw32", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.5-gcc10.3/x86_64-w64-mingw32.mkspiffs-7fefeac.211127.zip", - "archiveFileName": "x86_64-w64-mingw32.mkspiffs-7fefeac.211127.zip", - "checksum": "SHA-256:e17d3738fac1e792d9ab79b1d3a1d8fba97545aa1e80a2ccc29280000d84e088", - "size": "350354" + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/x86_64-w64-mingw32.mkspiffs-7fefeac.230224.zip", + "archiveFileName": "x86_64-w64-mingw32.mkspiffs-7fefeac.230224.zip", + "checksum": "SHA-256:8395a75119582a056a1dc2ae8792d6b99f0b9711edf8ca3df1990e5e0df50e2b", + "size": "361467" } ] }, { - "version": "3.0.5-gcc10.3-aff7f1b", + "version": "3.2.0-gcc10.3-c791b74", "name": "mklittlefs", "systems": [ { "host": "aarch64-linux-gnu", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.5-gcc10.3/aarch64-linux-gnu.mklittlefs-943d2f7.211127.tar.gz", - "archiveFileName": "aarch64-linux-gnu.mklittlefs-943d2f7.211127.tar.gz", - "checksum": "SHA-256:b70c8e7456d46fbc1860b658a3f5f1005a2bef6466bc5a93dce7c8734d493049", - "size": "44964" + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/aarch64-linux-gnu.mklittlefs-4aca452.230224.tar.gz", + "archiveFileName": "aarch64-linux-gnu.mklittlefs-4aca452.230224.tar.gz", + "checksum": "SHA-256:e0ae2d3759f726c0fb106b82927ce9cf36e33e3912640405dd6f156845d6ad41", + "size": "63075" }, { "host": "arm-linux-gnueabihf", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.5-gcc10.3/arm-linux-gnueabihf.mklittlefs-943d2f7.211127.tar.gz", - "archiveFileName": "arm-linux-gnueabihf.mklittlefs-943d2f7.211127.tar.gz", - "checksum": "SHA-256:670cebf09371c5825946e7d5f8194503ced88956f9800cb8c886cb4c2dee12ba", - "size": "37467" + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/arm-linux-gnueabihf.mklittlefs-4aca452.230224.tar.gz", + "archiveFileName": "arm-linux-gnueabihf.mklittlefs-4aca452.230224.tar.gz", + "checksum": "SHA-256:5295e75819021e4faf87a3b4203ecf02c4768660417e3affb41aa040c1b2ebaa", + "size": "54541" }, { "host": "i686-pc-linux-gnu", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.5-gcc10.3/i686-linux-gnu.mklittlefs-943d2f7.211127.tar.gz", - "archiveFileName": "i686-linux-gnu.mklittlefs-943d2f7.211127.tar.gz", - "checksum": "SHA-256:14dfffa0e43b38e61a5784e0cfdfe110a865338db9e9b3e4ac63faba6cafb99e", - "size": "48407" + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/i686-linux-gnu.mklittlefs-4aca452.230224.tar.gz", + "archiveFileName": "i686-linux-gnu.mklittlefs-4aca452.230224.tar.gz", + "checksum": "SHA-256:d460eb040a379fe45876761eecbc32218189aeb067c9e5fdc4bcede26a3d431f", + "size": "69833" }, { "host": "i686-mingw32", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.5-gcc10.3/i686-w64-mingw32.mklittlefs-943d2f7.211127.zip", - "archiveFileName": "i686-w64-mingw32.mklittlefs-943d2f7.211127.zip", - "checksum": "SHA-256:df87d81796d50b39b2cdae07bc35a65f518d4de1d1f4529046e1b7f527be5c98", - "size": "332965" + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/i686-w64-mingw32.mklittlefs-4aca452.230224.zip", + "archiveFileName": "i686-w64-mingw32.mklittlefs-4aca452.230224.zip", + "checksum": "SHA-256:7d3701f5e89dad12459b95359b7e5b668cba64661669e8c1cdc384b83f985714", + "size": "347321" }, { "host": "x86_64-apple-darwin", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.5-gcc10.3/x86_64-apple-darwin14.mklittlefs-943d2f7.211127.tar.gz", - "archiveFileName": "x86_64-apple-darwin14.mklittlefs-943d2f7.211127.tar.gz", - "checksum": "SHA-256:958ad02d403af3d9b6505b55adc9e90324eb1f3f254a4cb197604bdaf8e418b1", - "size": "362750" + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/x86_64-apple-darwin14.mklittlefs-4aca452.230224.tar.gz", + "archiveFileName": "x86_64-apple-darwin14.mklittlefs-4aca452.230224.tar.gz", + "checksum": "SHA-256:13048f6ae246b00ea1902156542a832c767ba43d839fc62b2f6668e8821bd899", + "size": "378772" }, { "host": "x86_64-pc-linux-gnu", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.5-gcc10.3/x86_64-linux-gnu.mklittlefs-943d2f7.211127.tar.gz", - "archiveFileName": "x86_64-linux-gnu.mklittlefs-943d2f7.211127.tar.gz", - "checksum": "SHA-256:1ac67fb5dffc09378ca91707e5a9840180287e8c32d4475073da651f55341811", - "size": "47055" + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/x86_64-linux-gnu.mklittlefs-4aca452.230224.tar.gz", + "archiveFileName": "x86_64-linux-gnu.mklittlefs-4aca452.230224.tar.gz", + "checksum": "SHA-256:4f215fd9f79a7128f1a7e49dbb1e75ba79ab6527169481364de867d4e50b6d24", + "size": "64659" }, { "host": "x86_64-mingw32", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.5-gcc10.3/x86_64-w64-mingw32.mklittlefs-943d2f7.211127.zip", - "archiveFileName": "x86_64-w64-mingw32.mklittlefs-943d2f7.211127.zip", - "checksum": "SHA-256:8b4388a602d97e57ac0cec8562f935a97e22c0e80d44dcb707a7090befe592cb", - "size": "345411" + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/x86_64-w64-mingw32.mklittlefs-4aca452.230224.zip", + "archiveFileName": "x86_64-w64-mingw32.mklittlefs-4aca452.230224.zip", + "checksum": "SHA-256:bedb416db3f30b34884b5ad37ea358452dbd1e6a951a31c7b7e6d3d2f467ea68", + "size": "359007" } ] } diff --git a/platform.txt b/platform.txt index 2f0cfded80..3ca4ffae29 100644 --- a/platform.txt +++ b/platform.txt @@ -5,8 +5,8 @@ # For more info: # https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5-3rd-party-Hardware-specification -name=ESP8266 Boards (3.1.0-dev) -version=3.1.0-dev +name=ESP8266 Boards (3.2.0-dev) +version=3.2.0-dev # These will be removed by the packager script when doing a JSON release runtime.tools.xtensa-lx106-elf-gcc.path={runtime.platform.path}/tools/xtensa-lx106-elf @@ -17,6 +17,7 @@ runtime.tools.signing={runtime.platform.path}/tools/signing.py runtime.tools.elf2bin={runtime.platform.path}/tools/elf2bin.py runtime.tools.sizes={runtime.platform.path}/tools/sizes.py runtime.tools.makecorever={runtime.platform.path}/tools/makecorever.py +runtime.tools.mkbuildoptglobals={runtime.platform.path}/tools/mkbuildoptglobals.py runtime.tools.mkdir={runtime.platform.path}/tools/mkdir.py runtime.tools.cp={runtime.platform.path}/tools/cp.py runtime.tools.eboot={runtime.platform.path}/bootloaders/eboot/eboot.elf @@ -43,6 +44,9 @@ build.stdcpp_level=-std=gnu++17 build.stacksmash_flags= +# Default - never leave undefined +build.debug_optim=-Os + build.float=-u _printf_float -u _scanf_float build.led= @@ -58,28 +62,38 @@ build.spiffs_start= build.spiffs_end= build.spiffs_blocksize= +# soft float location +build.iramfloat=-DFP_IN_IROM + +# Fully qualified file names for processing sketch global options +globals.h.source.fqfn={build.source.path}/{build.project_name}.globals.h +commonhfile.fqfn={build.core.path}/CommonHFile.h +build.opt.fqfn={build.path}/core/build.opt +build.opt.flags="@{build.opt.fqfn}" +mkbuildoptglobals.extra_flags= + compiler.path={runtime.tools.xtensa-lx106-elf-gcc.path}/bin/ compiler.sdk.path={runtime.platform.path}/tools/sdk compiler.libc.path={runtime.platform.path}/tools/sdk/libc/xtensa-lx106-elf -compiler.cpreprocessor.flags=-D__ets__ -DICACHE_FLASH -U__STRICT_ANSI__ -D_GNU_SOURCE -DESP8266 "-I{compiler.sdk.path}/include" "-I{compiler.sdk.path}/{build.lwip_include}" "-I{compiler.libc.path}/include" "-I{build.path}/core" +compiler.cpreprocessor.flags=-D__ets__ -DICACHE_FLASH -U__STRICT_ANSI__ -D_GNU_SOURCE -DESP8266 {build.debug_optim} {build.opt.flags} "-I{compiler.sdk.path}/include" "-I{compiler.sdk.path}/{build.lwip_include}" "-I{compiler.libc.path}/include" "-I{build.path}/core" # support precompiled libraries in IDE v1.8.6+ compiler.libraries.ldflags= compiler.c.cmd=xtensa-lx106-elf-gcc -compiler.c.flags=-c "{compiler.warning_flags}-gcc" -std=gnu17 {build.stacksmash_flags} -Os -g -free -fipa-pta -Werror=return-type -Wpointer-arith -Wno-implicit-function-declaration -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -falign-functions=4 -MMD -ffunction-sections -fdata-sections {build.exception_flags} {build.sslflags} {build.mmuflags} {build.non32xferflags} +compiler.c.flags=-c "{compiler.warning_flags}-cflags" -std=gnu17 {build.stacksmash_flags} -g -free -fipa-pta -Werror=return-type -Wpointer-arith -Wno-implicit-function-declaration -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -falign-functions=4 -MMD -ffunction-sections -fdata-sections {build.exception_flags} {build.sslflags} {build.mmuflags} {build.non32xferflags} {build.iramfloat} compiler.S.cmd=xtensa-lx106-elf-gcc compiler.S.flags=-c -g -x assembler-with-cpp -MMD -mlongcalls "-I{runtime.tools.xtensa-lx106-elf-gcc.path}/include/" -compiler.c.elf.flags=-g "{compiler.warning_flags}-gcc" -Os -nostdlib -Wl,--no-check-sections -u app_entry {build.float} -Wl,-static "-L{compiler.sdk.path}/lib" "-L{compiler.sdk.path}/lib/{build.sdk}" "-L{build.path}" "-L{compiler.libc.path}/lib" "-Tlocal.eagle.flash.ld" -Wl,--gc-sections -Wl,-wrap,system_restart_local -Wl,-wrap,spi_flash_read +compiler.c.elf.flags=-g "{compiler.warning_flags}-cflags" -nostdlib -Wl,--no-check-sections -u app_entry {build.float} -Wl,-static "-L{compiler.sdk.path}/lib" "-L{compiler.sdk.path}/lib/{build.sdk}" "-L{build.path}" "-L{compiler.libc.path}/lib" "-Tlocal.eagle.flash.ld" -Wl,--gc-sections -Wl,-wrap,system_restart_local -Wl,-wrap,spi_flash_read 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}-g++" {build.stacksmash_flags} -Os -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} +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.as.cmd=xtensa-lx106-elf-as @@ -107,22 +121,26 @@ 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.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}" --build_path "{build.path}" --platform_path "{runtime.platform.path}" --version "{version}" + +# 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} + ## Build the app.ld linker file recipe.hooks.linking.prelink.1.pattern="{runtime.tools.python3.path}/python3" -I "{runtime.tools.mkdir}" -p "{build.path}/ld_h/" recipe.hooks.linking.prelink.2.pattern="{runtime.tools.python3.path}/python3" -I "{runtime.tools.cp}" "{runtime.platform.path}/tools/sdk/ld/{build.flash_ld}" "{build.path}/ld_h/local.eagle.flash.ld.h" recipe.hooks.linking.prelink.3.pattern="{compiler.path}{compiler.c.cmd}" -CC -E -P {build.vtable_flags} {build.mmuflags} "{build.path}/ld_h/local.eagle.flash.ld.h" -o "{build.path}/local.eagle.flash.ld" -recipe.hooks.linking.prelink.4.pattern="{compiler.path}{compiler.c.cmd}" -CC -E -P {build.vtable_flags} {build.mmuflags} "{runtime.platform.path}/tools/sdk/ld/eagle.app.v6.common.ld.h" -o "{build.path}/local.eagle.app.v6.common.ld" +recipe.hooks.linking.prelink.4.pattern="{compiler.path}{compiler.c.cmd}" {build.iramfloat} -CC -E -P {build.vtable_flags} {build.mmuflags} "{runtime.platform.path}/tools/sdk/ld/eagle.app.v6.common.ld.h" -o "{build.path}/local.eagle.app.v6.common.ld" ## Compile c files -recipe.c.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.cpreprocessor.flags} {compiler.c.flags} -D{build.sdk}=1 -DF_CPU={build.f_cpu} {build.lwip_flags} {build.debug_port} {build.debug_level} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" {build.led} {build.flash_flags} {compiler.c.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" +recipe.c.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.cpreprocessor.flags} {compiler.c.flags} -D{build.sdk}=1 -DF_CPU={build.f_cpu} {build.lwip_flags} {build.debug_port} {build.debug_level} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" -DARDUINO_BOARD_ID="{_id}" {build.led} {build.flash_flags} {compiler.c.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" ## Compile c++ files -recipe.cpp.o.pattern="{compiler.path}{compiler.cpp.cmd}" {compiler.cpreprocessor.flags} {compiler.cpp.flags} -D{build.sdk}=1 -DF_CPU={build.f_cpu} {build.lwip_flags} {build.debug_port} {build.debug_level} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" {build.led} {build.flash_flags} {compiler.cpp.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" +recipe.cpp.o.pattern="{compiler.path}{compiler.cpp.cmd}" {compiler.cpreprocessor.flags} {compiler.cpp.flags} -D{build.sdk}=1 -DF_CPU={build.f_cpu} {build.lwip_flags} {build.debug_port} {build.debug_level} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" -DARDUINO_BOARD_ID="{_id}" {build.led} {build.flash_flags} {compiler.cpp.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" ## Compile S files -recipe.S.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.cpreprocessor.flags} {compiler.S.flags} -D{build.sdk}=1 -DF_CPU={build.f_cpu} {build.lwip_flags} {build.debug_port} {build.debug_level} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" {build.led} {build.flash_flags} {compiler.S.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" +recipe.S.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.cpreprocessor.flags} {compiler.S.flags} -D{build.sdk}=1 -DF_CPU={build.f_cpu} {build.lwip_flags} {build.debug_port} {build.debug_level} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" -DARDUINO_BOARD_ID="{_id}" {build.led} {build.flash_flags} {compiler.S.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" ## Create archives recipe.ar.pattern="{compiler.path}{compiler.ar.cmd}" {compiler.ar.flags} {compiler.ar.extra_flags} "{archive_file_path}" "{object_file}" @@ -136,7 +154,7 @@ recipe.objcopy.eep.pattern= ## Create hex recipe.objcopy.hex.1.pattern="{runtime.tools.python3.path}/python3" -I "{runtime.tools.elf2bin}" --eboot "{runtime.tools.eboot}" --app "{build.path}/{build.project_name}.elf" --flash_mode {build.flash_mode} --flash_freq {build.flash_freq} --flash_size {build.flash_size} --path "{runtime.tools.xtensa-lx106-elf-gcc.path}/bin" --out "{build.path}/{build.project_name}.bin" recipe.objcopy.hex.2.pattern="{runtime.tools.python3.path}/python3" -I "{runtime.tools.signing}" --mode sign --privatekey "{build.source.path}/private.key" --bin "{build.path}/{build.project_name}.bin" --out "{build.path}/{build.project_name}.bin.signed" --legacy "{build.path}/{build.project_name}.bin.legacy_sig" -recipe.objcopy.hex.3.pattern="{runtime.tools.python3.path}/python3" -I "{runtime.tools.sizes}" --elf "{build.path}/{build.project_name}.elf" --path "{runtime.tools.xtensa-lx106-elf-gcc.path}/bin" --mmu "{build.mmuflags}" +recipe.objcopy.hex.3.pattern="{runtime.tools.python3.path}/python3" -X utf8 -I "{runtime.tools.sizes}" --elf "{build.path}/{build.project_name}.elf" --path "{runtime.tools.xtensa-lx106-elf-gcc.path}/bin" --mmu "{build.mmuflags}" ## Save hex recipe.output.tmp_file={build.project_name}.bin diff --git a/tests/build.sh b/tests/build.sh index 45f88e7ef2..6e544b9b18 100755 --- a/tests/build.sh +++ b/tests/build.sh @@ -1,22 +1,85 @@ #!/usr/bin/env bash -cache_dir=$(mktemp -d) - -source "$TRAVIS_BUILD_DIR"/tests/common.sh - -if [ -z "$BUILD_PARITY" ]; then - mod=1 - rem=0 -elif [ "$BUILD_PARITY" = "even" ]; then - mod=2 - rem=0 -elif [ "$BUILD_PARITY" = "odd" ]; then - mod=2 - rem=1 -fi +root=$(git rev-parse --show-toplevel) + +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} + +ESP8266_ARDUINO_DEBUG=${ESP8266_ARDUINO_DEBUG:-nodebug} +ESP8266_ARDUINO_LWIP=${ESP8266_ARDUINO_LWIP:-default} +ESP8266_ARDUINO_SKETCHES=${ESP8266_ARDUINO_SKETCHES:-} + +source "$root/tests/common.sh" + +cmd=${0##*/} +usage=" +ENVIRONMENT: + ESP8266_ARDUINO_SKETCHES - list of .ino files; defaults to **all available examples** + ESP8266_ARDUINO_BUILDER - arduino or platformio -install_arduino nodebug -build_sketches_with_arduino "$mod" "$rem" lm2f + For Arduino IDE: + ESP8266_ARDUINO_IDE - path to the IDE (portable) + 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 + ESP8266_ARDUINO_LWIP - v4 or v6 -rm -rf "$cache_dir" +USAGE: + $cmd <[even | odd]> - build every Nth, when ' % 2' is either even or odd + $cmd - build every Nth, when ' % ' is equal to 'rem' + $cmd - build every .ino file from ESP8266_ARDUINO_SKETCHES +" + +mod=1 +rem=0 + +if [ "$#" -eq 1 ] ; then + case "$1" in + "-h") + echo "$usage" + exit 0 + ;; + "even") + mod=2 + rem=0 + ;; + "odd") + mod=2 + rem=1 + ;; + *) + echo 'Can either be even or odd' + exit 1 + ;; + esac +elif [ "$#" -eq 2 ] ; then + mod=$1 + rem=$2 +elif [ "$#" -gt 2 ] ; then + echo "$usage" + exit 1 +fi + +if [ -z "$ESP8266_ARDUINO_SKETCHES" ] ; then + ESP8266_ARDUINO_SKETCHES=$(find $root/libraries -name *.ino | sort) +fi +case "$ESP8266_ARDUINO_BUILDER" in +"arduino") + install_arduino "$ESP8266_ARDUINO_DEBUG" + build_sketches_with_arduino "$mod" "$rem" "$ESP8266_ARDUINO_LWIP" + ;; +"platformio") + install_platformio nodemcuv2 + build_sketches_with_platformio "$mod" "$rem" + ;; +*) + echo "Unknown builder! Must be either arduino or platformio" + exit 1 + ;; +esac diff --git a/tests/build6.sh b/tests/build6.sh deleted file mode 100755 index 4fc199699f..0000000000 --- a/tests/build6.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash - -cache_dir=$(mktemp -d) - -source "$TRAVIS_BUILD_DIR"/tests/common.sh - -if [ -z "$BUILD_PARITY" ]; then - mod=1 - rem=0 -elif [ "$BUILD_PARITY" = "even" ]; then - mod=2 - rem=0 -elif [ "$BUILD_PARITY" = "odd" ]; then - mod=2 - rem=1 -fi - -install_arduino nodebug -build_sketches_with_arduino "$mod" "$rem" lm6f - -rm -rf "$cache_dir" - diff --git a/tests/buildm.sh b/tests/buildm.sh deleted file mode 100755 index 1b973a59f6..0000000000 --- a/tests/buildm.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -set -e - -cd $(cd ${0%/*}; pwd)/host - -make -j ../../libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser diff --git a/tests/ci/build_boards.sh b/tests/ci/build_boards.sh index a263bdad28..852f77d339 100755 --- a/tests/ci/build_boards.sh +++ b/tests/ci/build_boards.sh @@ -4,11 +4,14 @@ set -ev -cd $TRAVIS_BUILD_DIR +root=$(git rev-parse --show-toplevel) +cd $root tools/boards.txt.py --boardsgen --ldgen --packagegen --docgen -git diff --exit-code -- boards.txt \ - doc/boards.rst \ - tools/sdk/ld/ -git diff --exit-code -w -- package/package_esp8266com_index.template.json +git diff --exit-code -- \ + boards.txt \ + doc/boards.rst \ + tools/sdk/ld/ +git diff --exit-code --ignore-all-space -- \ + package/package_esp8266com_index.template.json diff --git a/tests/ci/build_docs.sh b/tests/ci/build_docs.sh index 31db9f8f9f..7a5ae2a632 100755 --- a/tests/ci/build_docs.sh +++ b/tests/ci/build_docs.sh @@ -4,6 +4,5 @@ set -ev -cd $TRAVIS_BUILD_DIR/doc - -SPHINXOPTS="-W" make html +root=$(git rev-parse --show-toplevel) +make SPHINXOPTS="--fail-on-warning" SPHINXBUILD="${SPHINXBUILD:?sphinx-build}" -C $root/doc html diff --git a/tests/ci/build_package.sh b/tests/ci/build_package.sh index 11e9acf6b0..f71339731e 100755 --- a/tests/ci/build_package.sh +++ b/tests/ci/build_package.sh @@ -4,13 +4,16 @@ set -ev -export PKG_URL=https://github.com/esp8266/Arduino/releases/download/$TRAVIS_TAG/esp8266-$TRAVIS_TAG.zip -export DOC_URL=https://arduino-esp8266.readthedocs.io/en/$TRAVIS_TAG/ +root=$(git rev-parse --show-toplevel) +tag=$ESP8266_ARDUINO_RELEASE_TAG + +export PKG_URL=https://github.com/esp8266/Arduino/releases/download/$tag/esp8266-$tag.zip +export DOC_URL=https://arduino-esp8266.readthedocs.io/en/$tag/ if [ -z "$CI_GITHUB_API_KEY" ]; then echo "Github API key not set. Skip building the package." exit 0 fi -cd $TRAVIS_BUILD_DIR/package +cd $root/package ./build_boards_manager_package.sh diff --git a/tests/ci/eboot_test.sh b/tests/ci/eboot_test.sh index 0450b03353..7efb6bbd60 100755 --- a/tests/ci/eboot_test.sh +++ b/tests/ci/eboot_test.sh @@ -1,13 +1,14 @@ #!/bin/bash -READELF="$TRAVIS_BUILD_DIR/tools/xtensa-lx106-elf/bin/xtensa-lx106-elf-readelf" - set -ev -cd $TRAVIS_BUILD_DIR/tools +root=$(git rev-parse --show-toplevel) +READELF="$root/tools/xtensa-lx106-elf/bin/xtensa-lx106-elf-readelf" + +cd $root/tools python3 get.py -q -cd $TRAVIS_BUILD_DIR/bootloaders/eboot +cd $root/bootloaders/eboot "$READELF" -x .data -x .text eboot.elf > git.txt make clean diff --git a/tests/ci/host_test.sh b/tests/ci/host_test.sh index ef143f90e1..9ce2ecf0e8 100755 --- a/tests/ci/host_test.sh +++ b/tests/ci/host_test.sh @@ -4,8 +4,8 @@ set -ev -cd $TRAVIS_BUILD_DIR/tests/host - +root=$(git rev-parse --show-toplevel) +cd $root/tests/host make -j2 FORCE32=0 ssl for i in ../../libraries/ESP8266WiFi/examples/WiFiClient/WiFiClient \ @@ -13,7 +13,8 @@ for i in ../../libraries/ESP8266WiFi/examples/WiFiClient/WiFiClient \ ../../libraries/ESP8266WebServer/examples/HelloServer/HelloServer \ ../../libraries/SD/examples/Files/Files \ ../../libraries/LittleFS/examples/LittleFS_Timestamp/LittleFS_Timestamp \ - ../../libraries/LittleFS/examples/SpeedTest/SpeedTest ; do + ../../libraries/LittleFS/examples/SpeedTest/SpeedTest \ + ../../libraries/DNSServer/examples/DNSServer/DNSServer ; do make -j2 D=1 FORCE32=0 $i valgrind --leak-check=full --track-origins=yes --error-limit=no --show-leak-kinds=all --error-exitcode=999 bin/$(basename $i)/$(basename $i) -1 done diff --git a/tests/ci/pkgrefs_test.sh b/tests/ci/pkgrefs_test.sh index ea4f1c235e..a189e1a594 100755 --- a/tests/ci/pkgrefs_test.sh +++ b/tests/ci/pkgrefs_test.sh @@ -2,8 +2,10 @@ set -ev +root=$(git rev-parse --show-toplevel) + fail=0 -for i in $(cat "$TRAVIS_BUILD_DIR/package/package_esp8266com_index.template.json" | jq '.packages[0]."tools" | .[] | .systems[] | "\(.url) \(.checksum)"' | sort -u | sed 's/ /@/'); do +for i in $(cat "$root/package/package_esp8266com_index.template.json" | jq '.packages[0]."tools" | .[] | .systems[] | "\(.url) \(.checksum)"' | sort -u | sed 's/ /@/'); do url=$(echo $i | sed 's/@/ /' | cut -f2 -d\" | cut -f1 -d' ') sha=$(echo $i | sed 's/@/ /' | cut -f2 -d\" | cut -f2 -d' ' | cut -f2 -d:) echo "INFO: Checking $url" diff --git a/tests/ci/style_check.sh b/tests/ci/style_check.sh index ddcd48b906..e8a1dd7697 100755 --- a/tests/ci/style_check.sh +++ b/tests/ci/style_check.sh @@ -4,12 +4,15 @@ set -e -x +git --version || true root=$(git rev-parse --show-toplevel) -${root}/tests/restyle.sh -# Revert changes which formatter might have done to the submodules, +# Run formatter and compare what changed in the git tree. +# Also revert changes which formatter might have done to the submodules, # as we don't want to fail the build because of the 3rd party libraries -git --version || true -git submodule foreach --recursive 'git reset --hard' -git diff --exit-code -- $TRAVIS_BUILD_DIR +cd $root +./tests/restyle.sh + +git submodule foreach --recursive 'git reset --hard' +git diff --exit-code diff --git a/tests/clang-format-core.yaml b/tests/clang-format-core.yaml index f8e8bd13b9..0df633d245 100644 --- a/tests/clang-format-core.yaml +++ b/tests/clang-format-core.yaml @@ -6,7 +6,6 @@ KeepEmptyLinesAtTheStartOfBlocks: false SpaceAfterTemplateKeyword: false SpaceBeforeInheritanceColon: false SpacesBeforeTrailingComments: 2 -AlignTrailingComments: true AllowShortBlocksOnASingleLine: Empty AllowShortFunctionsOnASingleLine: Empty AllowShortIfStatementsOnASingleLine: false @@ -27,3 +26,5 @@ NamespaceIndentation: Inner BreakBeforeBraces: Allman IndentWidth: 4 IndentCaseLabels: false +ReflowComments: false +SkipMacroDefinitionBody: true diff --git a/tests/common.sh b/tests/common.sh index b6ce2fe61e..f05eb6b37a 100755 --- a/tests/common.sh +++ b/tests/common.sh @@ -1,253 +1,518 @@ #!/usr/bin/env bash -# return 1 if this test should not be built in CI (for other archs, not needed, etc.) -function skip_ino() +set -u -e -E -o pipefail + +cache_dir=$(mktemp -d) +trap 'trap_exit' EXIT + +function trap_exit() { - local ino=$1 - local skiplist="" - # Add items to the following list with "\n" netween them to skip running. No spaces, tabs, etc. allowed - read -d '' skiplist << EOL || true -/#attic/ -/AvrAdcLogger/ -/examplesV1/ -/RtcTimestampTest/ -/SoftwareSpi/ -/TeensyDmaAdcLogger/ -/TeensyRtcTimestamp/ -/TeensySdioDemo/ -/TeensySdioLogger/ -/UserChipSelectFunction/ -/UserSPIDriver/ -EOL - echo $ino | grep -q -F "$skiplist" - echo $(( 1 - $? )) + # workaround for macOS shipping with broken bash + local exit_code=$? + if [ -z "${ESP8266_ARDUINO_PRESERVE_CACHE-}" ]; then + rm -rf "$cache_dir" + fi + + exit $exit_code } -function print_size_info() +function step_summary() { - elf_file=$1 + local header=$1 + local contents=$2 + + # ref. https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#adding-a-job-summary + if [ -n "${GITHUB_STEP_SUMMARY-}" ]; then + { echo "# $header"; echo '```console'; cat "$contents"; echo '```'; } \ + >> $GITHUB_STEP_SUMMARY + else + echo "# $header" + cat "$contents" + fi +} - if [ -z "$elf_file" ]; then - printf "sketch data rodata bss text irom0.text dram flash\n" +# 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/"* | \ + *"/onewiretest/"* | \ + *"/debug/"*) 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 +} - elf_name=$(basename $elf_file) - sketch_name="${elf_name%.*}" - # echo $sketch_name - xtensa-lx106-elf-size --format=sysv $elf_file | sed s/irom0.text/irom0text/g > size.txt - declare -A segments - for seg in data rodata bss text irom0text; do - segments[$seg]=$(grep ^.$seg size.txt | awk '{sum += $2} END {print sum}') - done +function print_size_info_header() +{ + printf "%-28s %-8s %-8s %-8s %-8s %-10s %-8s %-8s\n" sketch data rodata bss text irom0.text dram flash +} - total_ram=$((${segments[data]} + ${segments[rodata]} + ${segments[bss]})) - total_flash=$((${segments[data]} + ${segments[rodata]} + ${segments[text]} + ${segments[irom0text]})) +function print_size_info() +{ + local awk_script=' +/^\.data/ || /^\.rodata/ || /^\.bss/ || /^\.text/ || /^\.irom0\.text/{ + size[$1] = $2 +} - printf "%-28s %-8d %-8d %-8d %-8d %-8d %-8d %-8d\n" $sketch_name ${segments[data]} ${segments[rodata]} ${segments[bss]} ${segments[text]} ${segments[irom0text]} $total_ram $total_flash - return 0 +END { + total_ram = size[".data"] + size[".rodata"] + size[".bss"] + total_flash = size[".data"] + size[".rodata"] + size[".text"] + size[".irom0.text"] + + printf "%-28s %-8d %-8d %-8d %-8d %-10d %-8d %-8d\n", + sketch_name, + size[".data"], size[".rodata"], size[".bss"], size[".text"], size[".irom0.text"], + total_ram, total_flash +} +' + local size=$1 + local elf_file=$2 + + local elf_name + elf_name=$(basename $elf_file) + $size --format=sysv "$elf_file" | \ + awk -v sketch_name="${elf_name%.*}" "$awk_script" - } function build_sketches() { - set +e - local arduino=$1 - local srcpath=$2 - local build_arg=$3 - local build_dir=build.tmp - local build_mod=$4 - local build_rem=$5 - local lwip=$6 - mkdir -p $build_dir - local build_cmd="python3 tools/build.py -b generic -v -w all -s 4M1M -v -k --build_cache $cache_dir -p ./$build_dir -n $lwip $build_arg " - if [ "$WINDOWS" = "1" ]; then - # Paths to the arduino builder need to be / referenced, not our native ones - build_cmd=$(echo $build_cmd --ide_path $arduino | sed 's/ \/c\// \//g' ) # replace '/c/' with '/' - fi - local sketches=$(find $srcpath -name *.ino | sort) - print_size_info >size.log - export ARDUINO_IDE_PATH=$arduino + local core_path=$1 + local ide_path=$2 + local hardware_path=$3 + local library_path=$4 + local build_mod=$5 + local build_rem=$6 + local lwip=$7 + + local build_dir="$cache_dir"/build + mkdir -p "$build_dir" + + local build_cache="$cache_dir"/cache + mkdir -p "$build_cache" + + 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" + + print_size_info_header >"$cache_dir"/size.log + + local mk_clean_core=1 local testcnt=0 - for sketch in $sketches; do + + for sketch in $ESP8266_ARDUINO_SKETCHES; do testcnt=$(( ($testcnt + 1) % $build_mod )) - if [ $testcnt -ne $build_rem ]; then + if [ $testcnt -ne "$build_rem" ]; then 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 + 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 - # Set the time of the cached core.a file to the future so the GIT header - # we regen won't cause the builder to throw it out and rebuild from scratch. - touch -d 'now + 1 day' $cache_dir/core/*.a + 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 + fi + 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 # 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 # them each sketch. - rm -rf $build_dir/sketch $build_dir/*.bin $build_dir/*.map $build_dir/*.elf + rm -rf "$build_dir"/sketch \ + "$build_dir"/*.bin \ + "$build_dir"/*.map \ + "$build_dir"/*.elf - local sketchdir=$(dirname $sketch) - local sketchdirname=$(basename $sketchdir) - local sketchname=$(basename $sketch) - if [[ "${sketchdirname}.ino" != "$sketchname" ]]; then - echo "Skipping $sketch, because it is not the main sketch file"; - continue - fi; - if [[ -f "$sketchdir/.test.skip" ]]; then - echo -e "\n ------------ Skipping $sketch ------------ \n"; - continue - fi - if [[ $(skip_ino $sketch) = 1 ]]; then - echo -e "\n ------------ Skipping $sketch ------------ \n"; + 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 -e "\n ------------ Building $sketch ------------ \n"; - # $arduino --verify $sketch; - if [ "$WINDOWS" == "1" ]; then - sketch=$(echo $sketch | sed 's/^\/c//') - # MINGW will try to be helpful and silently convert args that look like paths to point to a spot inside the MinGW dir. This breaks everything. - # http://www.mingw.org/wiki/Posix_path_conversion - # https://stackoverflow.com/questions/7250130/how-to-stop-mingw-and-msys-from-mangling-path-names-given-at-the-command-line#34386471 - export MSYS2_ARG_CONV_EXC="*" - export MSYS_NO_PATHCONV=1 - fi + + echo ::group::Building $sketch echo "$build_cmd $sketch" - time ($build_cmd $sketch >build.log) - local result=$? + + local result + time $build_cmd $sketch >"$cache_dir"/build.log \ + && result=0 || result=1 + if [ $result -ne 0 ]; then - echo "Build failed ($1)" - echo "Build log:" - cat build.log - set -e + echo ::error::Build failed for $sketch + cat "$cache_dir/build.log" + echo ::endgroup:: return $result else - local warns=$( grep -c warning: build.log ) - if [ $warns -ne 0 ]; then - echo "Warnings detected, log follows:" - cat build.log - fi + grep -s -c warning: "$cache_dir"/build.log \ + && step_summary "$sketch warnings" "$cache_dir/build.log" fi - rm build.log - print_size_info $build_dir/*.elf >>size.log + + print_size_info "$core_path"/tools/xtensa-lx106-elf/bin/xtensa-lx106-elf-size \ + $build_dir/*.elf >>$cache_dir/size.log + + echo ::endgroup:: done - set -e +} + +function check_hash() +{ + local file=$1 + local hash=$2 + + local shasum + case ${RUNNER_OS-} in + "macOS") + shasum="shasum -a 512" + ;; + *) + shasum="sha512sum" + ;; + esac + + echo "$hash $file" | $shasum -c - +} + +function fetch_and_unpack() +{ + local archive=$1 + local hash=$2 + local url=$3 + + test -r "$archive" \ + && check_hash "$archive" "$hash" \ + || { pushd "$cache_dir" + curl --output "$archive" --location "$url"; + check_hash "$archive" "$hash"; + popd; + mv "$cache_dir/$archive" ./"$archive"; } + + case $archive in + *".zip") + unzip -q "$archive" + ;; + *) + tar xf "$archive" + ;; + esac +} + +function install_library() +{ + local lib_path=$1 + local name=$2 + local archive=$3 + local hash=$4 + local url=$5 + + fetch_and_unpack "$archive" "$hash" "$url" + mkdir -p "$lib_path" + rm -rf "$lib_path/$name" + mv "$name" "$lib_path/$name" } function install_libraries() { - mkdir -p $HOME/Arduino/libraries - pushd $HOME/Arduino/libraries + local core_path=$1 + local lib_path=$2 - # install ArduinoJson library - { test -r ArduinoJson-v6.11.0.zip || curl --output ArduinoJson-v6.11.0.zip -L https://github.com/bblanchon/ArduinoJson/releases/download/v6.11.0/ArduinoJson-v6.11.0.zip; } && unzip -q ArduinoJson-v6.11.0.zip + 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" popd } function install_ide() { - local idever='nightly' - local ideurl='https://www.arduino.cc/download.php?f=/arduino-nightly' + # TODO replace ide distribution + arduino-builder with arduino-cli + local idever='1.8.19' + local ideurl="https://downloads.arduino.cc/arduino-$idever" - #local idever='1.8.10' - #local ideurl="https://downloads.arduino.cc/arduino-$idever" + echo "Arduino IDE ${idever}" - echo "using Arduino IDE distribution ${idever}" + local core_path=$1 + local ide_path=$2 - local ide_path=$1 - local core_path=$2 - local debug=$3 mkdir -p ${core_path}/tools/dist - if [ "$WINDOWS" = "1" ]; then - test -r ${core_path}/tools/dist/arduino-windows.zip || curl --output ${core_path}/tools/dist/arduino-windows.zip -L "${ideurl}-windows.zip" - unzip -q ${core_path}/tools/dist/arduino-windows.zip - mv arduino-${idever} arduino-distrib - elif [ "$MACOSX" = "1" ]; then - # MACOS only has next-to-obsolete Python2 installed. Install Python 3 from python.org - wget -q https://www.python.org/ftp/python/3.7.4/python-3.7.4-macosx10.9.pkg - sudo installer -pkg python-3.7.4-macosx10.9.pkg -target / - # Install the Python3 certificates, because SSL connections fail w/o them and of course they aren't installed by default. - ( cd "/Applications/Python 3.7/" && sudo "./Install Certificates.command" ) + pushd ${core_path}/tools/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 - test -r ${core_path}/tools/dist/arduino-macos.zip || wget -q -O ${core_path}/tools/dist/arduino-macos.zip "${ideurl}-macosx.zip" - unzip -q ${core_path}/tools/dist/arduino-macos.zip mv Arduino.app arduino-distrib mv arduino-distrib/Contents/Java/* arduino-distrib/. else - test -r ${core_path}/tools/dist/arduino-linux.tar.xz || wget -q -O ${core_path}/tools/dist/arduino-linux.tar.xz "${ideurl}-linux64.tar.xz" - tar xf ${core_path}/tools/dist/arduino-linux.tar.xz - mv arduino-${idever} arduino-distrib - fi - mv arduino-distrib $ide_path - cd $ide_path/hardware - mkdir esp8266com - cd esp8266com - if [ "$WINDOWS" = "1" ]; then - cp -a $core_path esp8266 - else - ln -s $core_path esp8266 + fetch_and_unpack "arduino-linux.tar.xz" \ + "9328abf8778200019ed40d4fc0e6afb03a4cee8baaffbcea7dd3626477e14243f779eaa946c809fb153a542bf2ed60cf11a5f135c91ecccb1243c1387be95328" \ + "${ideurl}-linux64.tar.xz" + mv arduino-$idever arduino-distrib fi + + mv arduino-distrib "$ide_path" + popd +} + +function install_core() +{ + local core_path=$1 + local hardware_core_path=$2 + local debug=$3 + + pushd "${core_path}" + local debug_flags="" if [ "$debug" = "debug" ]; then - debug_flags="-DDEBUG_ESP_PORT=Serial -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" + debug_flags="-DDEBUG_ESP_PORT=Serial -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" fi - # Set custom warnings for all builds (i.e. could add -Wextra at some point) - echo "compiler.c.extra_flags=-Wall -Wextra -Werror $debug_flags" > esp8266/platform.local.txt - echo "compiler.cpp.extra_flags=-Wall -Wextra -Werror $debug_flags" >> esp8266/platform.local.txt + + # 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 echo -e "\n----platform.local.txt----" - cat esp8266/platform.local.txt + cat platform.local.txt echo -e "\n----\n" - cd esp8266/tools + + pushd tools python3 get.py -q - if [ "$WINDOWS" = "1" ]; then - # Because the symlinks don't work well under Win32, we need to add the path to this copy, not the original... - relbin=$(realpath $PWD/xtensa-lx106-elf/bin) - export PATH="$ide_path:$relbin:$PATH" + + popd + popd + + local core_dir + core_dir=$(dirname "$hardware_core_path") + mkdir -p "$core_dir" + + if [ "${RUNNER_OS-}" = "Windows" ]; then + cp -a "$core_path" "${core_dir}/esp8266" else - export PATH="$ide_path:$core_path/tools/xtensa-lx106-elf/bin:$PATH" + ln -s "$core_path" "$hardware_core_path" fi } function install_arduino() { + echo ::group::Install arduino local debug=$1 - # Install Arduino IDE and required libraries - echo -e "travis_fold:start:sketch_test_env_prepare" - cd $TRAVIS_BUILD_DIR - install_ide $HOME/arduino_ide $TRAVIS_BUILD_DIR $debug - cd $TRAVIS_BUILD_DIR - install_libraries - echo -e "travis_fold:end:sketch_test_env_prepare" + + 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" + + install_libraries "$ESP8266_ARDUINO_BUILD_DIR" "$ESP8266_ARDUINO_LIBRARIES" + + echo ::endgroup:: +} + +function arduino_lwip_menu_option() +{ + case $1 in + "default") + echo "lm2f" + ;; + "IPv6") + echo "lm6f" + ;; + esac } function build_sketches_with_arduino() { local build_mod=$1 local build_rem=$2 - local lwip=$3 - # Compile sketches - echo -e "travis_fold:start:sketch_test" - build_sketches $HOME/arduino_ide $TRAVIS_BUILD_DIR/libraries "-l $HOME/Arduino/libraries" $build_mod $build_rem $lwip - echo -e "travis_fold:end:sketch_test" + local lwip + lwip=$(arduino_lwip_menu_option $3) - # Generate size report - echo -e "travis_fold:start:size_report" - cat size.log - echo -e "travis_fold:end:size_report" + build_sketches "$ESP8266_ARDUINO_BUILD_DIR" \ + "$ESP8266_ARDUINO_IDE" \ + "$ESP8266_ARDUINO_HARDWARE" \ + "$ESP8266_ARDUINO_LIBRARIES" \ + "$build_mod" "$build_rem" "$lwip" + step_summary "Size report" "$cache_dir/size.log" } +function install_platformio() +{ + echo ::group::Install PlatformIO -set -e + local board=$1 -if [ -z "$TRAVIS_BUILD_DIR" ]; then - echo "TRAVIS_BUILD_DIR is not set, trying to guess:" - pushd $(dirname $0)/../ > /dev/null - TRAVIS_BUILD_DIR=$PWD - popd > /dev/null - echo "TRAVIS_BUILD_DIR=$TRAVIS_BUILD_DIR" -fi + pushd $ESP8266_ARDUINO_BUILD_DIR/tools + python3 get.py -q + popd + + # 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" + + 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) + cat < $cache_dir/platformio.ini +[env:$board] +platform = espressif8266 +board = $board +framework = arduino +platform_packages = + ${framework_symlink} + ${toolchain_symlink} +EOF + + # Install dependencies: + # - esp8266/examples/ConfigFile + pio pkg install --global --library "ArduinoJson@^6.11.0" + + echo ::endgroup:: +} + +function build_sketches_with_platformio() +{ + local build_mod=$1 + local build_rem=$2 + local testcnt=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 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 + + local result + time pio ci \ + --verbose \ + --project-conf $cache_dir/platformio.ini \ + $sketchdir >$cache_dir/build.log 2>&1 \ + && result=0 || result=1 + if [ $result -ne 0 ]; then + echo ::error::Build failed for $sketch + cat "$cache_dir/build.log" + echo ::endgroup:: + return $result + fi + + echo ::endgroup:: + done +} + +if [ -z "${ESP8266_ARDUINO_BUILD_DIR-}" ]; then + ESP8266_ARDUINO_BUILD_DIR=$(git rev-parse --show-toplevel) + echo "Using ESP8266_ARDUINO_BUILD_DIR=$ESP8266_ARDUINO_BUILD_DIR" +fi diff --git a/tests/debug.sh b/tests/debug.sh deleted file mode 100755 index bbca35ff53..0000000000 --- a/tests/debug.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash - -cache_dir=$(mktemp -d) - -source "$TRAVIS_BUILD_DIR"/tests/common.sh - -if [ "$BUILD_PARITY" = "even" ]; then - mod=2 - rem=0 -elif [ "$BUILD_PARITY" = "odd" ]; then - mod=2 - rem=1 -fi - -install_arduino debug -build_sketches_with_arduino "$mod" "$rem" lm2f - -rm -rf "$cache_dir" diff --git a/tests/debug6.sh b/tests/debug6.sh deleted file mode 100644 index b0a1aeaaba..0000000000 --- a/tests/debug6.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bash - -cache_dir=$(mktemp -d) - -source "$TRAVIS_BUILD_DIR"/tests/common.sh - -if [ -z "$BUILD_PARITY" ]; then - mod=1 - rem=0 -elif [ "$BUILD_PARITY" = "even" ]; then - mod=2 - rem=0 -elif [ "$BUILD_PARITY" = "odd" ]; then - mod=2 - rem=1 -fi - -install_arduino debug -build_sketches_with_arduino "$mod" "$rem" lm6f - -rm -rf "$cache_dir" diff --git a/tests/device/Makefile b/tests/device/Makefile index 8bc145a532..cbbd34728a 100644 --- a/tests/device/Makefile +++ b/tests/device/Makefile @@ -65,8 +65,8 @@ $(TEST_LIST): ifeq ("$(MOCK)", "1") @echo Compiling $(notdir $@) (cd ../host; make D=$(V) ULIBDIRS=../device/libraries/BSTest ../device/$(@:%.ino=%)) - $(SILENT)source $(BS_DIR)/virtualenv/bin/activate && \ - $(PYTHON) $(BS_DIR)/runner.py \ + $(SILENT)$(BS_DIR)/virtualenv/bin/python \ + $(BS_DIR)/runner.py \ $(RUNNER_DEBUG_FLAG) \ -e "$(ESP8266_CORE_PATH)/tests/host/bin/$(@:%.ino=%)" \ -n $(basename $(notdir $@)) \ @@ -120,8 +120,8 @@ ifneq ("$(NO_RUN)","1") --port $(UPLOAD_PORT) \ --baud $(UPLOAD_BAUD) \ read_flash_status $(REDIR) # reset - $(SILENT)source $(BS_DIR)/virtualenv/bin/activate && \ - $(PYTHON) $(BS_DIR)/runner.py \ + $(SILENT)$(BS_DIR)/virtualenv/bin/python \ + $(BS_DIR)/runner.py \ $(RUNNER_DEBUG_FLAG) \ -p $(UPLOAD_PORT) \ -n $(basename $(notdir $@)) \ diff --git a/tests/device/libraries/BSTest/Makefile b/tests/device/libraries/BSTest/Makefile index 93c181f5fb..78e7a19a02 100644 --- a/tests/device/libraries/BSTest/Makefile +++ b/tests/device/libraries/BSTest/Makefile @@ -11,11 +11,11 @@ clean: rm -rf $(TEST_EXECUTABLE) $(PYTHON_ENV_DIR): - virtualenv --python=$(PYTHON) --no-site-packages $(PYTHON_ENV_DIR) - . $(PYTHON_ENV_DIR)/bin/activate && pip install -r requirements.txt + $(PYTHON) -mvenv $(PYTHON_ENV_DIR) + $(PYTHON_ENV_DIR)/bin/pip install -r requirements.txt test: $(TEST_EXECUTABLE) $(PYTHON_ENV_DIR) - . $(PYTHON_ENV_DIR)/bin/activate && $(PYTHON) runner.py -e $(TEST_EXECUTABLE) -m test/test.py + $(PYTHON_ENV_DIR)/bin/python runner.py -e $(TEST_EXECUTABLE) -m test/test.py $(TEST_EXECUTABLE): test/test.cpp g++ -std=c++11 -Isrc -o $@ test/test.cpp diff --git a/tests/device/libraries/BSTest/runner.py b/tests/device/libraries/BSTest/runner.py index 97849a5e32..5c9dddfef9 100644 --- a/tests/device/libraries/BSTest/runner.py +++ b/tests/device/libraries/BSTest/runner.py @@ -8,7 +8,9 @@ import argparse import serial import subprocess -import imp + +from importlib.machinery import SourceFileLoader + try: from configparser import ConfigParser except: @@ -278,12 +280,12 @@ def main(): if args.env_file is not None: cfg = ConfigParser() cfg.optionxform = str - with args.env_file as fp: - cfg.readfp(fp) + with args.env_file as env: + cfg.read_file(env) env_vars = cfg.items('global') mocks = {} if args.mock is not None: - mocks_mod = imp.load_source('mocks', args.mock) + mocks_mod = SourceFileLoader('mocks', args.mock).load_module() mocks = mock_decorators.env with spawn_func(spawn_arg) as sp: ts = run_tests(sp, name, mocks, env_vars) 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_spi_flash/test_spi_flash.ino b/tests/device/test_spi_flash/test_spi_flash.ino index b1e18241f5..16fa4a9750 100644 --- a/tests/device/test_spi_flash/test_spi_flash.ino +++ b/tests/device/test_spi_flash/test_spi_flash.ino @@ -1,5 +1,4 @@ #include -#include BS_ENV_DECLARE(); @@ -176,6 +175,11 @@ TEST_CASE("Unaligned page cross only", "[spi_flash]") CHECK(testFlash(0xa0000 + 255, 1, 2)); } +TEST_CASE("Unaligned page cross with unaligned size (#8372, #8588, #8605)", "[spi_flash]") +{ + CHECK(testFlash(0xa00b, 0, 202)); +} + void loop () { } diff --git a/tests/host/Makefile b/tests/host/Makefile index 13c9feed52..97a4c671fb 100644 --- a/tests/host/Makefile +++ b/tests/host/Makefile @@ -12,31 +12,25 @@ RANLIB ?= ranlib MAKEFILE = $(word 1, $(MAKEFILE_LIST)) -CXX = $(shell for i in g++-10 g++-9 g++-8 g++; do which $$i > /dev/null && { echo $$i; break; } done) -CC = $(shell for i in gcc-10 gcc-9 gcc-8 gcc; do which $$i > /dev/null && { echo $$i; break; } done) -GCOV = $(shell for i in gcov-10 gcov-9 gcov-8 gcov; do which $$i > /dev/null && { echo $$i; break; } done) -$(warning using $(CXX)) -ifeq ($(CXX),g++) -CXXFLAGS += -std=gnu++11 -else -CXXFLAGS += -std=gnu++17 -endif -ifeq ($(CC),gcc) -CFLAGS += -std=gnu11 -else -CFLAGS += -std=gnu17 -endif +# Prefer named GCC (and, specifically, GCC10), same as platform.txt / platformio_build.py +find_tool = $(shell for name in $(1) $(2); do which $$name && break; done 2>/dev/null) +CXX = $(call find_tool,g++-10,g++) +CC = $(call find_tool,gcc-10,gcc) +GCOV = $(call find_tool,gcov-10,gcov) -# I wasn't able to build with clang when -coverage flag is enabled, forcing GCC on OS X -ifeq ($(shell uname -s),Darwin) -CC ?= gcc -CXX ?= g++ -endif -GCOV ?= gcov +$(warning using $(CXX) and $(CC)) + +GCOV ?= gcov VALGRIND ?= valgrind -LCOV ?= lcov --gcov-tool $(GCOV) -GENHTML ?= genhtml +LCOV ?= lcov --gcov-tool $(GCOV) +GENHTML ?= genhtml + +# Board fild will be built with GCC10, but we have some limited ability to build with older versions +# *Always* push the standard used in the platform.txt +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) @@ -57,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++ $@"; @@ -73,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 \ @@ -150,6 +169,7 @@ MOCK_CPP_FILES_EMU := $(MOCK_CPP_FILES_COMMON) \ ArduinoMainUdp.cpp \ ArduinoMainSpiffs.cpp \ ArduinoMainLittlefs.cpp \ + DhcpServer.cpp \ user_interface.cpp \ ) @@ -174,6 +194,8 @@ INC_PATHS += \ ../../tools/sdk/lwip2/include \ ) +TEST_ARGS ?= + TEST_CPP_FILES := \ fs/test_fs.cpp \ core/test_pgmspace.cpp \ @@ -236,7 +258,7 @@ CI: # run CI doCI: build-info $(OUTPUT_BINARY) valgrind test gcov test: $(OUTPUT_BINARY) # run host test for CI - $(OUTPUT_BINARY) + $(OUTPUT_BINARY) $(TEST_ARGS) .PHONY: clean clean: clean-lcov clean-objects @@ -376,12 +398,13 @@ else LIBSSL = $(LIBSSLFILE) endif ssl: # download source and build BearSSL - cd ../../tools/sdk/ssl && make native$(N32) + cd ../../tools/sdk/ssl && $(MAKE) native$(N32) ULIBPATHS = $(shell echo $(ULIBDIRS) | sed 's,:, ,g') -USERLIBDIRS = $(shell test -z "$(ULIBPATHS)" || for d in $(ULIBPATHS); do for dd in $$d $$d/src $$d/src/libmad; do test -d $$dd && { echo -I$$dd; echo "userlib: using directory '$$dd'" 1>&2; } done; done) -USERLIBSRCS = $(shell test -z "$(ULIBPATHS)" || for d in $(ULIBPATHS); do for ss in $$d/*.cpp $$d/src/*.cpp $$d/src/libmad/*.c; do test -r $$ss && echo $$ss; done; done) -INC_PATHS += $(USERLIBDIRS) +USERLIBDIRS = $(shell test -z "$(ULIBPATHS)" || for d in $(ULIBPATHS); do for dd in $$d $$d/utility $$d/src $$d/src/utility; do test -d $$dd && echo $$dd; done; done) +USERLIBSRCS := $(shell test -z "$(USERLIBDIRS)" || for d in $(USERLIBDIRS); do for ss in $$d/*.c $$d/*.cpp; do test -r $$ss && echo $$ss; done; done) +USERLIBINCS = $(shell for d in $(USERLIBDIRS); do echo -I$$d; done) +INC_PATHS += $(USERLIBINCS) INC_PATHS += -I$(INODIR)/.. CPP_OBJECTS_CORE_EMU = $(CPP_SOURCES_CORE_EMU:.cpp=.cpp.o) $(USERLIBSRCS:.cpp=.cpp.o) $(USERCXXSOURCES:.cpp=.cpp.o) C_OBJECTS_CORE_EMU = $(USERCSOURCES:.c=.c.o) @@ -396,7 +419,7 @@ $(BINDIR)/fullcore.a: $(FULLCORE_OBJECTS_ISOLATED) ifeq ($(INO),) %: - make INO=$@.ino $(BINDIR)/$(abspath $@) + $(MAKE) INO=$@.ino $(BINDIR)/$(abspath $@) else diff --git a/tests/host/common/ArduinoCatch.cpp b/tests/host/common/ArduinoCatch.cpp index 2a3404a931..5308278979 100644 --- a/tests/host/common/ArduinoCatch.cpp +++ b/tests/host/common/ArduinoCatch.cpp @@ -14,6 +14,25 @@ */ #define CATCH_CONFIG_MAIN -#include -#include -#include "Arduino.h" +#include "ArduinoCatch.hpp" + +std::ostream& operator<<(std::ostream& out, const String& str) +{ + out.write(str.c_str(), str.length()); + return out; +} + +namespace Catch +{ + +std::string toString(const String& str) +{ + return std::string(str.begin(), str.length()); +} + +std::string StringMaker::convert(String const& str) +{ + return toString(str); +} + +} // namespace Catch diff --git a/tests/host/common/ArduinoCatch.hpp b/tests/host/common/ArduinoCatch.hpp new file mode 100644 index 0000000000..d30d30e4ec --- /dev/null +++ b/tests/host/common/ArduinoCatch.hpp @@ -0,0 +1,38 @@ +/* + Arduino.cpp - Mocks for common Arduino APIs + Copyright © 2016 Ivan Grokhotkov + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. +*/ + +#include +#include + +#include + +#include "catch.hpp" + +// Since Catch does not know about Arduino types, help it out so we could have these displayed in the tests output + +std::ostream& operator<<(std::ostream&, const String&); + +namespace Catch +{ + +std::string toString(const String&); + +template<> +struct StringMaker +{ + static std::string convert(const String&); +}; + +} // namespace Catch diff --git a/tests/host/common/ArduinoMain.cpp b/tests/host/common/ArduinoMain.cpp index 80e0a0c85b..692ea97b0e 100644 --- a/tests/host/common/ArduinoMain.cpp +++ b/tests/host/common/ArduinoMain.cpp @@ -115,8 +115,14 @@ static int mock_stop_uart(void) static uint8_t mock_read_uart(void) { - uint8_t ch = 0; - return (read(STDIN, &ch, 1) == 1) ? ch : 0; + uint8_t ch = 0; + int ret = read(STDIN, &ch, 1); + if (ret == -1) + { + perror("read(STDIN,1)"); + return 0; + } + return (ret == 1) ? ch : 0; } void help(const char* argv0, int exitcode) @@ -163,6 +169,7 @@ static struct option options[] = { void cleanup() { + mock_stop_udp(); mock_stop_spiffs(); mock_stop_littlefs(); mock_stop_uart(); diff --git a/tests/host/common/ArduinoMainUdp.cpp b/tests/host/common/ArduinoMainUdp.cpp index cfcaacef2a..49d7c091d2 100644 --- a/tests/host/common/ArduinoMainUdp.cpp +++ b/tests/host/common/ArduinoMainUdp.cpp @@ -33,7 +33,7 @@ #include #include -std::map udps; +static std::map udps; void register_udp(int sock, UdpContext* udp) { @@ -58,3 +58,8 @@ void check_incoming_udp() } } } + +void mock_stop_udp() +{ + udps.clear(); +} diff --git a/tests/host/common/ClientContextTools.cpp b/tests/host/common/ClientContextTools.cpp index cf9987fea0..74f66fd6bb 100644 --- a/tests/host/common/ClientContextTools.cpp +++ b/tests/host/common/ClientContextTools.cpp @@ -37,18 +37,27 @@ #include // gethostbyname -err_t dns_gethostbyname(const char* hostname, ip_addr_t* addr, dns_found_callback found, - void* callback_arg) +err_t dns_gethostbyname_addrtype(const char* hostname, ip_addr_t* addr, dns_found_callback, void*, + u8 type) { - (void)callback_arg; - (void)found; - struct hostent* hbn = gethostbyname(hostname); + auto* hbn = gethostbyname(hostname); if (!hbn) return ERR_TIMEOUT; - addr->addr = *(uint32_t*)hbn->h_addr_list[0]; + + uint32_t tmp; + std::memcpy(&tmp, hbn->h_addr_list[0], sizeof(tmp)); + addr->addr = tmp; + return ERR_OK; } +err_t dns_gethostbyname_addrtype(const char* hostname, ip_addr_t* addr, dns_found_callback found, + void* callback_arg) +{ + return dns_gethostbyname_addrtype(hostname, addr, found, callback_arg, + LWIP_DNS_ADDRTYPE_DEFAULT); +} + static struct tcp_pcb mock_tcp_pcb; tcp_pcb* tcp_new(void) { diff --git a/tests/host/common/DhcpServer.cpp b/tests/host/common/DhcpServer.cpp new file mode 100644 index 0000000000..c4f9dd158b --- /dev/null +++ b/tests/host/common/DhcpServer.cpp @@ -0,0 +1,54 @@ +#include +#include + +DhcpServer& getNonOSDhcpServer() +{ + static DhcpServer instance(nullptr); + return instance; +} + +bool DhcpServer::set_dhcps_lease(struct dhcps_lease* please) +{ + (void)please; + return false; +} + +void DhcpServer::end() { } + +bool DhcpServer::begin() +{ + return false; +} + +DhcpServer::DhcpServer(netif*) { } + +DhcpServer::~DhcpServer() +{ + end(); +} + +extern "C" +{ +#include + + bool wifi_softap_dhcps_start(void) + { + return true; + } + + enum dhcp_status wifi_softap_dhcps_status(void) + { + return DHCP_STARTED; + } + + bool wifi_softap_dhcps_stop(void) + { + return true; + } + + bool wifi_softap_set_dhcps_lease(struct dhcps_lease* please) + { + (void)please; + return true; + } +} diff --git a/tests/host/common/MockEsp.cpp b/tests/host/common/MockEsp.cpp index 75c609ef65..18e7be6c83 100644 --- a/tests/host/common/MockEsp.cpp +++ b/tests/host/common/MockEsp.cpp @@ -36,6 +36,9 @@ #include +#include +struct rst_info resetInfo; + unsigned long long operator"" _kHz(unsigned long long x) { return x * 1000; diff --git a/tests/host/common/MockTools.cpp b/tests/host/common/MockTools.cpp index 2d9f79e999..600462feed 100644 --- a/tests/host/common/MockTools.cpp +++ b/tests/host/common/MockTools.cpp @@ -99,9 +99,15 @@ extern "C" } void stack_thunk_dump_stack() { } + void* umm_info(void*, bool) + { + return nullptr; + } + // Thunking macro #define make_stack_thunk(fcnToThunk) -}; + +}; // extern "C" void configTime(int timezone, int daylightOffset_sec, const char* server1, const char* server2, const char* server3) diff --git a/tests/host/common/MockWiFiServer.cpp b/tests/host/common/MockWiFiServer.cpp index f199a2b588..8fb2b91031 100644 --- a/tests/host/common/MockWiFiServer.cpp +++ b/tests/host/common/MockWiFiServer.cpp @@ -61,10 +61,22 @@ WiFiClient WiFiServer::available(uint8_t* status) return accept(); } -WiFiClient WiFiServer::accept() +void WiFiServer::_mockUnclaimed() { if (hasClient()) - return WiFiClient(new ClientContext(serverAccept(pcb2int(_listen_pcb)))); + _unclaimed + = slist_append_tail(_unclaimed, new ClientContext(serverAccept(pcb2int(_listen_pcb)))); +} + +WiFiClient WiFiServer::accept() +{ + _mockUnclaimed(); + if (_unclaimed) + { + auto ctx = _unclaimed; + _unclaimed = _unclaimed->next(); + return WiFiClient(ctx); + } return WiFiClient(); } diff --git a/tests/host/common/MockWiFiServerSocket.cpp b/tests/host/common/MockWiFiServerSocket.cpp index 1dd50b644f..0023c6e2a1 100644 --- a/tests/host/common/MockWiFiServerSocket.cpp +++ b/tests/host/common/MockWiFiServerSocket.cpp @@ -129,7 +129,7 @@ bool WiFiServer::hasClient() void WiFiServer::close() { - if (pcb2int(_listen_pcb) >= 0) + if (pcb2int(_listen_pcb) >= 3) // 0=stdin 1=stdout 2=stderr ::close(pcb2int(_listen_pcb)); _listen_pcb = int2pcb(-1); } diff --git a/tests/host/common/MocklwIP.cpp b/tests/host/common/MocklwIP.cpp index 47b62ebb46..8df8929d98 100644 --- a/tests/host/common/MocklwIP.cpp +++ b/tests/host/common/MocklwIP.cpp @@ -3,6 +3,8 @@ #include "MocklwIP.h" +#include + esp8266::AddressListImplementation::AddressList addrList; extern "C" @@ -57,4 +59,18 @@ extern "C" return &netif0; } + void dns_setserver(u8_t numdns, const ip_addr_t* dnsserver) + { + (void)numdns; + (void)dnsserver; + } + + const ip_addr_t* dns_getserver(u8_t numdns) + { + (void)numdns; + static ip_addr_t addr; + IP4_ADDR(&addr, 127, 0, 0, 1); + return &addr; + } + } // extern "C" diff --git a/tests/host/common/UdpContextSocket.cpp b/tests/host/common/UdpContextSocket.cpp index c598460180..2ffa4eb22f 100644 --- a/tests/host/common/UdpContextSocket.cpp +++ b/tests/host/common/UdpContextSocket.cpp @@ -166,14 +166,14 @@ size_t mockUDPFillInBuf(int sock, char* ccinbuf, size_t& ccinbufsize, uint8_t& a return ccinbufsize += ret; } -size_t mockUDPPeekBytes(int sock, char* dst, size_t usersize, int timeout_ms, char* ccinbuf, - size_t& ccinbufsize) +size_t mockUDPPeekBytes(int sock, char* dst, size_t offset, size_t usersize, int timeout_ms, + char* ccinbuf, size_t& ccinbufsize) { (void)sock; (void)timeout_ms; - if (usersize > CCBUFSIZE) + if (offset + usersize > CCBUFSIZE) fprintf(stderr, MOCK "CCBUFSIZE(%d) should be increased by %zd bytes (-> %zd)\n", CCBUFSIZE, - usersize - CCBUFSIZE, usersize); + offset + usersize - CCBUFSIZE, offset + usersize); size_t retsize = 0; if (ccinbufsize) @@ -183,25 +183,10 @@ size_t mockUDPPeekBytes(int sock, char* dst, size_t usersize, int timeout_ms, ch if (retsize > ccinbufsize) retsize = ccinbufsize; } - memcpy(dst, ccinbuf, retsize); + memcpy(dst, ccinbuf + offset, retsize); return retsize; } -void mockUDPSwallow(size_t copied, char* ccinbuf, size_t& ccinbufsize) -{ - // poor man buffer - memmove(ccinbuf, ccinbuf + copied, ccinbufsize - copied); - ccinbufsize -= copied; -} - -size_t mockUDPRead(int sock, char* dst, size_t size, int timeout_ms, char* ccinbuf, - size_t& ccinbufsize) -{ - size_t copied = mockUDPPeekBytes(sock, dst, size, timeout_ms, ccinbuf, ccinbufsize); - mockUDPSwallow(copied, ccinbuf, ccinbufsize); - return copied; -} - size_t mockUDPWrite(int sock, const uint8_t* data, size_t size, int timeout_ms, uint32_t ipv4, uint16_t port) { diff --git a/tests/host/common/include/UdpContext.h b/tests/host/common/include/UdpContext.h index 88cb46f9ae..e8ae9461e9 100644 --- a/tests/host/common/include/UdpContext.h +++ b/tests/host/common/include/UdpContext.h @@ -117,12 +117,12 @@ class UdpContext size_t getSize() { - return _inbufsize; + return _inbufsize - _inoffset; } size_t tell() const { - return 0; + return _inoffset; } void seek(const size_t pos) @@ -132,7 +132,7 @@ class UdpContext mockverbose("UDPContext::seek too far (%zd >= %zd)\n", pos, _inbufsize); exit(EXIT_FAILURE); } - mockUDPSwallow(pos, _inbuf, _inbufsize); + _inoffset = pos; } bool isValidOffset(const size_t pos) const @@ -165,6 +165,7 @@ class UdpContext bool next() { _inbufsize = 0; + _inoffset = 0; mockUDPFillInBuf(_sock, _inbuf, _inbufsize, addrsize, addr, _dstport); if (_inbufsize > 0) { @@ -182,13 +183,16 @@ class UdpContext size_t read(char* dst, size_t size) { - return mockUDPRead(_sock, dst, size, _timeout_ms, _inbuf, _inbufsize); + //return mockUDPRead(_sock, dst, size, _timeout_ms, _inbuf, _inbufsize); + auto ret = mockUDPPeekBytes(_sock, dst, _inoffset, size, _timeout_ms, _inbuf, _inbufsize); + _inoffset += ret; + return ret; } int peek() { char c; - return mockUDPPeekBytes(_sock, &c, 1, _timeout_ms, _inbuf, _inbufsize) ?: -1; + return mockUDPPeekBytes(_sock, &c, _inoffset, 1, _timeout_ms, _inbuf, _inbufsize) ?: -1; } void flush() @@ -280,6 +284,7 @@ class UdpContext char _inbuf[CCBUFSIZE]; size_t _inbufsize = 0; + size_t _inoffset = 0; char _outbuf[CCBUFSIZE]; size_t _outbufsize = 0; diff --git a/tests/host/common/mock.h b/tests/host/common/mock.h index a8dc3e2bdc..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 @@ -160,16 +165,14 @@ int mockUDPSocket(); bool mockUDPListen(int sock, uint32_t dstaddr, uint16_t port, uint32_t mcast = 0); size_t mockUDPFillInBuf(int sock, char* ccinbuf, size_t& ccinbufsize, uint8_t& addrsize, uint8_t addr[16], uint16_t& port); -size_t mockUDPPeekBytes(int sock, char* dst, size_t usersize, int timeout_ms, char* ccinbuf, - size_t& ccinbufsize); -size_t mockUDPRead(int sock, char* dst, size_t size, int timeout_ms, char* ccinbuf, - size_t& ccinbufsize); +size_t mockUDPPeekBytes(int sock, char* dst, size_t offset, size_t usersize, int timeout_ms, + char* ccinbuf, size_t& ccinbufsize); size_t mockUDPWrite(int sock, const uint8_t* data, size_t size, int timeout_ms, uint32_t ipv4, uint16_t port); -void mockUDPSwallow(size_t copied, char* ccinbuf, size_t& ccinbufsize); class UdpContext; void register_udp(int sock, UdpContext* udp = nullptr); +void mock_stop_udp(); // diff --git a/tests/host/common/noniso.c b/tests/host/common/noniso.c index eacb2b14fd..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; @@ -65,44 +66,22 @@ char* itoa(int value, char* result, int base) *result = 0; return result; } - if (base != 10) - { - return utoa((unsigned)value, result, base); - } - char* out = result; - int quotient = abs(value); + unsigned uvalue; + char* out = result; - do + // after this point we convert the value to unsigned and go to the utoa + // only base10 gets minus sign in the front, adhering to the newlib implementation + if ((base == 10) && (value < 0)) { - const int tmp = quotient / base; - *out = "0123456789abcdef"[quotient - (tmp * base)]; - ++out; - quotient = tmp; - } while (quotient); - - // Apply negative sign - if (value < 0) - *out++ = '-'; - - reverse(result, out); - *out = 0; - return result; -} - -int atoi(const char* s) -{ - return (int)atol(s); -} - -long atol(const char* s) -{ - char* tmp; - return strtol(s, &tmp, 10); -} + *result++ = '-'; + uvalue = (unsigned)-value; + } + else + { + uvalue = (unsigned)value; + } -double atof(const char* s) -{ - char* tmp; - return strtod(s, &tmp); + utoa(uvalue, result, base); + return out; } 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/common/user_interface.cpp b/tests/host/common/user_interface.cpp index 05f90c704a..26144aec1f 100644 --- a/tests/host/common/user_interface.cpp +++ b/tests/host/common/user_interface.cpp @@ -41,47 +41,6 @@ #include "MocklwIP.h" -#include - -bool DhcpServer::set_dhcps_lease(struct dhcps_lease* please) -{ - (void)please; - return false; -} - -bool DhcpServer::set_dhcps_lease_time(uint32 minute) -{ - (void)minute; - return false; -} - -bool DhcpServer::set_dhcps_offer_option(uint8 level, void* optarg) -{ - (void)level; - (void)optarg; - return false; -} - -void DhcpServer::end() { } - -bool DhcpServer::begin(struct ip_info* info) -{ - (void)info; - return false; -} - -DhcpServer::DhcpServer(netif* netif) -{ - (void)netif; -} - -DhcpServer::~DhcpServer() -{ - end(); -} - -DhcpServer dhcpSoftAP(nullptr); - extern "C" { #include @@ -126,8 +85,12 @@ extern "C" config->bssid[i] = i; config->threshold.rssi = 1; config->threshold.authmode = AUTH_WPA_PSK; -#ifdef NONOSDK3V0 +#if (NONOSDK >= (0x30000)) config->open_and_wep_mode_disable = true; +#endif +#if (NONOSDK >= (0x30200)) + config->channel = 1; + config->all_channel_scan = true; #endif return true; } @@ -177,12 +140,11 @@ extern "C" for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) { mockverbose("host: interface: %s", ifa->ifa_name); - if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET // ip_info is IPv4 only - ) + if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET) // ip_info is IPv4 only { auto test_ipv4 = lwip_ntohl(*(uint32_t*)&((struct sockaddr_in*)ifa->ifa_addr)->sin_addr); - mockverbose(" IPV4 (0x%08lx)", test_ipv4); + mockverbose(" IPV4 (0x%08x)", test_ipv4); if ((test_ipv4 & 0xff000000) == 0x7f000000) // 127./8 mockverbose(" (local, ignored)"); @@ -220,14 +182,14 @@ extern "C" info->ip.addr = ipv4; info->netmask.addr = mask; info->gw.addr = ipv4; - - netif0.ip_addr.addr = ipv4; - netif0.netmask.addr = mask; - netif0.gw.addr = ipv4; - netif0.flags = NETIF_FLAG_IGMP | NETIF_FLAG_UP | NETIF_FLAG_LINK_UP; - netif0.next = nullptr; } + netif0.ip_addr.addr = ipv4; + netif0.netmask.addr = mask; + netif0.gw.addr = ipv4; + netif0.flags = NETIF_FLAG_IGMP | NETIF_FLAG_UP | NETIF_FLAG_LINK_UP; + netif0.next = nullptr; + return true; } @@ -253,7 +215,7 @@ extern "C" return STATION_MODE; } -#ifdef NONOSDK3V0 +#if (NONOSDK >= (0x30000)) sleep_level_t wifi_get_sleep_level(void) { @@ -309,7 +271,7 @@ extern "C" return true; } -#ifdef NONOSDK3V0 +#if (NONOSDK >= (0x30000)) bool wifi_set_sleep_level(sleep_level_t level) { @@ -350,10 +312,12 @@ extern "C" return wifi_station_get_config(config); } - char wifi_station_get_hostname_str[128]; - const char* wifi_station_get_hostname(void) + extern "C" char* wifi_station_hostname; // exists in nonosdk + char wifi_station_hostname_str[33] { "esposix" }; + char* wifi_station_hostname = wifi_station_hostname_str; + const char* wifi_station_get_hostname(void) { - return strcpy(wifi_station_get_hostname_str, "esposix"); + return wifi_station_hostname; } bool wifi_station_get_reconnect_policy() @@ -400,21 +364,6 @@ extern "C" (void)max_tpw; } - bool wifi_softap_dhcps_start(void) - { - return true; - } - - enum dhcp_status wifi_softap_dhcps_status(void) - { - return DHCP_STARTED; - } - - bool wifi_softap_dhcps_stop(void) - { - return true; - } - bool wifi_softap_get_config(struct softap_config* config) { strcpy((char*)config->ssid, "apssid"); @@ -450,25 +399,6 @@ extern "C" return true; } - bool wifi_softap_set_dhcps_lease(struct dhcps_lease* please) - { - (void)please; - return true; - } - - bool wifi_softap_set_dhcps_lease_time(uint32 minute) - { - (void)minute; - return true; - } - - bool wifi_softap_set_dhcps_offer_option(uint8 level, void* optarg) - { - (void)level; - (void)optarg; - return true; - } - bool wifi_station_scan(struct scan_config* config, scan_done_cb_t cb) { (void)config; @@ -491,19 +421,6 @@ extern "C" (void)intr; } - void dns_setserver(u8_t numdns, ip_addr_t* dnsserver) - { - (void)numdns; - (void)dnsserver; - } - - ip_addr_t dns_getserver(u8_t numdns) - { - (void)numdns; - ip_addr_t addr = { 0x7f000001 }; - return addr; - } - #include bool smartconfig_start(sc_callback_t cb, ...) { diff --git a/tests/host/core/test_string.cpp b/tests/host/core/test_string.cpp index 1a235ea92a..92b37e4279 100644 --- a/tests/host/core/test_string.cpp +++ b/tests/host/core/test_string.cpp @@ -13,12 +13,14 @@ all copies or substantial portions of the Software. */ -#include -#include -#include -#include +#include #include +#include +#include +#include +#include + TEST_CASE("String::move", "[core][String]") { const char buffer[] = "this string goes over the sso limit"; @@ -117,8 +119,10 @@ TEST_CASE("String concantenation", "[core][String]") str += "bcde"; str += str; str += 987; - str += (int)INT_MAX; - str += (int)INT_MIN; + REQUIRE(str == "abcdeabcde987"); + str += std::numeric_limits::max(); + REQUIRE(str == "abcdeabcde9872147483647"); + str += std::numeric_limits::min(); REQUIRE(str == "abcdeabcde9872147483647-2147483648"); str += (unsigned char)69; REQUIRE(str == "abcdeabcde9872147483647-214748364869"); diff --git a/tests/host/sys/pgmspace.h b/tests/host/sys/pgmspace.h index 6ccd881a8c..ac60cb15be 100644 --- a/tests/host/sys/pgmspace.h +++ b/tests/host/sys/pgmspace.h @@ -75,10 +75,13 @@ inline int vsnprintf_P(char* str, size_t size, const char* format, va_list ap) #define memmove_P memmove #define strncpy_P strncpy #define strcmp_P strcmp +#define strcasecmp_P strcasecmp #define memccpy_P memccpy #define snprintf_P snprintf #define sprintf_P sprintf #define strncmp_P strncmp +#define strncasecmp_P strncasecmp #define strcat_P strcat +#define memcmp_P memcmp #endif diff --git a/tests/platformio.sh b/tests/platformio.sh deleted file mode 100755 index b557251273..0000000000 --- a/tests/platformio.sh +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env bash - -cache_dir=$(mktemp -d) - -source "$TRAVIS_BUILD_DIR"/tests/common.sh - -function install_platformio() -{ - pip3 install -U platformio - platformio platform install "https://github.com/platformio/platform-espressif8266.git" - # Overwrite toolchain with this PR's toolset. Probably better way to do this - ( cd $TRAVIS_BUILD_DIR/tools && python3 get.py -q ) - mv $TRAVIS_BUILD_DIR/tools/xtensa-lx106-elf ~/.platformio/packages/toolchain-xtensa-latest - mv ~/.platformio/packages/toolchain-xtensa/package.json ~/.platformio/packages/toolchain-xtensa/.piopm ~/.platformio/packages/toolchain-xtensa-latest/ - python -c "import json; import os; fp=open(os.path.expanduser('~/.platformio/platforms/espressif8266/platform.json'), 'r+'); data=json.load(fp); data['packages']['framework-arduinoespressif8266']['version'] = '*'; del data['packages']['framework-arduinoespressif8266']['owner'];fp.seek(0); fp.truncate(); json.dump(data, fp); fp.close()" - ln -sf $TRAVIS_BUILD_DIR ~/.platformio/packages/framework-arduinoespressif8266 - # Install dependencies: - # - esp8266/examples/ConfigFile - pio lib --global install "ArduinoJson@^6.11.0" -} - -function build_sketches_with_platformio() -{ - set +e - local srcpath=$1 - local build_arg=$2 - local build_mod=$3 - local build_rem=$4 - local sketches=$(find $srcpath -name *.ino | sort) - local testcnt=0 - for sketch in $sketches; do - testcnt=$(( ($testcnt + 1) % $build_mod )) - if [ $testcnt -ne $build_rem ]; then - continue # Not ours to do - fi - local sketchdir=$(dirname $sketch) - local sketchdirname=$(basename $sketchdir) - local sketchname=$(basename $sketch) - if [[ "${sketchdirname}.ino" != "$sketchname" ]]; then - echo "Skipping $sketch, because it is not the main sketch file"; - continue - fi; - if [[ -f "$sketchdir/.test.skip" ]]; then - echo -e "\n ------------ Skipping $sketch ------------ \n"; - continue - fi - if [[ $(skip_ino $sketch) = 1 ]]; then - echo -e "\n ------------ Skipping $sketch ------------ \n"; - continue - fi - local build_cmd="pio ci $sketchdir $build_arg" - echo -e "\n ------------ Building $sketch ------------ \n"; - echo "$build_cmd" - time ($build_cmd >build.log) - local result=$? - if [ $result -ne 0 ]; then - echo "Build failed ($1)" - echo "Build log:" - cat build.log - set -e - return $result - fi - rm build.log - done - set -e -} - -if [ -z "$BUILD_PARITY" ]; then - mod=1 - rem=0 -elif [ "$BUILD_PARITY" = "even" ]; then - mod=2 - rem=0 -elif [ "$BUILD_PARITY" = "odd" ]; then - mod=2 - rem=1 -fi - -install_platformio -build_sketches_with_platformio "$TRAVIS_BUILD_DIR"/libraries "--board nodemcuv2 --verbose" "$mod" "$rem" - -rm -rf "$cache_dir" - diff --git a/tests/restyle.py b/tests/restyle.py new file mode 100755 index 0000000000..78a8816000 --- /dev/null +++ b/tests/restyle.py @@ -0,0 +1,290 @@ +#!/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/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="http://github.com/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="http://github.com/store_true") + assert_.add_argument("--with-errors", action="http://github.com/store_true") + assert_.add_argument("--with-summary", action="http://github.com/store_true") + + args = parser.parse_args() + args.func(args) diff --git a/tests/restyle.sh b/tests/restyle.sh index 5460ca25a0..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 @@ -7,58 +7,14 @@ root=$(git rev-parse --show-toplevel) test -d ${root}/cores/esp8266 test -d ${root}/libraries -# allow `env CLANG_FORMAT=clang-format-13`, or some other version -# default to v13, latest stable version from https://apt.llvm.org -CLANG_FORMAT=${CLANG_FORMAT:-clang-format-13} - -# TODO: waiting for llvm-14 to allow --style=file: -makeClangFormatStyle() { - python3 -c 'import sys,yaml; sys.stdout.write(yaml.dump(yaml.safe_load(open(sys.argv[1], "r")), default_flow_style=True)); sys.stdout.flush();' $1 -} - -######################################### -# '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 +# allow `env CLANG_FORMAT=clang-format-N`, or some other version +CLANG_FORMAT=${CLANG_FORMAT:-clang-format} cd $root +python $root/tests/restyle.py format --clang-format=$CLANG_FORMAT preset --include core --include arduino -style=$(makeClangFormatStyle ${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="$style" -i {} \; - else - $CLANG_FORMAT --verbose --style="$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) - -style=$(makeClangFormatStyle ${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="$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/run_CI_locally.sh b/tests/run_CI_locally.sh index ee9dc39cb0..24d05041d6 100755 --- a/tests/run_CI_locally.sh +++ b/tests/run_CI_locally.sh @@ -82,40 +82,47 @@ done git submodule update --init export HOME="${TMPCI}" -export TRAVIS_BUILD_DIR="${TMPCI}" +export ESP8266_ARDUINO_BUILD_DIR="${TMPCI}" export BUILD_TYPE="$BUILD_TYPE" if [ "$BUILD_TYPE" = "build" ]; then tests/build.sh + elif [ "$BUILD_TYPE" = "build_even" ]; then - BUILD_PARITY=even tests/build.sh + tests/build.sh even + elif [ "$BUILD_TYPE" = "build_odd" ]; then - BUILD_PARITY=odd tests/build.sh + tests/build.sh odd elif [ "$BUILD_TYPE" = "debug_even" ]; then - BUILD_PARITY=even tests/debug.sh + env ESP8266_ARDUINO_DEBUG=debug tests/build.sh even + elif [ "$BUILD_TYPE" = "debug_odd" ]; then - BUILD_PARITY=odd tests/debug.sh + env ESP8266_ARDUINO_DEBUG=debug tests/build.sh odd elif [ "$BUILD_TYPE" = "build6" ]; then - tests/build6.sh + env ESP8266_ARDUINO_LWIP=lm6f tests/build.sh + elif [ "$BUILD_TYPE" = "build6_even" ]; then - BUILD_PARITY=even tests/build6.sh + env ESP8266_ARDUINO_LWIP=lm6f tests/build.sh even + elif [ "$BUILD_TYPE" = "build6_odd" ]; then - BUILD_PARITY=odd tests/build6.sh + env ESP8266_ARDUINO_LWIP=lm6f tests/build.sh odd elif [ "$BUILD_TYPE" = "platformio" ]; then - tests/platformio.sh + env ESP8266_ARDUINO_BUILDER=platformio tests/build.sh + elif [ "$BUILD_TYPE" = "platformio_even" ]; then - BUILD_PARITY=even tests/platformio.sh + env ESP8266_ARDUINO_BUILDER=platformio tests/build.sh even + elif [ "$BUILD_TYPE" = "platformio_odd" ]; then - BUILD_PARITY=odd tests/platformio.sh + env ESP8266_ARDUINO_BUILDER=platformio tests/build.sh odd elif [ "$BUILD_TYPE" = host ]; then tests/ci/host_test.sh elif [ "$BUILD_TYPE" = style ]; then - tests/ci/check_restyle.sh + tests/ci/style_check.sh tests/restyle.sh else diff --git a/tests/sanity_check.sh b/tests/sanity_check.sh new file mode 100755 index 0000000000..a4754a0ed4 --- /dev/null +++ b/tests/sanity_check.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash + +root=$(git rev-parse --show-toplevel) +source "$root/tests/common.sh" + +pushd "$root"/tools +python3 get.py -q + +popd +pushd "$cache_dir" + +gcc="$root/tools/xtensa-lx106-elf/bin/xtensa-lx106-elf-gcc"\ +" -I$root/cores/esp8266"\ +" -I$root/tools/sdk/include"\ +" -I$root/variants/generic"\ +" -I$root/tools/sdk/libc/xtensa-lx106-elf" + +$gcc --verbose + +set -v -x + +cat << EOF > arduino.c +#include +EOF + +$gcc -c arduino.c + +cat << EOF > coredecls.c +#include +EOF + +$gcc -c coredecls.c + +cat << EOF > features.c +#include +EOF + +$gcc -c features.c + +cat << EOF > sdk.c +#include +EOF + +$gcc -c sdk.c 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/TZupdate.sh b/tools/TZupdate.sh deleted file mode 100755 index 3c6f41e156..0000000000 --- a/tools/TZupdate.sh +++ /dev/null @@ -1,72 +0,0 @@ -#!/bin/sh - -# this shell script refreshes world timezone definitions in -# cores/esp8266/TZ.h -# -# to run it, use: -# /path/to/TZupdate.sh -# tools/TZupdate.sh -# ./TZupdate.sh - -dir=$(cd ${0%/*} 2>/dev/null; pwd) -base=${0##*/} - -csv=https://raw.githubusercontent.com/nayarsystems/posix_tz_db/master/zones.csv - -set -e - -tz_tmpdir=$(mktemp -d) -trap 'rm -r $tz_tmpdir' EXIT - -input=$tz_tmpdir/zones.csv -names=$tz_tmpdir/names.txt -values=$tz_tmpdir/values.txt - -wget -O $input $csv || curl $csv > $input - -sed -e 's/^[^,]*,//g' -e 's,^,PSTR(,g' -e 's,$,),g' < $input > $values -sed -e 's/^\([^,]*\),.*/#define TZ_\1/g' -e 's,["],,g' < $input | tr '/\-+' '_mp' > $names - -( - -cat << EOF - -// autogenerated from $csv -// by script /tools/${base} -// $(date -u) -// -// This database is autogenerated from IANA timezone database -// ${csv} -// (using https://www.iana.org/time-zones) -// and can be updated on demand in this repository -// or by yourself using the above script - -#ifndef TZDB_H -#define TZDB_H - -EOF - -paste $names $values - -cat << EOF - -#endif // TZDB_H -EOF - -) > $tz_tmpdir/TZ.h - -backup=$(date +%s) -mv ${dir}/../cores/esp8266/TZ.h ${dir}/../cores/esp8266/TZ.h.$backup -mv $tz_tmpdir/TZ.h ${dir}/../cores/esp8266/TZ.h - -cat << EOF - -Done: - '${dir}/../cores/esp8266/TZ.h' is updated - -Diff: -----8<-------8<------8<--- -$(diff -u ${dir}/../cores/esp8266/TZ.h.$backup ${dir}/../cores/esp8266/TZ.h) ---->8----->8------>8------ - -EOF diff --git a/tools/boards.txt.py b/tools/boards.txt.py index 89efe7f6ca..0ccfa8f4d9 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.', ] }), +( 'mercury1', { + 'name': 'Mercury 1.0', + 'opts': { + '.build.board': 'mercury', + '.build.variant': 'mercury_v1', + }, + 'macro': [ + 'resetmethod_nodemcu', + 'flashmode_dio', + 'flashfreq_40', + '4M', + ], + 'desc': [ 'Based on ESP8266, Mercury is board developed by Ralio Technologies. Board supports on motor drivers and direct-connect feature for various endpoints.', + '', + 'Product page: https://www.raliotech.com', + ], + }), ( 'phoenix_v1', { 'name': 'Phoenix 1.0', 'opts': { @@ -621,6 +647,21 @@ 'serial': '921', 'desc': [ 'Product page: https://www.wemos.cc/' ], }), + ( 'd1_wroom_02', { + 'name': 'LOLIN(WEMOS) D1 ESP-WROOM-02', + 'opts': { + '.build.board': 'ESP8266_WEMOS_D1WROOM02', + '.build.variant': 'd1_mini', + }, + 'macro': [ + 'resetmethod_nodemcu', + 'flashmode_dio', + 'flashfreq_26', + '2M', + ], + 'serial': '921', + 'desc': [ 'No real product pages. See: https://www.instructables.com/How-to-Use-Wemos-ESP-Wroom-02-D1-Mini-WiFi-Module-/ or https://www.arduino-tech.com/wemos-esp-wroom-02-mainboard-d1-mini-wifi-module-esp826618650-battery/ ' ], + }), ( 'd1_mini_clone', { 'name': 'LOLIN(WEMOS) D1 mini (clone)', 'opts': { @@ -741,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%20(Please%20see%20pg.%208)', + '* 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', { @@ -1030,6 +1067,7 @@ ( '.build.core', 'esp8266' ), ( '.build.variant', 'generic' ), ( '.build.spiffs_pagesize', '256' ), + ( '.build.debug_optim', '' ), ( '.build.debug_port', '' ), ( '.build.debug_level', '' ), ]), @@ -1085,6 +1123,10 @@ ( '.menu.FlashFreq.26.build.flash_freq', '26' ), ]), + 'flashfreq_26': collections.OrderedDict([ + ( '.build.flash_freq', '26' ), + ]), + 'flashfreq_40': collections.OrderedDict([ ( '.build.flash_freq', '40' ), ]), @@ -1341,6 +1383,12 @@ def all_debug (): ( '.menu.dbg.Serial1.build.debug_port', '-DDEBUG_ESP_PORT=Serial1' ), ( '.menu.lvl.None____', 'None' ), ( '.menu.lvl.None____.build.debug_level', '' ), + ( '.menu.optim.Smallest', 'None' ), + ( '.menu.optim.Smallest.build.debug_optim', '-Os' ), + ( '.menu.optim.Lite', 'Lite' ), + ( '.menu.optim.Lite.build.debug_optim', '-Os -fno-optimize-sibling-calls' ), + ( '.menu.optim.Full', 'Optimum' ), + ( '.menu.optim.Full.build.debug_optim', '-Og' ), ]) for optlist in options: @@ -1600,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, @@ -1649,8 +1697,19 @@ def sdk (): ('.menu.sdk.nonosdk_190313.build.sdk', 'NONOSDK22x_190313'), ('.menu.sdk.nonosdk221', 'nonos-sdk 2.2.1 (legacy)'), ('.menu.sdk.nonosdk221.build.sdk', 'NONOSDK221'), - ('.menu.sdk.nonosdk3v0', 'nonos-sdk pre-3 (180626 known issues)'), - ('.menu.sdk.nonosdk3v0.build.sdk', 'NONOSDK3V0'), + ('.menu.sdk.nonosdk305', 'nonos-sdk 3.0.5 (experimental)'), + ('.menu.sdk.nonosdk305.build.sdk', 'NONOSDK305'), + ]) + } + +################################################################ + +def float_in_iram (): + return { 'iramfloat': collections.OrderedDict([ + ('.menu.iramfloat.no', 'in IROM'), + ('.menu.iramfloat.no.build.iramfloat', '-DFP_IN_IROM'), + ('.menu.iramfloat.yes', 'allowed in ISR'), + ('.menu.iramfloat.yes.build.iramfloat', '-DFP_IN_IRAM'), ]) } @@ -1683,6 +1742,7 @@ def all_boards (): macros.update(led('led', led_default, range(0,led_max+1))) macros.update(led('led216', 2, { 16 })) macros.update(sdk()) + macros.update(float_in_iram()) if boardfilteropt or excludeboards: print('#') @@ -1721,12 +1781,14 @@ def all_boards (): print('menu.ResetMethod=Reset Method') print('menu.dbg=Debug port') print('menu.lvl=Debug Level') + print('menu.optim=Debug Optimization') print('menu.ip=lwIP Variant') print('menu.vt=VTables') print('menu.exception=C++ Exceptions') print('menu.stacksmash=Stack Protection') print('menu.wipe=Erase Flash') - print('menu.sdk=Espressif FW') + print('menu.sdk=NONOS SDK Version') + print('menu.iramfloat=Floating Point operations') print('menu.ssl=SSL Support') print('menu.mmu=MMU') print('menu.non32xfer=Non-32-Bit Access') @@ -1764,6 +1826,7 @@ def all_boards (): macrolist += speeds[default_speed] macrolist += [ 'autoflash' ] + macrolist += [ 'iramfloat' ] for block in macrolist: for optname in macros[block]: diff --git a/tools/build.py b/tools/build.py index 791ac30e2a..dd494ef747 100755 --- a/tools/build.py +++ b/tools/build.py @@ -30,34 +30,25 @@ import shutil -# Arduino-builder needs forward-slash paths for passed in params or it cannot -# launch the needed toolset. -def windowsize_paths(l): - """Convert forward-slash paths to backslash paths referenced from C:""" - out = [] - for i in l: - if i.startswith('/'): - i = 'C:' + i - out += [i.replace('/', '\\')] - return out - -def compile(tmp_dir, sketch, cache, tools_dir, hardware_dir, ide_path, f, args): +def compile(tmp_dir, sketch, cache, ide_path, f, args): cmd = [] - cmd += [ide_path + '/arduino-builder'] + cmd += [os.path.join(ide_path, 'arduino-builder')] cmd += ['-compile', '-logger=human'] cmd += ['-build-path', tmp_dir] - cmd += ['-tools', ide_path + '/tools-builder'] if cache != "": cmd += ['-build-cache', cache ] - if args.library_path: - for lib_dir in args.library_path: - cmd += ['-libraries', lib_dir] - cmd += ['-hardware', ide_path + '/hardware'] - if args.hardware_dir: - for hw_dir in args.hardware_dir: - cmd += ['-hardware', hw_dir] - else: - cmd += ['-hardware', hardware_dir] + + cmd += ['-tools', os.path.join(ide_path, 'tools-builder')] + cmd += ['-hardware', os.path.join(ide_path, 'hardware')] + + flag_paths = [ + ['-tools', args.tool_path], + ['-libraries', args.library_path], + ['-hardware', args.hardware_path]] + for flag, paths in flag_paths: + for path in paths or []: + cmd += [flag, path] + # Debug=Serial,DebugLevel=Core____ fqbn = '-fqbn=esp8266com:esp8266:{board_name}:' \ 'xtal={cpu_freq},' \ @@ -72,16 +63,13 @@ def compile(tmp_dir, sketch, cache, tools_dir, hardware_dir, ide_path, f, args): if args.waveform_phase: fqbn += ',waveform=phase' cmd += [fqbn] - cmd += ['-built-in-libraries', ide_path + '/libraries'] - cmd += ['-ide-version=10607'] + cmd += ['-built-in-libraries', os.path.join(ide_path, 'libraries')] + cmd += ['-ide-version=10802'] cmd += ['-warnings={warnings}'.format(**vars(args))] if args.verbose: cmd += ['-verbose'] cmd += [sketch] - if platform.system() == "Windows": - cmd = windowsize_paths(cmd) - if args.verbose: print('Building: ' + " ".join(cmd), file=f) @@ -95,12 +83,14 @@ def parse_args(): action='http://github.com/store_true') parser.add_argument('-i', '--ide_path', help='Arduino IDE path') parser.add_argument('-p', '--build_path', help='Build directory') - parser.add_argument('-l', '--library_path', help='Additional library path', + parser.add_argument('-t', '--tool_path', help='Additional tool path', action='http://github.com/append') - parser.add_argument('-d', '--hardware_dir', help='Additional hardware path', + parser.add_argument('-d', '--hardware_path', help='Additional hardware path', + action='http://github.com/append') + parser.add_argument('-l', '--library_path', help='Additional library path', action='http://github.com/append') parser.add_argument('-b', '--board_name', help='Board name', default='generic') - parser.add_argument('-s', '--flash_size', help='Flash size', default='512K64', + parser.add_argument('-s', '--flash_size', help='Flash size', default='4M1M', choices=['512K0', '512K64', '1M512', '4M1M', '4M3M']) parser.add_argument('-f', '--cpu_freq', help='CPU frequency', default=80, choices=[80, 160], type=int) @@ -112,7 +102,7 @@ def parse_args(): default='none', choices=['none', 'all', 'more']) parser.add_argument('-o', '--output_binary', help='File name for output binary') parser.add_argument('-k', '--keep', action='http://github.com/store_true', - help='Don\'t delete temporary build directory') + help="Don't delete temporary build directory") parser.add_argument('--flash_freq', help='Flash frequency', default=40, type=int, choices=[40, 80]) parser.add_argument('--debug_port', help='Debug port', @@ -121,6 +111,9 @@ def parse_args(): help='Select waveform locked on phase') parser.add_argument('--debug_level', help='Debug level') parser.add_argument('--build_cache', help='Build directory to cache core.a', default='') + parser.add_argument('--log', nargs='?', help='Redirect output to a file', + type=argparse.FileType('w'), + default=sys.stdout) parser.add_argument('sketch_path', help='Sketch file path') return parser.parse_args() @@ -128,25 +121,17 @@ def main(): args = parse_args() ide_path = args.ide_path - if not ide_path: - ide_path = os.environ.get('ARDUINO_IDE_PATH') - if not ide_path: - print("Please specify Arduino IDE path via --ide_path option" - "or ARDUINO_IDE_PATH environment variable.", file=sys.stderr) - return 2 - sketch_path = args.sketch_path tmp_dir = args.build_path + created_tmp_dir = False if not tmp_dir: tmp_dir = tempfile.mkdtemp() created_tmp_dir = True - tools_dir = os.path.dirname(os.path.realpath(__file__)) + '/../tools' - # this is not the correct hardware folder to add. - hardware_dir = os.path.dirname(os.path.realpath(__file__)) + '/../cores' + file = os.path.realpath(__file__) - output_name = tmp_dir + '/' + os.path.basename(sketch_path) + '.bin' + output_name = os.path.join(tmp_dir, f'{os.path.basename(sketch_path)}.bin') if args.verbose: print("Sketch: ", sketch_path) @@ -154,12 +139,7 @@ def main(): print("Cache dir: ", args.build_cache) print("Output: ", output_name) - if args.verbose: - f = sys.stdout - else: - f = open(tmp_dir + '/build.log', 'w') - - res = compile(tmp_dir, sketch_path, args.build_cache, tools_dir, hardware_dir, ide_path, f, args) + res = compile(tmp_dir, sketch_path, args.build_cache, ide_path, args.log, args) if res != 0: return res 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 new file mode 100755 index 0000000000..b1f1706767 --- /dev/null +++ b/tools/decoder.py @@ -0,0 +1,222 @@ +#!/usr/bin/env python3 + +# Baseline code from https://github.com/me-no-dev/EspExceptionDecoder by Hristo Gochkov (@me-no-dev) +# - https://github.com/me-no-dev/EspExceptionDecoder/blob/master/src/EspExceptionDecoder.java +# Stack line detection from https://github.com/platformio/platform-espressif8266/ monitor exception filter by Vojtěch Boček (@Tasssadar) +# - https://github.com/platformio/platform-espressif8266/commits?author=Tasssadar + +import os +import argparse +import sys +import re +import subprocess +import shutil + +# https://github.com/me-no-dev/EspExceptionDecoder/blob/349d17e4c9896306e2c00b4932be3ba510cad208/src/EspExceptionDecoder.java#L59-L90 +EXCEPTION_CODES = ( + "Illegal instruction", + "SYSCALL instruction", + "InstructionFetchError: Processor internal physical address or data error during " + "instruction fetch", + "LoadStoreError: Processor internal physical address or data error during load or store", + "Level1Interrupt: Level-1 interrupt as indicated by set level-1 bits in " + "the INTERRUPT register", + "Alloca: MOVSP instruction, if caller's registers are not in the register file", + "IntegerDivideByZero: QUOS, QUOU, REMS, or REMU divisor operand is zero", + "reserved", + "Privileged: Attempt to execute a privileged operation when CRING ? 0", + "LoadStoreAlignmentCause: Load or store to an unaligned address", + "reserved", + "reserved", + "InstrPIFDataError: PIF data error during instruction fetch", + "LoadStorePIFDataError: Synchronous PIF data error during LoadStore access", + "InstrPIFAddrError: PIF address error during instruction fetch", + "LoadStorePIFAddrError: Synchronous PIF address error during LoadStore access", + "InstTLBMiss: Error during Instruction TLB refill", + "InstTLBMultiHit: Multiple instruction TLB entries matched", + "InstFetchPrivilege: An instruction fetch referenced a virtual address at a ring level " + "less than CRING", + "reserved", + "InstFetchProhibited: An instruction fetch referenced a page mapped with an attribute " + "that does not permit instruction fetch", + "reserved", + "reserved", + "reserved", + "LoadStoreTLBMiss: Error during TLB refill for a load or store", + "LoadStoreTLBMultiHit: Multiple TLB entries matched for a load or store", + "LoadStorePrivilege: A load or store referenced a virtual address at a ring level " + "less than CRING", + "reserved", + "LoadProhibited: A load referenced a page mapped with an attribute that does not " + "permit loads", + "StoreProhibited: A store referenced a page mapped with an attribute that does not " + "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`? +def addresses_gdb(gdb, elf, addresses): + cmd = [gdb, "--batch"] + for address in addresses: + if not address.startswith("0x"): + address = f"0x{address}" + cmd.extend(["--ex", f"info line *{address}"]) + cmd.append(elf) + + with subprocess.Popen(cmd, stdout=subprocess.PIPE, universal_newlines=True) as proc: + for line in proc.stdout.readlines(): + if "No line number" in line: + continue + yield line.strip() + + +# original approach using addr2line, which is pretty enough already +def addresses_addr2line(addr2line, elf, addresses): + cmd = [ + addr2line, + "--addresses", + "--inlines", + "--functions", + "--pretty-print", + "--demangle", + "--exe", + elf, + ] + + for address in addresses: + if not address.startswith("0x"): + address = f"0x{address}" + cmd.append(address) + + with subprocess.Popen(cmd, stdout=subprocess.PIPE, universal_newlines=True) as proc: + for line in proc.stdout.readlines(): + if "??:0" in line: + continue + yield line.strip() + + +def decode_lines(format_addresses, elf, lines): + 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}") + + 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=" + + # either print everything as-is, or cache current string and dump after stack contents end + last_stack = None + stack_addresses = {} + + in_stack = False + + def print_all_addresses(addresses): + for ctx, addrs in addresses.items(): + print() + print(ctx) + for formatted in format_addresses(elf, addrs): + print(formatted) + return dict() + + def format_address(address): + return "\n".join(format_addresses(elf, [address])) + + for line in lines: + # ctx could happen multiple times. for the 2nd one, reset list + # ctx: bearssl *or* ctx: cont *or* ctx: sys *or* ctx: whatever + if in_stack and "ctx:" in line: + stack_addresses = print_all_addresses(stack_addresses) + last_stack = line.strip() + # 3fffffb0: feefeffe feefeffe 3ffe85d8 401004ed + 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, []) + stack_addresses[last_stack].extend(addrs) + # epc1=0xfffefefe epc2=0xfefefefe epc3=0xefefefef excvaddr=0xfefefefe depc=0xfefefefe + elif EPC_STRING in line: + pairs = line.split() + for pair in pairs: + name, addr = pair.split("=") + if name in ["epc1", "excvaddr"]: + output = format_address(addr) + if output: + print(f"{name}={output}") + # Exception (123): + # Other reasons coming before the guard shown as-is + 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 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 "<<> 24) & 255 return raw + def add_crc(out): with open(out, "rb") as binfile: raw = bytearray(binfile.read()) @@ -141,15 +185,16 @@ def add_crc(out): with open(out, "wb") as binfile: binfile.write(raw) + def gzip_bin(mode, out): import gzip firmware_path = out - gzip_path = firmware_path + '.gz' - orig_path = firmware_path + '.orig' + gzip_path = f"{firmware_path}.gz" + orig_path = f"{firmware_path}.orig" if os.path.exists(gzip_path): os.remove(gzip_path) - print('GZipping firmware ' + firmware_path) + print(f'GZipping firmware {firmware_path}') with open(firmware_path, 'rb') as firmware_file, \ gzip.open(gzip_path, 'wb') as dest: data = firmware_file.read() @@ -162,10 +207,11 @@ def gzip_bin(mode, out): if mode == "PIO": if os.path.exists(orig_path): os.remove(orig_path) - print('Moving original firmware to ' + orig_path) + print(f'Moving original firmware to {orig_path}') os.rename(firmware_path, orig_path) os.rename(gzip_path, firmware_path) + def main(): parser = argparse.ArgumentParser(description='Create a BIN file from eboot.elf and Arduino sketch.elf for upload by esptool.py') parser.add_argument('-e', '--eboot', action='http://github.com/store', required=True, help='Path to the Arduino eboot.elf bootloader') @@ -179,8 +225,7 @@ def main(): args = parser.parse_args() - print('Creating BIN file "{out}" using "{eboot}" and "{app}"'.format( - out=args.out, eboot=args.eboot, app=args.app)) + print(f'Creating BIN file "{args.out}" using "{args.eboot}" and "{args.app}"') with open(args.out, "wb") as out: def wrapper(**kwargs): diff --git a/tools/format_tzdata.py b/tools/format_tzdata.py new file mode 100755 index 0000000000..609a9218bf --- /dev/null +++ b/tools/format_tzdata.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python3 + +# this script refreshes world timezone definitions in +# cores/esp8266/TZ.h +# +# use the file output argument or stdout redirect to overwrite the target file + +import argparse +import contextlib +import datetime +import mmap +import os +import pathlib +import re +import sys +import pathlib + +from importlib import resources + +import tzdata # https://tzdata.readthedocs.io/en/latest/ + + +def known_alias(entry): + swaps = { + "Europe/Zaporozhye": "Europe/Zaporizhzhia", + "Europe/Uzhgorod": "Europe/Uzhhorod", + } + + return swaps.get(entry) + + +def fix_name(name): + swaps = [["-", "m"], ["+", "p"], ["/", "_"]] + + for lhs, rhs in swaps: + name = name.replace(lhs, rhs) + + return name + + +def utc_alias(zone): + return zone in ( + "Universal", + "UTC", + "UCT", + "Zulu", + "GMT", + "GMT+0", + "GMT-0", + "GMT0", + "Greenwich", + ) + + +def tzdata_resource_from_name(name): + pair = name.rsplit("/", 1) + if len(pair) == 1: + return resources.files("tzdata.zoneinfo") / pair[0] + + return resources.files(f'tzdata.zoneinfo.{pair[0].replace("/", ".")}') / pair[1] + + +def make_zones_list(f): + return [zone.strip() for zone in f.readlines()] + + +def make_zones(args): + out = [] + + for zone in make_zones_list(args.zones): + if args.root: + target = args.root / zone + else: + target = tzdata_resource_from_name(zone) + + with target.open("rb") as f: + magic = f.read(4) + if magic != b"TZif": + continue + + m = mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ) + newline = m.rfind(b"\n", 0, len(m) - 1) + if newline < 0: + continue + + m.seek(newline + 1) + tz = m.readline().strip() + tz = tz.decode("ascii") + + if alias := known_alias(zone): + out.append([alias, tz]) + + out.append([zone, tz]) + + out.sort(key=lambda x: x[0]) + return out + + +def markdown(zones): + utcs = [] + rows = [] + + for name, value in zones: + if utc_alias(name): + utcs.append(name) + continue + + rows.append(f"|{name}|`{value}`|") + + print("|Name|Value|") + print("|---|---|") + for name in utcs: + print(f"|{name}|UTC0|") + + last = "" + for row in rows: + prefix, _, _ = row.partition("/") + if last != prefix: + last = prefix + print("|||") + print(row) + print() + print("---") + print() + print(f"*Generated with *{tzdata.IANA_VERSION=} {tzdata.__version__=}*") + + +def header(zones): + print("// ! ! ! DO NOT EDIT, AUTOMATICALLY GENERATED ! ! !") + print(f"// File created {datetime.datetime.now(tz=datetime.timezone.utc)}") + print(f"// Based on IANA database {tzdata.IANA_VERSION}") + print(f"// Re-run /tools/{sys.argv[0]} to update") + print() + print("#pragma once") + print() + for name, value in zones: + print(f'#define TZ_{fix_name(name)}\tPSTR("{value}")') + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + parser.add_argument( + "--output", + type=argparse.FileType("w", encoding="utf-8"), + default=sys.stdout, + ) + parser.add_argument( + "--format", + default="header", + choices=["header", "markdown"], + ) + parser.add_argument( + "--zones", + type=argparse.FileType("r", encoding="utf-8"), + help="Zone names file, one per line", + default=os.path.join(os.path.dirname(tzdata.__file__), "zones"), + ) + parser.add_argument( + "--root", + help="Where do we get raw zoneinfo files from", + type=pathlib.Path, + ) + + args = parser.parse_args() + zones = make_zones(args) + + with contextlib.redirect_stdout(args.output): + if args.format == "markdown": + markdown(zones) + elif args.format == "header": + header(zones) diff --git a/tools/mkbuildoptglobals.py b/tools/mkbuildoptglobals.py new file mode 100644 index 0000000000..62a3373aee --- /dev/null +++ b/tools/mkbuildoptglobals.py @@ -0,0 +1,828 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# This script manages the use of a file with a unique name, like +# `Sketch.ino.globals.h`, in the Sketch source directory to provide compiler +# command-line options (build options) and sketch global macros. The build +# option data is encapsulated in a unique "C" comment block and extracted into +# the build tree during prebuild. +# +# Copyright (C) 2022 - M Hightower +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# A Tip of the hat to: +# +# This PR continues the effort to get some form of global build support +# presented by brainelectronics' PR https://github.com/esp8266/Arduino/pull/8095 +# +# Used d-a-v's global name suggestion from arduino PR +# https://github.com/arduino/arduino-cli/pull/1524 +# +""" +Operation + +"Sketch.ino.globals.h" - A global h file in the Source Sketch directory. The +string Sketch.ino is the actual name of the sketch program. A matching copy is +kept in the build path/core directory. The file is empty when it does not exist +in the source directory. + +Using Sketch.ino.globals.h as a container to hold build.opt, gives implicit +dependency tracking for build.opt by way of Sketch.ino.globals.h's +dependencies. +Example: + gcc ... @{build.path}/core/build.opt -include "{build.path}/core/{build.project_name}.globals.h" ... + +In this implementation the '-include "{build.path}/core/{build.project_name}.globals.h"' +component is added to the build.opt file. + gcc ... @{build.path}/core/build.opt ... + +At each build cycle, "{build.project_name}.globals.h" is conditoinally copied to +"{build.path}/core/" at prebuild, and build.opt is extraction as needed. The +Sketch.ino.globals.h's dependencies will trigger "rebuild all" as needed. + +If Sketch.ino.globals.h is not in the source sketch folder, an empty +versions is created in the build tree. The file build.opt always contains a +"-include ..." entry so that file dependencies are generated for +Sketch.ino.globals.h. This allows for change detection when the file is +added. +""" + +""" +Arduino `preferences.txt` changes + +"Aggressively cache compiled core" ideally should be turned off; however, +a workaround has been implimented. +In ~/.arduino15/preferences.txt, to disable the feature: + compiler.cache_core=false + +Reference: +https://forum.arduino.cc/t/no-aggressively-cache-compiled-core-in-ide-1-8-15/878954/2 +""" + +""" +# Updates or Additions for platform.txt or platform.local.txt + +runtime.tools.mkbuildoptglobals={runtime.platform.path}/tools/mkbuildoptglobals.py + +# Fully qualified file names for processing sketch global options +globals.h.source.fqfn={build.source.path}/{build.project_name}.globals.h +commonhfile.fqfn={build.core.path}/CommonHFile.h +build.opt.fqfn={build.path}/core/build.opt +mkbuildoptglobals.extra_flags= + +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} + +compiler.cpreprocessor.flags=-D__ets__ -DICACHE_FLASH -U__STRICT_ANSI__ -D_GNU_SOURCE -DESP8266 @{build.opt.path} "-I{compiler.sdk.path}/include" "-I{compiler.sdk.path}/{build.lwip_include}" "-I{compiler.libc.path}/include" "-I{build.path}/core" +""" + +""" +A Sketch.ino.globals.h file with embedded build.opt might look like this + +/*@create-file:build.opt@ +// An embedded build.opt file using a "C" block comment. The starting signature +// must be on a line by itself. The closing block comment pattern should be on a +// line by itself. Each line within the block comment will be space trimmed and +// written to build.opt, skipping blank lines and lines starting with '//', '*' +// or '#'. + +-DMYDEFINE="\"Chimichangas do not exist\"" +-O3 +-fanalyzer +-DUMM_STATS=2 +*/ + +#ifndef SKETCH_INO_GLOBALS_H +#define SKETCH_INO_GLOBALS_H + +#if defined(__cplusplus) +// Defines kept private to .cpp modules +//#pragma message("__cplusplus has been seen") +#endif + +#if !defined(__cplusplus) && !defined(__ASSEMBLER__) +// Defines kept private to .c modules +#endif + +#if defined(__ASSEMBLER__) +// Defines kept private to assembler modules +#endif + +#endif +""" + +""" +Added 2) and 5) to docs + +Caveats, Observations, and Ramblings + +1) Edits to platform.txt or platform.local.txt force a complete rebuild that +removes the core folder. Not a problem, just something to be aware of when +debugging this script. Similarly, changes on the IDE Tools selection cause a +complete rebuild. + +In contrast, the core directory is not deleted when the rebuild occurs from +changing a file with an established dependency. + +2) Renaming files does not change the last modified timestamp, possibly causing +issues when replacing files by renaming and rebuilding. + +A good example of this problem is when you correct the spelling of file +Sketch.ino.globals.h. You need to touch (update time stampt) the file so a +rebuild all is performed. + +3) During the build two identical copies of Sketch.ino.globals.h will exist. +#ifndef fencing will be needed for non comment blocks in Sketch.ino.globals.h. + +4) By using a .h file to encapsulate "build.opt" options, the information is not +lost after a save-as. Before with an individual "build.opt" file, the file was +missing in the saved copy. + +5) When a .h file is renamed, a copy of the old file remains in the build +sketch folder. This can create confusion if you missed an edit in updating an +include in one or more of your modules. That module will continue to use the +stale version of the .h, until you restart the IDE or other major changes that +would cause the IDE to delete and recopy the contents from the source sketch. + +This may be the culprit for "What! It built fine last night!" + +6a) In The case of two Arduino IDE screens up with different programs, they can +share the same core archive file. Defines on one screen will change the core +archive, and a build on the 2nd screen will build with those changes. +The 2nd build will have the core built for the 1st screen. It gets uglier. With +the 2nd program, the newly built modules used headers processed with different +defines than the core. + +6b) Problem: Once core has been build, changes to build.opt or globals.h will +not cause the core archive to be rebuild. You either have to change tool +settings or close and reopen the Arduino IDE. This is a variation on 6a) above. +I thought this was working for the single sketch case, but it does not! :( +That is because sometimes it does build properly. What is unknown are the +causes that will make it work and fail? + * Fresh single Arduino IDE Window, open with file to build - works + +I think these, 6a and 6b, are resolved by setting `compiler.cache_core=false` +in ~/.arduino15/preferences.txt, to disable the aggressive caching feature: + https://forum.arduino.cc/t/no-aggressively-cache-compiled-core-in-ide-1-8-15/878954/2 + +Added workaround for `compiler.cache_core=true` case. +See `if use_aggressive_caching_workaround:` in main(). + +7) Suspected but not confirmed. A quick edit and rebuild don't always work well. +Build does not work as expected. This does not fail often. Maybe PIC NIC. +""" + +import argparse +import glob +import locale +import os +import platform +import sys +import textwrap +import time +import traceback + +from shutil import copyfile + + +# Stay in sync with our bundled version +PYTHON_REQUIRES = (3, 7) + +if sys.version_info < PYTHON_REQUIRES: + raise SystemExit(f"{__file__}\nMinimal supported version of Python is {PYTHON_REQUIRES[0]}.{PYTHON_REQUIRES[1]}") + + +# Need to work on signature line used for match to avoid conflicts with +# existing embedded documentation methods. +build_opt_signature = "/*@create-file:build.opt@" + +docs_url = "https://arduino-esp8266.readthedocs.io/en/latest/faq/a06-global-build-options.html" + + +err_print_flag = False +msg_print_buf = "" +debug_enabled = False +default_encoding = None + +# Issues trying to address through buffered printing +# 1. Arduino IDE 2.0 RC5 does not show stderr text in color. Text printed does +# not stand out from stdout messages. +# 2. Separate pipes, buffering, and multiple threads with output can create +# mixed-up messages. "flush" helped but did not resolve. The Arduino IDE 2.0 +# somehow makes the problem worse. +# 3. With Arduino IDE preferences set for "no verbose output", you only see +# stderr messages. Prior related prints are missing. +# +# Locally buffer and merge both stdout and stderr prints. This allows us to +# print a complete context when there is an error. When any buffered prints +# are targeted to stderr, print the whole buffer to stderr. + +def print_msg(*args, **kwargs): + global msg_print_buf + if 'sep' in kwargs: + sep = kwargs['sep'] + else: + sep = ' ' + + msg_print_buf += args[0] + for arg in args[1:]: + msg_print_buf += sep + msg_print_buf += arg + + if 'end' in kwargs: + msg_print_buf += kwargs['end'] + else: + msg_print_buf += '\n' + + +# Bring attention to errors with a blank line and lines starting with "*** ". +def print_err(*args, **kwargs): + global err_print_flag + if (args[0])[0] != ' ': + print_msg("") + print_msg("***", *args, **kwargs) + err_print_flag = True + +def print_dbg(*args, **kwargs): + global debug_enabled + global err_print_flag + if debug_enabled: + print_msg("DEBUG:", *args, **kwargs) + err_print_flag = True + + +def handle_error(err_no): + # on err_no 0, commit print buffer to stderr or stdout + # on err_no != 0, commit print buffer to stderr and sys exist with err_no + global msg_print_buf + global err_print_flag + if len(msg_print_buf): + if err_no or err_print_flag: + fd = sys.stderr + else: + fd = sys.stdout + print(msg_print_buf, file=fd, end='', flush=True) + msg_print_buf = "" + err_print_flag = False + if err_no: + sys.exit(err_no) + + +def copy_create_build_file(source_fqfn, build_target_fqfn): + """ + Conditionally copy a newer file between the source directory and the build + directory. When source file is missing, create an empty file in the build + directory. + return True when file change detected. + """ + if os.path.exists(source_fqfn): + if os.path.exists(build_target_fqfn) and \ + os.path.getmtime(build_target_fqfn) >= os.path.getmtime(source_fqfn): + # only copy newer files - do nothing, all is good + print_dbg(f"up to date os.path.exists({source_fqfn}) ") + return False + else: + # The new copy gets stamped with the current time, just as other + # files copied by `arduino-builder`. + copyfile(source_fqfn, build_target_fqfn) + print_dbg(f"copyfile({source_fqfn}, {build_target_fqfn})") + else: + if os.path.exists(build_target_fqfn) and \ + os.path.getsize(build_target_fqfn) == 0: + return False + else: + # Place holder - Must have an empty file to satisfy parameter list + # specifications in platform.txt. + with open(build_target_fqfn, 'w', encoding="utf-8"): + pass + return True # file changed + +def add_include_line(build_opt_fqfn, include_fqfn): + global default_encoding + if not os.path.exists(include_fqfn): + # If file is missing, we need an place holder + with open(include_fqfn, 'w', encoding=default_encoding): + pass + print_msg("add_include_line: Created " + include_fqfn) + + with open(build_opt_fqfn, 'a', encoding=default_encoding) as build_opt: + build_opt.write('-include "' + include_fqfn.replace('\\', '\\\\') + '"\n') + +def extract_create_build_opt_file(globals_h_fqfn, file_name, build_opt_fqfn): + """ + Extract the embedded build.opt from Sketch.ino.globals.h into build + path/core/build.opt. The subdirectory path must already exist as well as the + copy of Sketch.ino.globals.h. + """ + global build_opt_signature + global default_encoding + + build_opt = open(build_opt_fqfn, 'w', encoding=default_encoding) + if not os.path.exists(globals_h_fqfn) or (0 == os.path.getsize(globals_h_fqfn)): + build_opt.close() + return False + + complete_comment = False + build_opt_error = False + line_no = 0 + # If the source sketch did not have the file Sketch.ino.globals.h, an empty + # file was created in the ./core/ folder. + # By using the copy, open will always succeed. + with open(globals_h_fqfn, 'r', encoding="utf-8") as src: + for line in src: + line = line.strip() + line_no += 1 + if line == build_opt_signature: + if complete_comment: + build_opt_error = True + print_err(" Multiple embedded build.opt blocks in", f'{file_name}:{line_no}') + continue + print_msg("Extracting embedded compiler command-line options from", f'{file_name}:{line_no}') + for line in src: + line = line.strip() + line_no += 1 + if 0 == len(line): + continue + if line.startswith("*/"): + complete_comment = True + break + elif line.startswith("*"): # these are so common - skip these should they occur + continue + elif line.startswith("#"): # allow some embedded comments + continue + elif line.startswith("//"): + continue + # some consistency checking before writing - give some hints about what is wrong + elif line == build_opt_signature: + print_err(" Double begin before end for embedded build.opt block in", f'{file_name}:{line_no}') + build_opt_error = True + elif line.startswith(build_opt_signature): + print_err(" build.opt signature block ignored, trailing character for embedded build.opt block in", f'{file_name}:{line_no}') + build_opt_error = True + elif "/*" in line or "*/" in line : + print_err(" Nesting issue for embedded build.opt block in", f'{file_name}:{line_no}') + build_opt_error = True + else: + print_msg(" ", f'{line_no:2}, Add command-line option: {line}', sep='') + build_opt.write(line + "\n") + elif line.startswith(build_opt_signature): + print_err(" build.opt signature block ignored, trailing character for embedded build.opt block in", f'{file_name}:{line_no}') + build_opt_error = True + if not complete_comment or build_opt_error: + build_opt.truncate(0) + build_opt.close() + if build_opt_error: + # this will help the script start over when the issue is fixed + os.remove(globals_h_fqfn) + print_err(" Extraction failed") + # Don't let the failure get hidden by a spew of nonsensical error + # messages that will follow. Bring things to a halt. + handle_error(1) + return False # not reached + elif complete_comment: + print_msg(" Created compiler command-line options file " + build_opt_fqfn) + build_opt.close() + return complete_comment + + +def enable_override(enable, commonhfile_fqfn): + # Reduce disk IO writes + if os.path.exists(commonhfile_fqfn): + if os.path.getsize(commonhfile_fqfn): # workaround active + if enable: + return + elif not enable: + return + with open(commonhfile_fqfn, 'w', encoding="utf-8") as file: + if enable: + file.write("//Override aggressive caching\n") + # enable workaround when getsize(commonhfile_fqfn) is non-zero, disabled when zero + + +def discover_1st_time_run(build_path): + # Need to know if this is the 1ST compile of the Arduino IDE starting. + # Use empty cache directory as an indicator for 1ST compile. + # Arduino IDE 2.0 RC5 does not cleanup on exist like 1.6.19. Probably for + # debugging like the irregular version number 10607. For RC5 this indicator + # will be true after a reboot instead of a 1ST compile of the IDE starting. + # Another issue for this technique, Windows does not clear the Temp directory. :( + tmp_path, build = os.path.split(build_path) + ide_2_0 = 'arduino-sketch-' + if ide_2_0 == build[:len(ide_2_0)]: + search_path = os.path.join(tmp_path, 'arduino-core-cache/*') # Arduino IDE 2.0 + else: + search_path = os.path.join(tmp_path, 'arduino_cache_*/*') # Arduino IDE 1.6.x and up + + count = 0 + for dirname in glob.glob(search_path): + count += 1 + return 0 == count + + +def get_preferences_txt(file_fqfn, key): + # Get Key Value, key is allowed to be missing. + # We assume file file_fqfn exists + basename = os.path.basename(file_fqfn) + with open(file_fqfn, encoding="utf-8") as file: + for line in file: + name, value = line.partition("=")[::2] + if name.strip().lower() == key: + val = value.strip().lower() + if val != 'true': + val = False + print_msg(f" {basename}: {key}={val}") + return val + print_err(f" Key '{key}' not found in file {basename}. Default to true.") + return True # If we don't find it just assume it is set True + + +def check_preferences_txt(runtime_ide_path, preferences_file): + key = "compiler.cache_core" + # return the state of "compiler.cache_core" found in preferences.txt + if preferences_file != None: + if os.path.exists(preferences_file): + print_msg(f"Using preferences from '{preferences_file}'") + return get_preferences_txt(preferences_file, key) + else: + print_err(f"Override preferences file '{preferences_file}' not found.") + + # Referencing the preferences.txt for an indication of shared "core.a" + # caching is unreliable. There are too many places reference.txt can be + # stored and no hints of which the Arduino build might be using. Unless + # directed otherwise, assume "core.a" caching true. + print_msg(f"Assume aggressive 'core.a' caching enabled.") + return True + +def touch(fname, times=None): + with open(fname, "ab") as file: + file.close(); + os.utime(fname, times) + +def synchronous_touch(globals_h_fqfn, commonhfile_fqfn): + global debug_enabled + # touch both files with the same timestamp + touch(globals_h_fqfn) + with open(globals_h_fqfn, "rb") as file: + file.close() + with open(commonhfile_fqfn, "ab") as file2: + file2.close() + ts = os.stat(globals_h_fqfn) + os.utime(commonhfile_fqfn, ns=(ts.st_atime_ns, ts.st_mtime_ns)) + + if debug_enabled: + print_dbg("After synchronous_touch") + ts = os.stat(globals_h_fqfn) + print_dbg(f" globals_h_fqfn ns_stamp = {ts.st_mtime_ns}") + print_dbg(f" getmtime(globals_h_fqfn) {os.path.getmtime(globals_h_fqfn)}") + ts = os.stat(commonhfile_fqfn) + print_dbg(f" commonhfile_fqfn ns_stamp = {ts.st_mtime_ns}") + print_dbg(f" getmtime(commonhfile_fqfn) {os.path.getmtime(commonhfile_fqfn)}") + +def determine_cache_state(args, runtime_ide_path, source_globals_h_fqfn): + global docs_url + print_dbg(f"runtime_ide_version: {args.runtime_ide_version}") + + if args.cache_core != None: + print_msg(f"Preferences override, this prebuild script assumes the 'compiler.cache_core' parameter is set to {args.cache_core}") + print_msg(f"To change, modify 'mkbuildoptglobals.extra_flags=(--cache_core | --no_cache_core)' in 'platform.local.txt'") + return args.cache_core + else: + ide_path = None + preferences_fqfn = None + if args.preferences_sketch != None: + preferences_fqfn = os.path.join( + os.path.dirname(source_globals_h_fqfn), + os.path.normpath(args.preferences_sketch)) + else: + if args.preferences_file != None: + preferences_fqfn = args.preferences_file + elif args.preferences_env != None: + preferences_fqfn = args.preferences_env + else: + ide_path = runtime_ide_path + + if preferences_fqfn != None: + preferences_fqfn = os.path.normpath(preferences_fqfn) + root = False + if 'Windows' == platform.system(): + if preferences_fqfn[1:2] == ':\\': + root = True + else: + if preferences_fqfn[0] == '/': + root = True + if not root: + if preferences_fqfn[0] != '~': + preferences_fqfn = os.path.join("~", preferences_fqfn) + preferences_fqfn = os.path.expanduser(preferences_fqfn) + print_dbg(f"determine_cache_state: preferences_fqfn: {preferences_fqfn}") + + return check_preferences_txt(ide_path, preferences_fqfn) + + +""" +TODO + +aggressive caching workaround +========== ======= ========== +The question needs to be asked, is it a good idea? +With all this effort to aid in determining the cache state, it is rendered +usless when arduino command line switches are used that contradict our +settings. + +Sort out which of these are imperfect solutions should stay in + +Possible options for handling problems caused by: + ./arduino --preferences-file other-preferences.txt + ./arduino --pref compiler.cache_core=false + +--cache_core +--no_cache_core +--preferences_file (relative to IDE or full path) +--preferences_sketch (default looks for preferences.txt or specify path relative to sketch folder) +--preferences_env, python docs say "Availability: most flavors of Unix, Windows." + + export ARDUINO15_PREFERENCES_FILE=$(realpath other-name-than-default-preferences.txt ) + ./arduino --preferences-file other-name-than-default-preferences.txt + + platform.local.txt: mkbuildoptglobals.extra_flags=--preferences_env + + Tested with: + export ARDUINO15_PREFERENCES_FILE=$(realpath ~/projects/arduino/arduino-1.8.19/portable/preferences.txt) + ~/projects/arduino/arduino-1.8.18/arduino + + + Future Issues + * "--preferences-file" does not work for Arduino IDE 2.0, they plan to address at a future release + * Arduino IDE 2.0 does not support portable, they plan to address at a future release + +""" + + +def check_env(env): + system = platform.system() + # From the docs: + # Availability: most flavors of Unix, Windows. + # “Availability: Unix” are supported on macOS + # Because of the soft commitment, I used "help=argparse.SUPPRESS" to keep + # the claim out of the help. The unavailable case is untested. + val = os.getenv(env) + if val == None: + if "Linux" == system or "Windows" == system: + raise argparse.ArgumentTypeError(f'Missing environment variable: {env}') + else: + # OS/Library limitation + raise argparse.ArgumentTypeError('Not supported') + return val + + +def parse_args(): + extra_txt = '''\ + Use platform.local.txt 'mkbuildoptglobals.extra_flags=...' to supply override options: + --cache_core | --no_cache_core | --preferences_file PREFERENCES_FILE | ... + + more help at {} + '''.format(docs_url) + parser = argparse.ArgumentParser( + description='Prebuild processing for globals.h and build.opt file', + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=textwrap.dedent(extra_txt)) + parser.add_argument('runtime_ide_path', help='Runtime IDE path, {runtime.ide.path}') + parser.add_argument('runtime_ide_version', type=int, help='Runtime IDE Version, {runtime.ide.version}') + parser.add_argument('build_path', help='Build path, {build.path}') + parser.add_argument('build_opt_fqfn', help="Build FQFN to build.opt") + parser.add_argument('source_globals_h_fqfn', help="Source FQFN Sketch.ino.globals.h") + parser.add_argument('commonhfile_fqfn', help="Core Source FQFN CommonHFile.h") + parser.add_argument('--debug', action='http://github.com/store_true', required=False, default=False) + parser.add_argument('-DDEBUG_ESP_PORT', nargs='?', action='http://github.com/store', const="", default="", help='Add mkbuildoptglobals.extra_flags={build.debug_port} to platform.local.txt') + parser.add_argument('--ci', action='http://github.com/store_true', required=False, default=False) + group = parser.add_mutually_exclusive_group(required=False) + group.add_argument('--cache_core', action='http://github.com/store_true', default=None, help='Assume a "compiler.cache_core" value of true') + group.add_argument('--no_cache_core', dest='cache_core', action='http://github.com/store_false', help='Assume a "compiler.cache_core" value of false') + group.add_argument('--preferences_file', help='Full path to preferences file') + group.add_argument('--preferences_sketch', nargs='?', action='http://github.com/store', const="preferences.txt", help='Sketch relative path to preferences file') + # Since the docs say most versions of Windows and Linux support the os.getenv method, suppress the help message. + group.add_argument('--preferences_env', nargs='?', action='http://github.com/store', type=check_env, const="ARDUINO15_PREFERENCES_FILE", help=argparse.SUPPRESS) + # ..., help='Use environment variable for path to preferences file') + return parser.parse_args() + # ref epilog, https://stackoverflow.com/a/50021771 + # ref nargs='*'', https://stackoverflow.com/a/4480202 + # ref no '--n' parameter, https://stackoverflow.com/a/21998252 + + +# retrieve *system* encoding, not the one used by python internally +if sys.version_info >= (3, 11): + def get_encoding(): + return locale.getencoding() +else: + def get_encoding(): + return locale.getdefaultlocale()[1] + + +def show_value(desc, value): + print_dbg(f'{desc:<40} {value}') + return + +def locale_dbg(): + show_value("get_encoding()", get_encoding()) + show_value("locale.getdefaultlocale()", locale.getdefaultlocale()) + show_value('sys.getfilesystemencoding()', sys.getfilesystemencoding()) + show_value("sys.getdefaultencoding()", sys.getdefaultencoding()) + show_value("locale.getpreferredencoding(False)", locale.getpreferredencoding(False)) + try: + show_value("locale.getpreferredencoding()", locale.getpreferredencoding()) + except: + pass + show_value("sys.stdout.encoding", sys.stdout.encoding) + + # use current setting + show_value("locale.setlocale(locale.LC_ALL, None)", locale.setlocale(locale.LC_ALL, None)) + try: + show_value("locale.getencoding()", locale.getencoding()) + except: + pass + show_value("locale.getlocale()", locale.getlocale()) + + # use user setting + show_value("locale.setlocale(locale.LC_ALL, '')", locale.setlocale(locale.LC_ALL, '')) + # show_value("locale.getencoding()", locale.getencoding()) + show_value("locale.getlocale()", locale.getlocale()) + return + + +def main(): + global build_opt_signature + global docs_url + global debug_enabled + global default_encoding + num_include_lines = 1 + + # Given that GCC will handle lines from an @file as if they were on + # the command line. I assume that the contents of @file need to be encoded + # to match that of the shell running GCC runs. I am not 100% sure this API + # gives me that, but it appears to work. + # + # However, elsewhere when dealing with source code we continue to use 'utf-8', + # ref. https://gcc.gnu.org/onlinedocs/cpp/Character-sets.html + default_encoding = get_encoding() + + args = parse_args() + debug_enabled = args.debug + runtime_ide_path = os.path.normpath(args.runtime_ide_path) + build_path = os.path.normpath(args.build_path) + build_opt_fqfn = os.path.normpath(args.build_opt_fqfn) + source_globals_h_fqfn = os.path.normpath(args.source_globals_h_fqfn) + commonhfile_fqfn = os.path.normpath(args.commonhfile_fqfn) + + globals_name = os.path.basename(source_globals_h_fqfn) + build_path_core, build_opt_name = os.path.split(build_opt_fqfn) + globals_h_fqfn = os.path.join(build_path_core, globals_name) + + if debug_enabled: + locale_dbg() + + print_msg(f'default_encoding: {default_encoding}') + + print_dbg(f"runtime_ide_path: {runtime_ide_path}") + print_dbg(f"runtime_ide_version: {args.runtime_ide_version}") + print_dbg(f"build_path: {build_path}") + print_dbg(f"build_opt_fqfn: {build_opt_fqfn}") + print_dbg(f"source_globals_h_fqfn: {source_globals_h_fqfn}") + print_dbg(f"commonhfile_fqfn: {commonhfile_fqfn}") + print_dbg(f"globals_name: {globals_name}") + print_dbg(f"build_path_core: {build_path_core}") + print_dbg(f"globals_h_fqfn: {globals_h_fqfn}") + print_dbg(f"DDEBUG_ESP_PORT: {args.DDEBUG_ESP_PORT}") + + if len(args.DDEBUG_ESP_PORT): + build_opt_signature = build_opt_signature[:-1] + ":debug@" + + print_dbg(f"build_opt_signature: {build_opt_signature}") + + if args.ci: + # Requires CommonHFile.h to never be checked in. + if os.path.exists(commonhfile_fqfn): + first_time = False + else: + first_time = True + else: + first_time = discover_1st_time_run(build_path) + if first_time: + print_dbg("First run since Arduino IDE started.") + + use_aggressive_caching_workaround = determine_cache_state(args, runtime_ide_path, source_globals_h_fqfn) + + print_dbg(f"first_time: {first_time}") + print_dbg(f"use_aggressive_caching_workaround: {use_aggressive_caching_workaround}") + + if not os.path.exists(build_path_core): + os.makedirs(build_path_core) + print_msg("Clean build, created dir " + build_path_core) + + if first_time or \ + not use_aggressive_caching_workaround or \ + not os.path.exists(commonhfile_fqfn): + enable_override(False, commonhfile_fqfn) + + # A future timestamp on commonhfile_fqfn will cause everything to + # rebuild. This occurred during development and may happen after + # changing the system time. + if time.time_ns() < os.stat(commonhfile_fqfn).st_mtime_ns: + touch(commonhfile_fqfn) + print_err(f"Neutralized future timestamp on build file: {commonhfile_fqfn}") + + if os.path.exists(source_globals_h_fqfn): + print_msg("Using global include from " + source_globals_h_fqfn) + + copy_create_build_file(source_globals_h_fqfn, globals_h_fqfn) + + # globals_h_fqfn timestamp was only updated if the source changed. This + # controls the rebuild on change. We can always extract a new build.opt + # w/o triggering a needless rebuild. + embedded_options = extract_create_build_opt_file(globals_h_fqfn, globals_name, build_opt_fqfn) + + if use_aggressive_caching_workaround: + # commonhfile_fqfn encodes the following information + # 1. When touched, it causes a rebuild of core.a + # 2. When file size is non-zero, it indicates we are using the + # aggressive cache workaround. The workaround is set to true + # (active) when we discover a non-zero length global .h file in + # any sketch. The aggressive workaround is cleared on the 1ST + # compile by the Arduino IDE after starting. + # 3. When the timestamp matches the build copy of globals.h + # (globals_h_fqfn), we know one two things: + # * The cached core.a matches up to the current build.opt and + # globals.h. The current sketch owns the cached copy of core.a. + # * globals.h has not changed, and no need to rebuild core.a + # 4. When core.a's timestamp does not match the build copy of + # the global .h file, we only know we need to rebuild core.a, and + # that is enough. + # + # When the sketch build has a "Sketch.ino.globals.h" file in the + # build tree that exactly matches the timestamp of "CommonHFile.h" + # in the platform source tree, it owns the core.a cache copy. If + # not, or "Sketch.ino.globals.h" has changed, rebuild core. + # A non-zero file size for commonhfile_fqfn, means we have seen a + # globals.h file before and workaround is active. + if debug_enabled: + print_dbg("Timestamps at start of check aggressive caching workaround") + ts = os.stat(globals_h_fqfn) + print_dbg(f" globals_h_fqfn ns_stamp = {ts.st_mtime_ns}") + print_dbg(f" getmtime(globals_h_fqfn) {os.path.getmtime(globals_h_fqfn)}") + ts = os.stat(commonhfile_fqfn) + print_dbg(f" commonhfile_fqfn ns_stamp = {ts.st_mtime_ns}") + print_dbg(f" getmtime(commonhfile_fqfn) {os.path.getmtime(commonhfile_fqfn)}") + + if os.path.getsize(commonhfile_fqfn): + if (os.path.getmtime(globals_h_fqfn) != os.path.getmtime(commonhfile_fqfn)): + # Need to rebuild core.a + # touching commonhfile_fqfn in the source core tree will cause rebuild. + # Looks like touching or writing unrelated files in the source core tree will cause rebuild. + synchronous_touch(globals_h_fqfn, commonhfile_fqfn) + print_msg("Using 'aggressive caching' workaround, rebuild shared 'core.a' for current globals.") + else: + print_dbg(f"Using old cached 'core.a'") + elif os.path.getsize(globals_h_fqfn): + enable_override(True, commonhfile_fqfn) + synchronous_touch(globals_h_fqfn, commonhfile_fqfn) + print_msg("Using 'aggressive caching' workaround, rebuild shared 'core.a' for current globals.") + else: + print_dbg(f"Workaround not active/needed") + + add_include_line(build_opt_fqfn, commonhfile_fqfn) + add_include_line(build_opt_fqfn, globals_h_fqfn) + + # Provide context help for build option support. + source_build_opt_h_fqfn = os.path.join(os.path.dirname(source_globals_h_fqfn), "build_opt.h") + if os.path.exists(source_build_opt_h_fqfn) and not embedded_options: + print_err("Build options file '" + source_build_opt_h_fqfn + "' not supported.") + print_err(" Add build option content to '" + source_globals_h_fqfn + "'.") + print_err(" Embedd compiler command-line options in a block comment starting with '" + build_opt_signature + "'.") + print_err(" Read more at " + docs_url) + elif os.path.exists(source_globals_h_fqfn): + if not embedded_options: + print_msg("Tip: Embedd compiler command-line options in a block comment starting with '" + build_opt_signature + "'.") + print_msg(" Read more at " + docs_url) + else: + print_msg("Note: optional global include file '" + source_globals_h_fqfn + "' does not exist.") + print_msg(" Read more at " + docs_url) + + handle_error(0) # commit print buffer + +if __name__ == '__main__': + rc = 1 + try: + rc = main() + except: + print_err(traceback.format_exc()) + handle_error(0) + sys.exit(rc) diff --git a/tools/platformio-build.py b/tools/platformio-build.py index a462ff391a..20fe8c77fb 100644 --- a/tools/platformio-build.py +++ b/tools/platformio-build.py @@ -59,7 +59,13 @@ def scons_patched_match_splitext(path, suffixes=None): gzip_switch = ["--gzip", "PIO"] env.Append( - ASFLAGS=["-x", "assembler-with-cpp"], + ASFLAGS=[ + "-mlongcalls", + "-mtext-section-literals", + ], + ASPPFLAGS=[ + "-x", "assembler-with-cpp", + ], # General options that are passed to the C compiler (C only; not C++) CFLAGS=[ @@ -78,7 +84,6 @@ def scons_patched_match_splitext(path, suffixes=None): "-mtext-section-literals", "-falign-functions=4", "-U__STRICT_ANSI__", - "-D_GNU_SOURCE", "-ffunction-sections", "-fdata-sections", "-Wall", @@ -119,8 +124,10 @@ def scons_patched_match_splitext(path, suffixes=None): ("F_CPU", "$BOARD_F_CPU"), "__ets__", "ICACHE_FLASH", + "_GNU_SOURCE", ("ARDUINO", 10805), ("ARDUINO_BOARD", '\\"PLATFORMIO_%s\\"' % env.BoardConfig().id.upper()), + ("ARDUINO_BOARD_ID", '\\"%s\\"' % env.BoardConfig().id), "FLASHMODE_${BOARD_FLASH_MODE.upper()}", "LWIP_OPEN_SRC" ], @@ -166,54 +173,43 @@ def scons_patched_match_splitext(path, suffixes=None): ) ) -# copy CCFLAGS to ASFLAGS (-x assembler-with-cpp mode) -env.Append(ASFLAGS=env.get("CCFLAGS", [])[:]) - -flatten_cppdefines = env.Flatten(env['CPPDEFINES']) - # # SDK # -if "PIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3" in flatten_cppdefines: - env.Append( - CPPDEFINES=[("NONOSDK3V0", 1)], - LIBPATH=[join(FRAMEWORK_DIR, "tools", "sdk", "lib", "NONOSDK3V0")] - ) -elif "PIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK221" in flatten_cppdefines: - #(previous default) - env.Append( - CPPDEFINES=[("NONOSDK221", 1)], - LIBPATH=[join(FRAMEWORK_DIR, "tools", "sdk", "lib", "NONOSDK221")] - ) -elif "PIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190313" in flatten_cppdefines: - env.Append( - CPPDEFINES=[("NONOSDK22x_190313", 1)], - LIBPATH=[join(FRAMEWORK_DIR, "tools", "sdk", "lib", "NONOSDK22x_190313")] - ) -elif "PIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_191024" in flatten_cppdefines: - env.Append( - CPPDEFINES=[("NONOSDK22x_191024", 1)], - LIBPATH=[join(FRAMEWORK_DIR, "tools", "sdk", "lib", "NONOSDK22x_191024")] - ) -elif "PIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_191105" in flatten_cppdefines: - env.Append( - CPPDEFINES=[("NONOSDK22x_191105", 1)], - LIBPATH=[join(FRAMEWORK_DIR, "tools", "sdk", "lib", "NONOSDK22x_191105")] - ) -elif "PIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_191122" in flatten_cppdefines: - env.Append( - CPPDEFINES=[("NONOSDK22x_191122", 1)], - LIBPATH=[join(FRAMEWORK_DIR, "tools", "sdk", "lib", "NONOSDK22x_191122")] - ) -else: #(default) if "PIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703" in flatten_cppdefines: - env.Append( - CPPDEFINES=[("NONOSDK22x_190703", 1)], - LIBPATH=[join(FRAMEWORK_DIR, "tools", "sdk", "lib", "NONOSDK22x_190703")] - ) +NONOSDK_VERSIONS = ( + ("SDK22x_190703", "NONOSDK22x_190703"), + ("SDK221", "NONOSDK221"), + ("SDK22x_190313", "NONOSDK22x_190313"), + ("SDK22x_191024", "NONOSDK22x_191024"), + ("SDK22x_191105", "NONOSDK22x_191105"), + ("SDK22x_191122", "NONOSDK22x_191122"), + ("SDK305", "NONOSDK305"), +) +nonosdk_version = NONOSDK_VERSIONS[0] + +NONOSDK_PREFIX = "PIO_FRAMEWORK_ARDUINO_ESPRESSIF_" +for define in env["CPPDEFINES"]: + if isinstance(define, (tuple, list)): + define, *_ = define + if define.startswith(NONOSDK_PREFIX): + for version in NONOSDK_VERSIONS: + name, _ = version + if define.endswith(name): + nonosdk_version = version + +NONOSDK_LIBPATH=join(FRAMEWORK_DIR, "tools", "sdk", "lib", nonosdk_version[1]) +assert isdir(NONOSDK_LIBPATH) + +env.Append( + CPPDEFINES=[(nonosdk_version[1], 1)], + LIBPATH=[NONOSDK_LIBPATH], +) # # lwIP # +flatten_cppdefines = env.Flatten(env["CPPDEFINES"]) + lwip_lib = None if "PIO_FRAMEWORK_ARDUINO_LWIP2_IPV6_LOW_MEMORY" in flatten_cppdefines: env.Append( @@ -279,17 +275,23 @@ def scons_patched_match_splitext(path, suffixes=None): # current_vtables = None -fp_in_irom = "" +current_fp = None for d in flatten_cppdefines: if str(d).startswith("VTABLES_IN_"): current_vtables = d - if str(d) == "FP_IN_IROM": - fp_in_irom = "-DFP_IN_IROM" + if str(d).startswith("FP_IN_"): + current_fp = d + if not current_vtables: current_vtables = "VTABLES_IN_FLASH" env.Append(CPPDEFINES=[current_vtables]) assert current_vtables +if not current_fp: + current_fp = "FP_IN_IROM" + env.Append(CPPDEFINES=[current_fp]) +assert current_fp + # # MMU # @@ -334,7 +336,7 @@ def scons_patched_match_splitext(path, suffixes=None): for flag in env["CPPDEFINES"]: define = flag if isinstance(flag, (tuple, list)): - define, _ = flag + define, *_ = flag if define.startswith("MMU_"): mmu_flags.append(flag) # PIO_FRAMEWORK_ARDUINO_MMU_CACHE32_IRAM32 (default) @@ -367,9 +369,10 @@ def scons_patched_match_splitext(path, suffixes=None): join("$BUILD_DIR", "ld", "local.eagle.app.v6.common.ld"), join(FRAMEWORK_DIR, "tools", "sdk", "ld", "eagle.app.v6.common.ld.h"), env.VerboseAction( - "$CC -CC -E -P -D%s %s %s $SOURCE -o $TARGET" + "$CC -CC -E -P -D%s -D%s %s $SOURCE -o $TARGET" % ( current_vtables, + current_fp, # String representation of MMU flags " ".join( [ @@ -377,7 +380,6 @@ def scons_patched_match_splitext(path, suffixes=None): for f in mmu_flags ] ), - fp_in_irom, ), "Generating LD script $TARGET", ), diff --git a/tools/sdk/include/bearssl/bearssl.h b/tools/sdk/include/bearssl/bearssl.h index 4f4797cf79..310edb258d 100644 --- a/tools/sdk/include/bearssl/bearssl.h +++ b/tools/sdk/include/bearssl/bearssl.h @@ -137,6 +137,10 @@ #include "bearssl_x509.h" #include "bearssl_pem.h" +#ifdef __cplusplus +extern "C" { +#endif + /** \brief Type for a configuration option. * * A "configuration option" is a value that is selected when the BearSSL @@ -167,4 +171,13 @@ typedef struct { */ const br_config_option *br_get_config(void); +/* ======================================================================= */ + +/** \brief Version feature: support for time callback. */ +#define BR_FEATURE_X509_TIME_CALLBACK 1 + +#ifdef __cplusplus +} +#endif + #endif diff --git a/tools/sdk/include/bearssl/bearssl_git.h b/tools/sdk/include/bearssl/bearssl_git.h index 9ee1904139..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 6105635 +#define BEARSSL_GIT 5166f2b diff --git a/tools/sdk/include/bearssl/bearssl_x509.h b/tools/sdk/include/bearssl/bearssl_x509.h index f2f6e6f877..9a1e6593e0 100644 --- a/tools/sdk/include/bearssl/bearssl_x509.h +++ b/tools/sdk/include/bearssl/bearssl_x509.h @@ -625,6 +625,52 @@ typedef struct { } br_name_element; +/** + * \brief Callback for validity date checks. + * + * The function receives as parameter an arbitrary user-provided context, + * and the notBefore and notAfter dates specified in an X.509 certificate, + * both expressed as a number of days and a number of seconds: + * + * - Days are counted in a proleptic Gregorian calendar since + * January 1st, 0 AD. Year "0 AD" is the one that preceded "1 AD"; + * it is also traditionally known as "1 BC". + * + * - Seconds are counted since midnight, from 0 to 86400 (a count of + * 86400 is possible only if a leap second happened). + * + * Each date and time is understood in the UTC time zone. The "Unix + * Epoch" (January 1st, 1970, 00:00 UTC) corresponds to days=719528 and + * seconds=0; the "Windows Epoch" (January 1st, 1601, 00:00 UTC) is + * days=584754, seconds=0. + * + * This function must return -1 if the current date is strictly before + * the "notBefore" time, or +1 if the current date is strictly after the + * "notAfter" time. If neither condition holds, then the function returns + * 0, which means that the current date falls within the validity range of + * the certificate. If the function returns a value distinct from -1, 0 + * and +1, then this is interpreted as an unavailability of the current + * time, which normally ends the validation process with a + * `BR_ERR_X509_TIME_UNKNOWN` error. + * + * During path validation, this callback will be invoked for each + * considered X.509 certificate. Validation fails if any of the calls + * returns a non-zero value. + * + * The context value is an abritrary pointer set by the caller when + * configuring this callback. + * + * \param tctx context pointer. + * \param not_before_days notBefore date (days since Jan 1st, 0 AD). + * \param not_before_seconds notBefore time (seconds, at most 86400). + * \param not_after_days notAfter date (days since Jan 1st, 0 AD). + * \param not_after_seconds notAfter time (seconds, at most 86400). + * \return -1, 0 or +1. + */ +typedef int (*br_x509_time_check)(void *tctx, + uint32_t not_before_days, uint32_t not_before_seconds, + uint32_t not_after_days, uint32_t not_after_seconds); + /** * \brief The "minimal" X.509 engine structure. * @@ -647,8 +693,8 @@ typedef struct { uint32_t *rp; const unsigned char *ip; } cpu; - uint32_t dp_stack[32]; - uint32_t rp_stack[32]; + uint32_t dp_stack[31]; + uint32_t rp_stack[31]; int err; /* Server name to match with the SAN / CN of the EE certificate. */ @@ -730,6 +776,12 @@ typedef struct { br_name_element *name_elts; size_t num_name_elts; + /* + * Callback function (and context) to get the current date. + */ + void *itime_ctx; + br_x509_time_check itime; + /* * Public key cryptography implementations (signature verification). */ @@ -890,7 +942,10 @@ void br_x509_minimal_init_full(br_x509_minimal_context *ctx, * - Seconds are counted since midnight, from 0 to 86400 (a count of * 86400 is possible only if a leap second happened). * - * The validation date and time is understood in the UTC time zone. + * The validation date and time is understood in the UTC time zone. The + * "Unix Epoch" (January 1st, 1970, 00:00 UTC) corresponds to days=719528 + * and seconds=0; the "Windows Epoch" (January 1st, 1601, 00:00 UTC) is + * days=584754, seconds=0. * * If the validation date and time are not explicitly set, but BearSSL * was compiled with support for the system clock on the underlying @@ -908,6 +963,28 @@ br_x509_minimal_set_time(br_x509_minimal_context *ctx, { ctx->days = days; ctx->seconds = seconds; + ctx->itime = 0; +} + +/** + * \brief Set the validity range callback function for the X.509 + * "minimal" engine. + * + * The provided function will be invoked to check whether the validation + * date is within the validity range for a given X.509 certificate; a + * call will be issued for each considered certificate. The provided + * context pointer (itime_ctx) will be passed as first parameter to the + * callback. + * + * \param tctx context for callback invocation. + * \param cb callback function. + */ +static inline void +br_x509_minimal_set_time_callback(br_x509_minimal_context *ctx, + void *itime_ctx, br_x509_time_check itime) +{ + ctx->itime_ctx = itime_ctx; + ctx->itime = itime; } /** 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/include/user_interface.h b/tools/sdk/include/user_interface.h index e69821b372..1d057417b8 100644 --- a/tools/sdk/include/user_interface.h +++ b/tools/sdk/include/user_interface.h @@ -25,6 +25,12 @@ #ifndef __USER_INTERFACE_H__ #define __USER_INTERFACE_H__ +#if defined(NONOSDK305) +#define NONOSDK (0x30500) +#else +#define NONOSDK (0x22100) +#endif + #include "os_type.h" #ifdef LWIP_OPEN_SRC @@ -249,13 +255,19 @@ typedef struct { struct station_config { uint8 ssid[32]; uint8 password[64]; +#if (NONOSDK >= (0x30200)) + uint8 channel; +#endif uint8 bssid_set; // Note: If bssid_set is 1, station will just connect to the router // with both ssid[] and bssid[] matched. Please check about this. uint8 bssid[6]; wifi_fast_scan_threshold_t threshold; -#ifdef NONOSDK3V0 +#if (NONOSDK >= (0x30000)) bool open_and_wep_mode_disable; // Can connect to open/wep router by default. #endif +#if (NONOSDK >= (0x30200)) + bool all_channel_scan; +#endif }; bool wifi_station_get_config(struct station_config *config); @@ -382,17 +394,17 @@ void wifi_softap_free_station_info(void); bool wifi_softap_dhcps_start(void); bool wifi_softap_dhcps_stop(void); -#if 1 // dhcp server -// these functions are open-source, in dhcp server, -// which is now moved to lwIPDhcpServer.cpp (lwip2) -// (but still there with lwip1) +// esp8266/Arduino notice: +// these dhcp functions are no longer provided by the lwip lib +// only way to include them is to build our NonOS LwipDhcpServer helpers +// (ref. libraries/ESP8266WiFi/src/ESP8266WiFiAP-DhcpServer.cpp) + bool wifi_softap_set_dhcps_lease(struct dhcps_lease *please); bool wifi_softap_get_dhcps_lease(struct dhcps_lease *please); uint32 wifi_softap_get_dhcps_lease_time(void); bool wifi_softap_set_dhcps_lease_time(uint32 minute); bool wifi_softap_reset_dhcps_lease_time(void); bool wifi_softap_add_dhcps_lease(uint8 *macaddr); // add static lease on the list, this will be the next available @ -#endif // dhcp server enum dhcp_status wifi_softap_dhcps_status(void); bool wifi_softap_set_dhcps_offer_option(uint8 level, void* optarg); @@ -438,7 +450,7 @@ typedef enum { MODEM_SLEEP_T } sleep_type_t; -#ifdef NONOSDK3V0 +#if (NONOSDK >= (0x30000)) typedef enum { MIN_SLEEP_T, @@ -771,6 +783,70 @@ bool wifi_set_country(wifi_country_t *country); */ bool wifi_get_country(wifi_country_t *country); +#if (NONOSDK >= (0x30000)) + +typedef enum { + SYSTEM_PARTITION_INVALID = 0, + SYSTEM_PARTITION_BOOTLOADER, /* user can't modify this partition address, but can modify size */ + SYSTEM_PARTITION_OTA_1, /* user can't modify this partition address, but can modify size */ + SYSTEM_PARTITION_OTA_2, /* user can't modify this partition address, but can modify size */ + SYSTEM_PARTITION_RF_CAL, /* user must define this partition */ + SYSTEM_PARTITION_PHY_DATA, /* user must define this partition */ + SYSTEM_PARTITION_SYSTEM_PARAMETER, /* user must define this partition */ + SYSTEM_PARTITION_AT_PARAMETER, + SYSTEM_PARTITION_SSL_CLIENT_CERT_PRIVKEY, + SYSTEM_PARTITION_SSL_CLIENT_CA, + SYSTEM_PARTITION_SSL_SERVER_CERT_PRIVKEY, + SYSTEM_PARTITION_SSL_SERVER_CA, + SYSTEM_PARTITION_WPA2_ENTERPRISE_CERT_PRIVKEY, + SYSTEM_PARTITION_WPA2_ENTERPRISE_CA, + + SYSTEM_PARTITION_CUSTOMER_BEGIN = 100, /* user can define partition after here */ + SYSTEM_PARTITION_MAX +} partition_type_t; + +typedef struct { + partition_type_t type; /* the partition type */ + uint32_t addr; /* the partition address */ + uint32_t size; /* the partition size */ +} partition_item_t; + +/** + * @brief regist partition table information, user MUST call it in user_pre_init() + * + * @param partition_table: the partition table + * @param partition_num: the partition number in partition table + * @param map: the flash map + * + * @return true : succeed + * @return false : fail + */ +bool system_partition_table_regist( + const partition_item_t* partition_table, + uint32_t partition_num, + uint32_t map + ); + +/** + * @brief get ota partition size + * + * @return the size of ota partition + */ +uint32_t system_partition_get_ota_partition_size(void); + +/** + * @brief get partition information + * + * @param type: the partition type + * @param partition_item: the point to store partition information + * + * @return true : succeed + * @return false : fail + */ +bool system_partition_get_item(partition_type_t type, partition_item_t* partition_item); + +#endif + #ifdef __cplusplus } #endif diff --git a/tools/sdk/ld/eagle.app.v6.common.ld.h b/tools/sdk/ld/eagle.app.v6.common.ld.h index 77c834ae19..e6b415c50b 100644 --- a/tools/sdk/ld/eagle.app.v6.common.ld.h +++ b/tools/sdk/ld/eagle.app.v6.common.ld.h @@ -138,10 +138,20 @@ SECTIONS *(.text.app_entry*) /* The main startup code */ - *(.text.gdbstub*, .text.gdb_init) /* Any GDB hooks */ + *(.text.gdbstub* .text.gdb_init) /* Any GDB hooks */ /* all functional callers are placed in IRAM (including SPI/IRQ callbacks/etc) here */ *(.text._ZNKSt8functionIF*EE*) /* std::function::operator()() const */ + +#ifdef FP_IN_IRAM + *libgcc.a:*f2.o(.literal .text) + *libgcc.a:*f3.o(.literal .text) + *libgcc.a:*fsi.o(.literal .text) + *libgcc.a:*fdi.o(.literal .text) + *libgcc.a:*ifs.o(.literal .text) + *libgcc.a:*idf.o(.literal .text) +#endif + } >iram1_0_seg :iram1_0_phdr .irom0.text : ALIGN(4) @@ -155,13 +165,31 @@ SECTIONS LONG(0x00000000); *(.ver_number) - *.c.o(.literal*, .text*) - *.cpp.o(EXCLUDE_FILE (umm_malloc.cpp.o) .literal*, EXCLUDE_FILE (umm_malloc.cpp.o) .text*) - *.cc.o(.literal*, .text*) + *.c.o(.literal* .text*) + *.cpp.o(EXCLUDE_FILE (umm_malloc.cpp.o) .literal* EXCLUDE_FILE (umm_malloc.cpp.o) .text*) + *.cc.o(.literal* .text*) #ifdef VTABLES_IN_FLASH *(.rodata._ZTV*) /* C++ vtables */ #endif + . = ALIGN(4); /* this table MUST be 4-byte aligned */ + /* C++ constructor and destructor tables, properly ordered: */ + __init_array_start = ABSOLUTE(.); + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + __init_array_end = ABSOLUTE(.); + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + . = ALIGN(4); /* this table MUST be 4-byte aligned */ + _bss_table_start = ABSOLUTE(.); + LONG(_bss_start) + LONG(_bss_end) + _bss_table_end = ABSOLUTE(.); + *libgcc.a:unwind-dw2.o(.literal .text .rodata .literal.* .text.* .rodata.*) *libgcc.a:unwind-dw2-fde.o(.literal .text .rodata .literal.* .text.* .rodata.*) @@ -220,7 +248,7 @@ SECTIONS *(.rodata._ZZ*__func__) /* std::* exception strings, in their own section to allow string coalescing */ - *(.irom.exceptiontext, .rodata.exceptiontext) + *(.irom.exceptiontext .rodata.exceptiontext) *(.rodata.*__exception_what__*) /* G++ seems to throw out templatized section attributes */ /* c++ typeof IDs, etc. */ diff --git a/tools/sdk/ld/eagle.app.v6.common.ld.vtables.h b/tools/sdk/ld/eagle.app.v6.common.ld.vtables.h index 6649e08a25..f837e12d41 100644 --- a/tools/sdk/ld/eagle.app.v6.common.ld.vtables.h +++ b/tools/sdk/ld/eagle.app.v6.common.ld.vtables.h @@ -12,18 +12,7 @@ *(.gnu.linkonce.e.*) *(.gnu.version_r) *(.eh_frame) - . = (. + 3) & ~ 3; - /* C++ constructor and destructor tables, properly ordered: */ - __init_array_start = ABSOLUTE(.); - KEEP (*crtbegin.o(.ctors)) - KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) - KEEP (*(SORT(.ctors.*))) - KEEP (*(.ctors)) - __init_array_end = ABSOLUTE(.); - KEEP (*crtbegin.o(.dtors)) - KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) - KEEP (*(SORT(.dtors.*))) - KEEP (*(.dtors)) + . = ALIGN(4); /* C++ exception handlers table: */ __XT_EXCEPTION_DESCS__ = ABSOLUTE(.); *(.xt_except_desc) @@ -32,11 +21,6 @@ *(.xt_except_desc_end) *(.dynamic) *(.gnu.version_d) - . = ALIGN(4); /* this table MUST be 4-byte aligned */ - _bss_table_start = ABSOLUTE(.); - LONG(_bss_start) - LONG(_bss_end) - _bss_table_end = ABSOLUTE(.); _rodata_end = ABSOLUTE(.); } >dram0_0_seg :dram0_0_phdr diff --git a/tools/sdk/lib/NONOSDK221/libwpa2.a b/tools/sdk/lib/NONOSDK221/libwpa2.a index 1f7aabb688..f2b2178fc0 100644 Binary files a/tools/sdk/lib/NONOSDK221/libwpa2.a and b/tools/sdk/lib/NONOSDK221/libwpa2.a differ diff --git a/tools/sdk/lib/NONOSDK22x_190313/libwpa2.a b/tools/sdk/lib/NONOSDK22x_190313/libwpa2.a index 07420c5bf0..fb08a7ee3e 100644 Binary files a/tools/sdk/lib/NONOSDK22x_190313/libwpa2.a and b/tools/sdk/lib/NONOSDK22x_190313/libwpa2.a differ diff --git a/tools/sdk/lib/NONOSDK22x_190703/libwpa2.a b/tools/sdk/lib/NONOSDK22x_190703/libwpa2.a index 07420c5bf0..fb08a7ee3e 100644 Binary files a/tools/sdk/lib/NONOSDK22x_190703/libwpa2.a and b/tools/sdk/lib/NONOSDK22x_190703/libwpa2.a differ diff --git a/tools/sdk/lib/NONOSDK22x_191024/libwpa2.a b/tools/sdk/lib/NONOSDK22x_191024/libwpa2.a index 07420c5bf0..fb08a7ee3e 100644 Binary files a/tools/sdk/lib/NONOSDK22x_191024/libwpa2.a and b/tools/sdk/lib/NONOSDK22x_191024/libwpa2.a differ diff --git a/tools/sdk/lib/NONOSDK22x_191105/libwpa2.a b/tools/sdk/lib/NONOSDK22x_191105/libwpa2.a index 07420c5bf0..fb08a7ee3e 100644 Binary files a/tools/sdk/lib/NONOSDK22x_191105/libwpa2.a and b/tools/sdk/lib/NONOSDK22x_191105/libwpa2.a differ diff --git a/tools/sdk/lib/NONOSDK22x_191122/libwpa2.a b/tools/sdk/lib/NONOSDK22x_191122/libwpa2.a index 07420c5bf0..fb08a7ee3e 100644 Binary files a/tools/sdk/lib/NONOSDK22x_191122/libwpa2.a and b/tools/sdk/lib/NONOSDK22x_191122/libwpa2.a differ diff --git a/tools/sdk/lib/NONOSDK305/commitlog.txt b/tools/sdk/lib/NONOSDK305/commitlog.txt new file mode 100644 index 0000000000..ebad3fedf1 --- /dev/null +++ b/tools/sdk/lib/NONOSDK305/commitlog.txt @@ -0,0 +1,147 @@ + feat: Update sdk version header file 3.0.5 + feat(core): update core version to b29dcd3 + feat: Update sdk version header file 3.0.4 + fix: It sometimes crashes after disable sntp + fix: Sometimes the parameter is missing because of power down when erasing or writing the flash + +commit 7b5b35da98ad9ee2de7afc63277d4933027ae91c +Merge: bd63c1f 26bedd0 +Author: Xu Chun Guang +Date: Fri Oct 15 11:40:39 2021 +0000 + + Merge branch 'feature/update_at_bin' into 'release/v3.0.5' + + feat: Update at bin to 1.7.5 + + See merge request sdk/ESP8266_NONOS_SDK!296 + +commit 26bedd0c7afd8a090670fb352f1f8a811b4f1839 +Author: Xu Chun Guang +Date: Fri Oct 15 18:06:49 2021 +0800 + + feat: Update at bin to 1.7.5 + +commit 38d251e1decafb56bae94e67f62ec425c6e5fe64 +Author: Xu Chun Guang +Date: Fri Oct 15 18:04:52 2021 +0800 + + feat: Update sdk version header file 3.0.5 + +commit bd63c1f81aae4bfb1b569e880a24fa73be81c713 +Merge: b1c14cd dae389b +Author: Xu Chun Guang +Date: Sat Oct 9 03:57:00 2021 +0000 + + Merge branch 'feature/update_core_v3.0.5' into 'release/v3.0.5' + + feat(core): update core version to b29dcd3 + + See merge request sdk/ESP8266_NONOS_SDK!293 + +commit dae389b804ad7c796d4dd62fab9ec346208507ac +Author: Chen Wu +Date: Sat Oct 9 11:37:28 2021 +0800 + + feat(core): update core version to b29dcd3 + +commit b1c14cdb08e2f39b654933f887b4f997b291982f +Merge: 5677e37 43dfa5a +Author: Xu Chun Guang +Date: Wed May 27 16:56:15 2020 +0800 + + Merge branch 'feature/update_at_bin' into 'master' + + feat: Update at bin to 1.7.4.0 + + See merge request sdk/ESP8266_NONOS_SDK!283 + +commit 43dfa5a7f919c3537929c1e36822118414da7d7f +Author: Xu Chun Guang +Date: Wed May 27 10:22:50 2020 +0800 + + feat: Update at bin to 1.7.4.0 + +commit 5677e379adf43032c8c05dde7816854409f9a8e8 +Merge: 8c0e419 9fbceb4 +Author: Xu Chun Guang +Date: Wed May 27 09:52:55 2020 +0800 + + Merge branch 'feature/update_sdk_version_h' into 'master' + + feat: Update sdk version header file 3.0.4 + + See merge request sdk/ESP8266_NONOS_SDK!282 + +commit 9fbceb4bc837521a09a790ee8db6dd1166e55498 +Author: Xu Chun Guang +Date: Wed May 27 09:50:30 2020 +0800 + + feat: Update sdk version header file 3.0.4 + +commit 8c0e419de1173347c4b9d073a2d891d3575c018a +Merge: f537843 13e436f +Author: Xu Chun Guang +Date: Tue May 26 15:35:24 2020 +0800 + + Merge branch 'bugfix/N8266-67' into 'master' + + fix: It sometimes crashes after disable sntp + + See merge request sdk/ESP8266_NONOS_SDK!281 + +commit 13e436f6214993e1f322c24f20a431fef8a123ca +Author: Xu Chun Guang +Date: Tue May 26 10:55:57 2020 +0800 + + feat: Update lwip lib to 5623f48f + +commit 5623f48f5e2dd1f45c25b9f2cad2314924a3cb00 +Author: Xu Chun Guang +Date: Tue May 26 10:52:30 2020 +0800 + + fix: It sometimes crashes after disable sntp + +commit f5378436fde39c0693df25efb51174b56f79dcc2 +Merge: c2ddeca da9767c +Author: Xu Chun Guang +Date: Fri May 22 17:45:10 2020 +0800 + + Merge branch 'bugfix/parameter_missing' into 'master' + + fix: Sometimes the parameter is missing because of power down when erasing or writing the flash + + See merge request sdk/ESP8266_NONOS_SDK!280 + +commit da9767c316e0e0dca9c00d91fbdac44ada62b29c +Author: Xu Chun Guang +Date: Fri May 22 17:26:39 2020 +0800 + + fix: Sometimes the parameter is missing because of power down when erasing or writing the flash + +commit c2ddeca67fe849a0a243ee50a28f8c81b8bba59a +Merge: b2831d5 613a042 +Author: Xu Chun Guang +Date: Tue May 12 11:17:23 2020 +0800 + + Merge branch 'feature/compatible_with_16M_512_512_in_at_nano' into 'master' + + feat: Compatible with 16M 512 512 in at nano + + See merge request sdk/ESP8266_NONOS_SDK!279 + +commit 613a042bc35823e7a48b0870a6562008f34009a9 +Author: Xu Chun Guang +Date: Tue May 12 10:06:16 2020 +0800 + + feat: Compatible with 16M 512 512 in at nano + +commit b2831d57b3c51e421bf35fc902d177ea57ba6e13 +Merge: b77cb8c 369b9bc +Author: Xu Chun Guang +Date: Mon May 11 20:04:36 2020 +0800 + + Merge branch 'bugfix/workaround_ota' into 'master' + + Bugfix/workaround ota + + See merge request sdk/ESP8266_NONOS_SDK!277 diff --git a/tools/sdk/lib/NONOSDK3V0/libairkiss.a b/tools/sdk/lib/NONOSDK305/libairkiss.a similarity index 72% rename from tools/sdk/lib/NONOSDK3V0/libairkiss.a rename to tools/sdk/lib/NONOSDK305/libairkiss.a index cfdcc84234..60415e5242 100644 Binary files a/tools/sdk/lib/NONOSDK3V0/libairkiss.a and b/tools/sdk/lib/NONOSDK305/libairkiss.a differ diff --git a/tools/sdk/lib/NONOSDK305/libcrypto.a b/tools/sdk/lib/NONOSDK305/libcrypto.a new file mode 100644 index 0000000000..cbb94e7552 Binary files /dev/null and b/tools/sdk/lib/NONOSDK305/libcrypto.a differ diff --git a/tools/sdk/lib/NONOSDK305/libespnow.a b/tools/sdk/lib/NONOSDK305/libespnow.a new file mode 100644 index 0000000000..2d3f7a8d2b Binary files /dev/null and b/tools/sdk/lib/NONOSDK305/libespnow.a differ diff --git a/tools/sdk/lib/NONOSDK305/libmain.a b/tools/sdk/lib/NONOSDK305/libmain.a new file mode 100644 index 0000000000..699b5b4b14 Binary files /dev/null and b/tools/sdk/lib/NONOSDK305/libmain.a differ diff --git a/tools/sdk/lib/NONOSDK305/libnet80211.a b/tools/sdk/lib/NONOSDK305/libnet80211.a new file mode 100644 index 0000000000..1edb9e5dda Binary files /dev/null and b/tools/sdk/lib/NONOSDK305/libnet80211.a differ diff --git a/tools/sdk/lib/NONOSDK305/libphy.a b/tools/sdk/lib/NONOSDK305/libphy.a new file mode 100644 index 0000000000..9bdb6d6448 Binary files /dev/null and b/tools/sdk/lib/NONOSDK305/libphy.a differ diff --git a/tools/sdk/lib/NONOSDK305/libpp.a b/tools/sdk/lib/NONOSDK305/libpp.a new file mode 100644 index 0000000000..e5c86d681c Binary files /dev/null and b/tools/sdk/lib/NONOSDK305/libpp.a differ diff --git a/tools/sdk/lib/NONOSDK305/libsmartconfig.a b/tools/sdk/lib/NONOSDK305/libsmartconfig.a new file mode 100644 index 0000000000..d6d593e87f Binary files /dev/null and b/tools/sdk/lib/NONOSDK305/libsmartconfig.a differ diff --git a/tools/sdk/lib/NONOSDK305/libwpa.a b/tools/sdk/lib/NONOSDK305/libwpa.a new file mode 100644 index 0000000000..18b5efc3f5 Binary files /dev/null and b/tools/sdk/lib/NONOSDK305/libwpa.a differ diff --git a/tools/sdk/lib/NONOSDK305/libwpa2.a b/tools/sdk/lib/NONOSDK305/libwpa2.a new file mode 100644 index 0000000000..a3a7a4210c Binary files /dev/null and b/tools/sdk/lib/NONOSDK305/libwpa2.a differ diff --git a/tools/sdk/lib/NONOSDK305/libwps.a b/tools/sdk/lib/NONOSDK305/libwps.a new file mode 100644 index 0000000000..fe821a418d Binary files /dev/null and b/tools/sdk/lib/NONOSDK305/libwps.a differ diff --git a/tools/sdk/lib/NONOSDK305/version b/tools/sdk/lib/NONOSDK305/version new file mode 100644 index 0000000000..7a5f16731d --- /dev/null +++ b/tools/sdk/lib/NONOSDK305/version @@ -0,0 +1 @@ +v3.0.5-g7b5b35d (shows as SDK:3.0.5(b29dcd3) in debug mode) \ No newline at end of file diff --git a/tools/sdk/lib/NONOSDK3V0/libcrypto.a b/tools/sdk/lib/NONOSDK3V0/libcrypto.a deleted file mode 100644 index d0aea338e8..0000000000 Binary files a/tools/sdk/lib/NONOSDK3V0/libcrypto.a and /dev/null differ diff --git a/tools/sdk/lib/NONOSDK3V0/libespnow.a b/tools/sdk/lib/NONOSDK3V0/libespnow.a deleted file mode 100644 index 2ed7994e1d..0000000000 Binary files a/tools/sdk/lib/NONOSDK3V0/libespnow.a and /dev/null differ diff --git a/tools/sdk/lib/NONOSDK3V0/libmain.a b/tools/sdk/lib/NONOSDK3V0/libmain.a deleted file mode 100644 index fb5242f1be..0000000000 Binary files a/tools/sdk/lib/NONOSDK3V0/libmain.a and /dev/null differ diff --git a/tools/sdk/lib/NONOSDK3V0/libnet80211.a b/tools/sdk/lib/NONOSDK3V0/libnet80211.a deleted file mode 100644 index 576304be26..0000000000 Binary files a/tools/sdk/lib/NONOSDK3V0/libnet80211.a and /dev/null differ diff --git a/tools/sdk/lib/NONOSDK3V0/libphy.a b/tools/sdk/lib/NONOSDK3V0/libphy.a deleted file mode 100644 index dfd469518e..0000000000 Binary files a/tools/sdk/lib/NONOSDK3V0/libphy.a and /dev/null differ diff --git a/tools/sdk/lib/NONOSDK3V0/libpp.a b/tools/sdk/lib/NONOSDK3V0/libpp.a deleted file mode 100644 index fbf3e85636..0000000000 Binary files a/tools/sdk/lib/NONOSDK3V0/libpp.a and /dev/null differ diff --git a/tools/sdk/lib/NONOSDK3V0/libsmartconfig.a b/tools/sdk/lib/NONOSDK3V0/libsmartconfig.a deleted file mode 100644 index 34598d36a6..0000000000 Binary files a/tools/sdk/lib/NONOSDK3V0/libsmartconfig.a and /dev/null differ diff --git a/tools/sdk/lib/NONOSDK3V0/libwpa.a b/tools/sdk/lib/NONOSDK3V0/libwpa.a deleted file mode 100644 index 3a58113a1a..0000000000 Binary files a/tools/sdk/lib/NONOSDK3V0/libwpa.a and /dev/null differ diff --git a/tools/sdk/lib/NONOSDK3V0/libwpa2.a b/tools/sdk/lib/NONOSDK3V0/libwpa2.a deleted file mode 100644 index b868608a14..0000000000 Binary files a/tools/sdk/lib/NONOSDK3V0/libwpa2.a and /dev/null differ diff --git a/tools/sdk/lib/NONOSDK3V0/libwps.a b/tools/sdk/lib/NONOSDK3V0/libwps.a deleted file mode 100644 index 1f6b365e3a..0000000000 Binary files a/tools/sdk/lib/NONOSDK3V0/libwps.a and /dev/null differ diff --git a/tools/sdk/lib/NONOSDK3V0/version b/tools/sdk/lib/NONOSDK3V0/version deleted file mode 100644 index 06e014650a..0000000000 --- a/tools/sdk/lib/NONOSDK3V0/version +++ /dev/null @@ -1 +0,0 @@ -v2.2.0-28-g89920dc \ No newline at end of file diff --git a/tools/sdk/lib/README.md b/tools/sdk/lib/README.md index 6b92d04307..6e30c9a58d 100644 --- a/tools/sdk/lib/README.md +++ b/tools/sdk/lib/README.md @@ -1,8 +1,20 @@ +## Adding a new SDK library + +- Create a directory for the new SDK. +- Copy .a files from SDK `lib` directory to the new directory +- Add the new SDK directory to those supported in `eval_fix_sdks.sh` and `fix_sdk_libs.sh`. +- To support WPA2 Enterprise connections, some patches are reguired review `wpa2_eap_patch.cpp` and `eval_fix_sdks.sh` for details. +- Use `./eval_fix_sdks.sh --analyze` to aid in finding relevant differences. + - Also, you can compare two SDKs with something like `./eval_fix_sdks.sh --analyze "NONOSDK305\nNONOSDK306"` +- Apply updates to `fix_sdk_libs.sh` and `wpa2_eap_patch.cpp`. You can run `./eval_fix_sdks.sh --patch` to do a batch run of `fix_sdk_libs.sh` against each SDK. +- If you used this section, you can skip _Updating SDK libraries_. + ## Updating SDK libraries - Copy .a files from SDK `lib` directory to this directory - Run `fix_sdk_libs.sh` + ## Updating libstdc++ After building gcc using crosstool-NG, get compiled libstdc++ and remove some objects: @@ -17,4 +29,3 @@ xtensa-lx106-elf-ar d libstdc++.a del_opv.o xtensa-lx106-elf-ar d libstdc++.a new_op.o xtensa-lx106-elf-ar d libstdc++.a new_opv.o ``` - diff --git a/tools/sdk/lib/eval_fix_sdks.sh b/tools/sdk/lib/eval_fix_sdks.sh new file mode 100755 index 0000000000..389ee31650 --- /dev/null +++ b/tools/sdk/lib/eval_fix_sdks.sh @@ -0,0 +1,128 @@ +#!/bin/bash +# set -e + +single_sdk="${2}" +if [[ -n "$single_sdk" ]]; then + if [[ "NONOSDK" != "${single_sdk:0:7}" ]]; then + single_sdk="" + fi +fi + +add_path_ifexist() { + if [[ -d $1 ]]; then + export PATH=$( realpath $1 ):$PATH + return 0 + fi + return 1 +} + +if ! which xtensa-lx106-elf-ar | grep "tools/xtensa-lx106-elf/bin" >>/dev/null; then + add_path_ifexist "../../../xtensa-lx106-elf/bin" || add_path_ifexist "../../xtensa-lx106-elf/bin" +fi + +help_msg() { + cat <new.txt + if [[ -f old.txt ]]; then + echo "eap_peer_config_deinit: diff $prev_sdk $sdk" + diff old.txt new.txt + echo "" + fi + mv new.txt old.txt + prev_sdk=${sdk} + done + + unset prev_sdk + for sdk in `list_sdks`; do + unasm -j ".text.wpa2_sm_rx_eapol" ${sdk}/eap.o >new.txt + if [[ -f old2.txt ]]; then + echo "wpa2_sm_rx_eapol: diff $prev_sdk $sdk" + diff old2.txt new.txt + echo "" + fi + mv new.txt old2.txt + prev_sdk=${sdk} + done + + # Find offsets for patching vPortFree with z2EapFree + for sdk in `list_sdks`; do + echo -en "\n${sdk}/eap.o:\n " + grep --byte-offset --only-matching --text vPortFree ${sdk}/eap.o + done + + cleanup +} + + +patch_all() { + for sdk in `list_sdks`; do + pushd $sdk + ../fix_sdk_libs.sh + popd + done +} + +if [[ "${1}" == "--analyze" ]]; then + analyze +elif [[ "${1}" == "--patch" ]]; then + patch_all +else + help_msg +fi +exit 0 diff --git a/tools/sdk/lib/fix_sdk_libs.sh b/tools/sdk/lib/fix_sdk_libs.sh index b6aea12752..d024b46a08 100755 --- a/tools/sdk/lib/fix_sdk_libs.sh +++ b/tools/sdk/lib/fix_sdk_libs.sh @@ -1,36 +1,156 @@ #!/bin/bash set -e -export PATH=../../xtensa-lx106-elf/bin:$PATH +add_path_ifexist() { + if [[ -d $1 ]]; then + export PATH=$( realpath $1 ):$PATH + return 0 + fi + return 1 +} + +if ! which xtensa-lx106-elf-ar | grep "tools/xtensa-lx106-elf/bin" >>/dev/null; then + add_path_ifexist "../../../xtensa-lx106-elf/bin" || add_path_ifexist "../../xtensa-lx106-elf/bin" +fi +WORK_SPACE=${PWD} + VERSION=$(basename ${PWD}) addSymbol_system_func1() { - ADDRESS=$1 - xtensa-lx106-elf-objcopy --add-symbol system_func1=.irom0.text:${ADDRESS},function,global user_interface.o + if ! xtensa-lx106-elf-nm user_interface.o | grep -q " T system_func1"; then # Don't add symbol if it already exists + ADDRESS=$1 + xtensa-lx106-elf-objcopy --add-symbol system_func1=.irom0.text:${ADDRESS},function,global user_interface.o + fi +} + +patchFile() { + FILE=$1 + ADDRESS=$2 # DO NOT PASS AS HEX! + LENGTH=$3 # DO NOT PASS AS HEX! + EXPECTED=$4 + REPLACEWITH=$5 + if [[ "$(dd if=$FILE bs=1 count=$LENGTH skip=$ADDRESS status=none | base64 -w0)" = "$EXPECTED" ]]; then + echo "Patching $VERSION $1 ..." + echo $5 | base64 -d | dd of=$FILE bs=1 count=$LENGTH seek=$ADDRESS conv=notrunc + elif ! [[ "$(dd if=$FILE bs=1 count=$LENGTH skip=$ADDRESS status=none | base64 -w0)" = "$REPLACEWITH" ]]; then + echo "PATCH FAILED!" + echo "dd if=$FILE bs=1 count=$LENGTH skip=$ADDRESS status=none | base64 -w0" + dd if=$FILE bs=1 count=$LENGTH skip=$ADDRESS status=none | hexdump -C + dd if=$FILE bs=1 count=$LENGTH skip=$ADDRESS status=none | base64 -w0 + echo "" + exit 1 + fi +} + +grepPatchFiles() { + local SDKVER OLDNAME NEWNAME FILES OLDNAME64 NEWNAME64 FILE OFFSET PATTERN + SDKVER="${1}" + OLDNAME="${2}" + NEWNAME="${3}" + FILES="${4}" + [[ "${SDKVER:0:9}" != "NONOSDK30" ]] && return + if [[ -z "${FILES}" ]]; then + echo "grepPatchFile: bad input: file specification required" + exit 1 + fi + if [[ "${#OLDNAME}" != "${#NEWNAME}" ]]; then + echo "grepPatchFile: bad input: old name ${OLDNAME}(${#OLDNAME}) and new name ${NEWNAME}(${#NEWNAME}) must be the same length." + exit 1 + fi + OLDNAME64=( `echo -n "${OLDNAME}" | base64 -w0` ) + NEWNAME64=( `echo -n "${NEWNAME}" | base64 -w0` ) + + while read -u3 FILE OFFSET PATTERN; do + if [[ "${#OLDNAME}" == "${#PATTERN}" ]] && [[ "${OLDNAME}" == "${PATTERN}" ]]; then + patchFile "$FILE" "$OFFSET" "${#PATTERN}" "${OLDNAME64}" "${NEWNAME64}" + else + echo "grepPatchFile: bad parameters FILE=${FILE} OFFSET=${OFFSET} PATTERN=${PATTERN}" + exit 1 + fi + done 3< <( grep --with-filename --byte-offset --only-matching --text "${OLDNAME}" $FILES | tr ":" " " ) + return +} + +redefineSym() { + local SDKVER OLDNAME NEWNAME FILES FILE EXTRA PATTERN + SDKVER="${1}" + OLDNAME="${2}" + NEWNAME="${3}" + FILES="${4}" + [[ "${SDKVER:0:9}" != "NONOSDK30" ]] && return + if [[ -z "${FILES}" ]]; then + echo "redefineSym: bad input: file specification required" + exit 1 + fi + PATTERN="UND ${OLDNAME}" + for FILE in $FILES ; do + echo "xtensa-lx106-elf-objcopy --redefine-sym ${OLDNAME}=${NEWNAME} \"$FILE\"" + echo "Before:" + xtensa-lx106-elf-nm "$FILE" | grep ${OLDNAME} | sort -u + xtensa-lx106-elf-objcopy --redefine-sym ${OLDNAME}=${NEWNAME} "$FILE" + echo "After:" + xtensa-lx106-elf-nm "$FILE" | grep ${OLDNAME} | sort -u + done + return } +# # xtensa-lx106-elf-ar x libwpa2.a eap.o +if [[ "--shell" == "$1" ]]; then + # need to poke around a bit + bash --rcfile <(echo ". ~/.bashrc; cd ${WORK_SPACE}") + exit 0 +fi + +if [[ ! -f libmain.a ]]; then + echo -e "\n\n*** Archive libmain.a is missing ***\n\n" + exit 1 +fi # Remove mem_manager.o from libmain.a to use custom heap implementation, # and time.o to fix redefinition of time-related functions: xtensa-lx106-elf-ar d libmain.a mem_manager.o xtensa-lx106-elf-ar d libmain.a time.o +# Patch WPA2-Enterprise double-free +xtensa-lx106-elf-ar x libwpa2.a eap.o +eapcs=$(sha256sum eap.o | awk '{print $1}') + # Rename `hostname` and `default_hostname` symbols: xtensa-lx106-elf-ar x libmain.a eagle_lwip_if.o user_interface.o -xtensa-lx106-elf-objcopy --redefine-sym hostname=wifi_station_hostname user_interface.o -xtensa-lx106-elf-objcopy --redefine-sym hostname=wifi_station_hostname eagle_lwip_if.o -xtensa-lx106-elf-objcopy --redefine-sym default_hostname=wifi_station_default_hostname user_interface.o -xtensa-lx106-elf-objcopy --redefine-sym default_hostname=wifi_station_default_hostname eagle_lwip_if.o +lwipcs=$(sha256sum eagle_lwip_if.o | awk '{print $1}') +uics=$(sha256sum user_interface.o | awk '{print $1}') +xtensa-lx106-elf-objcopy --redefine-sym hostname=wifi_station_hostname user_interface.o +xtensa-lx106-elf-objcopy --redefine-sym hostname=wifi_station_hostname eagle_lwip_if.o +xtensa-lx106-elf-objcopy --redefine-sym default_hostname=wifi_station_default_hostname user_interface.o +xtensa-lx106-elf-objcopy --redefine-sym default_hostname=wifi_station_default_hostname eagle_lwip_if.o + if [[ ${VERSION} == "NONOSDK221" ]]; then addSymbol_system_func1 "0x60" + patchFile "eap.o" "3055" "2" "wAA=" "8CA=" # WPA2-Enterprise patch which replaces a double-free with nop, see #8082 + patchFile "eap.o" "26352" "9" "dlBvcnRGcmVl" "ejJFYXBGcmVl" # special vPortFree to recover leaked memory elif [[ ${VERSION} == "NONOSDK22x"* ]]; then addSymbol_system_func1 "0x54" -elif [[ ${VERSION} == "NONOSDK3"* ]]; then - addSymbol_system_func1 "0x60" + patchFile "eap.o" "3059" "2" "wAA=" "8CA=" # WPA2-Enterprise patch which replaces a double-free with nop, see #8082 + patchFile "eap.o" "26356" "9" "dlBvcnRGcmVl" "ejJFYXBGcmVl" # special vPortFree to recover leaked memory +elif [[ ${VERSION} == "NONOSDK305" ]]; then + addSymbol_system_func1 "0x54" + patchFile "eap.o" "67670" "9" "dlBvcnRGcmVl" "ejJFYXBGcmVl" # special vPortFree to recover leaked memory else echo "WARN: Unknown address for system_func1() called by system_restart_local()" fi -xtensa-lx106-elf-ar r libmain.a eagle_lwip_if.o user_interface.o -rm -f eagle_lwip_if.o user_interface.o +if [[ $(sha256sum eap.o | awk '{print $1}') != $eapcs ]]; then + xtensa-lx106-elf-ar r libwpa2.a eap.o +fi +if [[ $(sha256sum user_interface.o | awk '{print $1}') != $uics || $(sha256sum eagle_lwip_if.o | awk '{print $1}') != $lwipcs ]]; then + xtensa-lx106-elf-ar r libmain.a eagle_lwip_if.o user_interface.o +fi +rm -f eagle_lwip_if.o user_interface.o eap.o + +if [[ ${VERSION:0:9} == "NONOSDK30" ]]; then + # v3.0.0 and up use a non-standard pvPortMalloc. + # SDK Library global replace + redefineSym "${VERSION}" "pvPortMalloc" "sdk3_pvPortMalloc" '*.a' + xtensa-lx106-elf-objcopy --weaken-symbol load_non_32_wide_handler libmain.a +fi diff --git a/tools/sdk/lib/libbearssl.a b/tools/sdk/lib/libbearssl.a index 58e8d80c5e..6b9a77e3f6 100644 Binary files a/tools/sdk/lib/libbearssl.a and b/tools/sdk/lib/libbearssl.a differ diff --git a/tools/sdk/lib/libhal.a b/tools/sdk/lib/libhal.a index a0593d02c3..5c46953b69 100644 Binary files a/tools/sdk/lib/libhal.a and b/tools/sdk/lib/libhal.a differ diff --git a/tools/sdk/lib/liblwip2-1460-feat.a b/tools/sdk/lib/liblwip2-1460-feat.a index 3ed56d2c2d..e22ff31ad7 100644 Binary files a/tools/sdk/lib/liblwip2-1460-feat.a and b/tools/sdk/lib/liblwip2-1460-feat.a differ diff --git a/tools/sdk/lib/liblwip2-1460.a b/tools/sdk/lib/liblwip2-1460.a index 7de7fadeda..3eed8274b1 100644 Binary files a/tools/sdk/lib/liblwip2-1460.a and b/tools/sdk/lib/liblwip2-1460.a differ diff --git a/tools/sdk/lib/liblwip2-536-feat.a b/tools/sdk/lib/liblwip2-536-feat.a index 556e864256..33fcdb32c5 100644 Binary files a/tools/sdk/lib/liblwip2-536-feat.a and b/tools/sdk/lib/liblwip2-536-feat.a differ diff --git a/tools/sdk/lib/liblwip2-536.a b/tools/sdk/lib/liblwip2-536.a index f9ece4cea5..41904c209a 100644 Binary files a/tools/sdk/lib/liblwip2-536.a and b/tools/sdk/lib/liblwip2-536.a differ diff --git a/tools/sdk/lib/liblwip6-1460-feat.a b/tools/sdk/lib/liblwip6-1460-feat.a index f50d97d2ad..9c17f87448 100644 Binary files a/tools/sdk/lib/liblwip6-1460-feat.a and b/tools/sdk/lib/liblwip6-1460-feat.a differ diff --git a/tools/sdk/lib/liblwip6-536-feat.a b/tools/sdk/lib/liblwip6-536-feat.a index fae8152112..6a5ebbf2e4 100644 Binary files a/tools/sdk/lib/liblwip6-536-feat.a and b/tools/sdk/lib/liblwip6-536-feat.a differ diff --git a/tools/sdk/lib/libstdc++-exc.a b/tools/sdk/lib/libstdc++-exc.a index 6c711f19f4..3faa8d9aa5 100644 Binary files a/tools/sdk/lib/libstdc++-exc.a and b/tools/sdk/lib/libstdc++-exc.a differ diff --git a/tools/sdk/lib/libstdc++.a b/tools/sdk/lib/libstdc++.a index d0d19f25b9..96ce460b83 100644 Binary files a/tools/sdk/lib/libstdc++.a and b/tools/sdk/lib/libstdc++.a differ diff --git a/tools/sdk/lwip2/builder b/tools/sdk/lwip2/builder index 450bb63c1b..4087efd9d2 160000 --- a/tools/sdk/lwip2/builder +++ b/tools/sdk/lwip2/builder @@ -1 +1 @@ -Subproject commit 450bb63c1bc8b35770ca7f246945cf383569f52f +Subproject commit 4087efd9d2a8e1cee9a159e0796d831dc1e0c497 diff --git a/tools/sdk/lwip2/include/arch/cc.h b/tools/sdk/lwip2/include/arch/cc.h index 1b879c84cd..0b73cba842 100644 --- a/tools/sdk/lwip2/include/arch/cc.h +++ b/tools/sdk/lwip2/include/arch/cc.h @@ -85,7 +85,21 @@ typedef uint32_t sys_prot_t; /////////////////////////////// //// MISSING -#define sys_now millis // arduino wire millis() definition returns 32 bits like sys_now() does +// Arduino Core exposes time func with a generic type +#ifdef __cplusplus +extern "C" +{ +#endif +unsigned long millis(void); +#ifdef __cplusplus +} +#endif + +// b/c we have conflicting typedefs of u32_t and ulong and can't substitute funcs, +// forcibly cast the `millis()` result to lwip's version of u32_t +// (previous version was `#define sys_now millis`) +#define sys_now() ((u32_t)millis()) + #define LWIP_RAND r_rand // old lwip uses this useful undocumented function #define IPSTR "%d.%d.%d.%d" #define IP2STR(ipaddr) ip4_addr1_16(ipaddr), \ diff --git a/tools/sdk/lwip2/include/arch/sys_arch.h b/tools/sdk/lwip2/include/arch/sys_arch.h deleted file mode 100644 index abcf8afd02..0000000000 --- a/tools/sdk/lwip2/include/arch/sys_arch.h +++ /dev/null @@ -1,5 +0,0 @@ - -#ifndef MYSYSARCH_H -#define MYSYSARCH_H - -#endif // MYSYSARCH_H \ No newline at end of file diff --git a/tools/sdk/lwip2/include/dhcpserver.h b/tools/sdk/lwip2/include/dhcpserver.h deleted file mode 100644 index 4ec907126f..0000000000 --- a/tools/sdk/lwip2/include/dhcpserver.h +++ /dev/null @@ -1,130 +0,0 @@ - -// adapted from dhcpserver.c distributed in esp8266 sdk 2.0.0 -// same license may apply - -#ifndef __DHCPS_H__ -#define __DHCPS_H__ - -#include "glue.h" // for UDEBUG - -#define USE_DNS - -typedef struct dhcps_state{ - sint16_t state; -} dhcps_state; - -typedef struct dhcps_msg { - uint8_t op, htype, hlen, hops; - uint8_t xid[4]; - uint16_t secs, flags; - uint8_t ciaddr[4]; - uint8_t yiaddr[4]; - uint8_t siaddr[4]; - uint8_t giaddr[4]; - uint8_t chaddr[16]; - uint8_t sname[64]; - uint8_t file[128]; - uint8_t options[312]; -}dhcps_msg; - -#ifndef LWIP_OPEN_SRC -struct dhcps_lease { - bool enable; - struct ipv4_addr start_ip; - struct ipv4_addr end_ip; -}; - -enum dhcps_offer_option{ - OFFER_START = 0x00, - OFFER_ROUTER = 0x01, - OFFER_END -}; -#endif - -typedef enum { - DHCPS_TYPE_DYNAMIC, - DHCPS_TYPE_STATIC -} dhcps_type_t; - -typedef enum { - DHCPS_STATE_ONLINE, - DHCPS_STATE_OFFLINE -} dhcps_state_t; - -struct dhcps_pool{ - struct ipv4_addr ip; - uint8 mac[6]; - uint32 lease_timer; - dhcps_type_t type; - dhcps_state_t state; - -}; - -typedef struct _list_node{ - void *pnode; - struct _list_node *pnext; -}list_node; - -extern uint32 dhcps_lease_time; -#define DHCPS_LEASE_TIMER dhcps_lease_time //0x05A0 -#define DHCPS_MAX_LEASE 0x64 -#define BOOTP_BROADCAST 0x8000 - -#define DHCP_REQUEST 1 -#define DHCP_REPLY 2 -#define DHCP_HTYPE_ETHERNET 1 -#define DHCP_HLEN_ETHERNET 6 -#define DHCP_MSG_LEN 236 - -#define DHCPS_SERVER_PORT 67 -#define DHCPS_CLIENT_PORT 68 - -#define DHCPDISCOVER 1 -#define DHCPOFFER 2 -#define DHCPREQUEST 3 -#define DHCPDECLINE 4 -#define DHCPACK 5 -#define DHCPNAK 6 -#define DHCPRELEASE 7 - -#define DHCP_OPTION_SUBNET_MASK 1 -#define DHCP_OPTION_ROUTER 3 -#define DHCP_OPTION_DNS_SERVER 6 -#define DHCP_OPTION_REQ_IPADDR 50 -#define DHCP_OPTION_LEASE_TIME 51 -#define DHCP_OPTION_MSG_TYPE 53 -#define DHCP_OPTION_SERVER_ID 54 -#define DHCP_OPTION_INTERFACE_MTU 26 -#define DHCP_OPTION_PERFORM_ROUTER_DISCOVERY 31 -#define DHCP_OPTION_BROADCAST_ADDRESS 28 -#define DHCP_OPTION_REQ_LIST 55 -#define DHCP_OPTION_END 255 - -//#define USE_CLASS_B_NET 1 -#define DHCPS_DEBUG UDEBUG -#define MAX_STATION_NUM 8 - -#define DHCPS_STATE_OFFER 1 -#define DHCPS_STATE_DECLINE 2 -#define DHCPS_STATE_ACK 3 -#define DHCPS_STATE_NAK 4 -#define DHCPS_STATE_IDLE 5 -#define DHCPS_STATE_RELEASE 6 - -#define dhcps_router_enabled(offer) ((offer & OFFER_ROUTER) != 0) - -#ifdef __cplusplus -extern "C" -{ -#endif - -void dhcps_set_dns (int num, const ipv4_addr_t* dns); - -void dhcps_start(struct ip_info *info); -void dhcps_stop(void); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/tools/sdk/lwip2/include/glue.h b/tools/sdk/lwip2/include/glue.h index 453a3830e9..ffe84d2625 100644 --- a/tools/sdk/lwip2/include/glue.h +++ b/tools/sdk/lwip2/include/glue.h @@ -118,20 +118,4 @@ err_glue_t glue2esp_linkoutput (int netif_idx, void* ref2save, void* data, size #define lwip_xt_rsil(level) (__extension__({uint32_t state; __asm__ __volatile__("rsil %0," __STRINGIFY(level) : "=a" (state) :: "memory"); state;})) #define lwip_xt_wsr_ps(state) __asm__ __volatile__("wsr %0,ps; isync" :: "a" (state) : "memory") -// quickfix: workaround for definition of __PRI32(x) in inttypes.h -// it has changed with recent version of xtensa-gcc -// __INT32 is missing -// gcc-4.x: __PRI32(x) is __STRINGIFY(l##x) -// gcc-10.2.0: __PRI32(x) is __INT32 __STRINGIFY(x) -#include -#if !defined(__INT8) -#define __INT8 -#endif -#if !defined(__INT16) -#define __INT16 -#endif -#if !defined(__INT32) -#define __INT32 "l" -#endif - #endif // GLUE_H diff --git a/tools/sdk/lwip2/include/gluedebug.h b/tools/sdk/lwip2/include/gluedebug.h index f7a1917799..9358878b15 100644 --- a/tools/sdk/lwip2/include/gluedebug.h +++ b/tools/sdk/lwip2/include/gluedebug.h @@ -9,11 +9,9 @@ // this is needed separately from lwipopts.h // because it is shared by both sides of glue -#define UNDEBUG 1 // 0 or 1 (1: uassert removed) +#define UNDEBUG 1 // 0 or 1 (1: uassert removed = saves flash) #define UDEBUG 0 // 0 or 1 (glue debug) -#define UDUMP 0 // 0 or 1 (glue / dump packet) -#define UDEBUGINDEX 0 // 0 or 1 (show debug line number) -#define UDEBUGSTORE 0 // 0 or 1 (store debug into buffer until doprint_allow=1=serial-available) +#define UDUMP 0 // 0 or 1 (glue: dump packet) #define ULWIPDEBUG 0 // 0 or 1 (trigger lwip debug) #define ULWIPASSERT 0 // 0 or 1 (trigger lwip self-check, 0 saves flash) @@ -24,8 +22,6 @@ #define STRING_IN_FLASH 0 // *print("fmt is stored in flash") #endif -#define ROTBUFLEN_BIT 11 // (UDEBUGSTORE=1) doprint()'s buffer: 11=2048B - #if ULWIPDEBUG //#define LWIP_DBG_TYPES_ON (LWIP_DBG_ON|LWIP_DBG_TRACE|LWIP_DBG_STATE|LWIP_DBG_FRESH|LWIP_DBG_HALT) #define LWIP_DBG_TYPES_ON (LWIP_DBG_ON|LWIP_DBG_TRACE|LWIP_DBG_STATE|LWIP_DBG_FRESH) @@ -45,17 +41,11 @@ extern void (*phy_capture) (int netif_idx, const char* data, size_t len, int out } #endif - ///////////////////////////////////////////////////////////////////////////// #if ARDUINO #include #endif -#if UDEBUG && UDEBUGSTORE -#warning use 'doprint_allow=1' right after Serial is enabled -extern int doprint_allow; -#endif - // print definitions: // uprint(): always used by glue, defined as doprint() in debug mode or nothing() // os_printf(): can be redefined as doprint() @@ -65,41 +55,21 @@ extern int doprint_allow; #if STRING_IN_FLASH && !defined(USE_OPTIMIZE_PRINTF) #define USE_OPTIMIZE_PRINTF // at least used in arduino/esp8266 #endif -// os_printf_plus() missing in osapi.h (fixed in arduino's sdk-2.1): -//extern int os_printf_plus (const char * format, ...) __attribute__ ((format (printf, 1, 2))); #include // os_printf* definitions + ICACHE_RODATA_ATTR -#if UDEBUG && (UDEBUGINDEX || UDEBUGSTORE) -// doprint() is used - -#undef os_printf -#define os_printf(x...) do { doprint(x); } while (0) - -#if STRING_IN_FLASH - -#define doprint(fmt, ...) \ - do { \ - static const char flash_str[] ICACHE_RODATA_ATTR STORE_ATTR = fmt; \ - doprint_minus(flash_str, ##__VA_ARGS__); \ - } while(0) - -#else // !STRING_IN_FLASH - -#define doprint(fmt, ...) doprint_minus(fmt, ##__VA_ARGS__) - -#endif // !STRING_IN_FLASH - -int doprint_minus (const char* format, ...) __attribute__ ((format (printf, 1, 2))); // format in flash - -#else // !( UDEBUG && (UDEBUGINDEX || UDEBUGSTORE) ) - -#define doprint(x...) do { os_printf(x); } while (0) - -#endif // !( UDEBUG && (UDEBUGINDEX || UDEBUGSTORE) ) +#if defined(ARDUINO) +// os_printf() does not understand ("%hhx",0x12345678) => "78") and prints 'h' instead +// now hacking/using ::printf() from updated and patched esp-quick-toolchain-by-Earle +#include +#undef os_printf +#undef os_printf_plus +#define os_printf printf +#define os_printf_plus printf +#endif #if UDEBUG -#define uprint(x...) do { doprint(x); } while (0) +#define uprint(x...) do { os_printf(x); } while (0) #else #define uprint(x...) do { (void)0; } while (0) #endif @@ -127,7 +97,7 @@ do { if ((assertion) == 0) { \ #define ualwaysassert(assertion...) udoassert(assertion) -#define uerror(x...) do { doprint(x); } while (0) +#define uerror(x...) do { os_printf(x); } while (0) #define uhalt() do { *((int*)0) = 0; /* this triggers gdb */ } while (0) #define nl() do { uprint("\n"); } while (0) diff --git a/tools/sdk/lwip2/include/lwip-err-t.h b/tools/sdk/lwip2/include/lwip-err-t.h index d4a832ab20..e546e16b71 100644 --- a/tools/sdk/lwip2/include/lwip-err-t.h +++ b/tools/sdk/lwip2/include/lwip-err-t.h @@ -4,8 +4,8 @@ typedef unsigned char u8_t; typedef signed char s8_t; typedef unsigned short u16_t; typedef signed short s16_t; -typedef unsigned long u32_t; -typedef signed long s32_t; +typedef unsigned int u32_t; +typedef signed int s32_t; typedef unsigned long mem_ptr_t; #define LWIP_ERR_T s32_t typedef uint32_t sys_prot_t; diff --git a/tools/sdk/lwip2/include/lwip-git-hash.h b/tools/sdk/lwip2/include/lwip-git-hash.h index 96c8999789..c8a92967ba 100644 --- a/tools/sdk/lwip2/include/lwip-git-hash.h +++ b/tools/sdk/lwip2/include/lwip-git-hash.h @@ -1,5 +1,5 @@ // generated by makefiles/make-lwip2-hash #ifndef LWIP_HASH_H #define LWIP_HASH_H -#define LWIP_HASH_STR "STABLE-2_1_3_RELEASE/glue:1.2-58-g450bb63" +#define LWIP_HASH_STR "STABLE-2_1_3_RELEASE/glue:1.2-70-g4087efd" #endif // LWIP_HASH_H diff --git a/tools/sdk/lwip2/include/lwip/sys.h b/tools/sdk/lwip2/include/lwip/sys.h index 168e465baa..e03e164aec 100644 --- a/tools/sdk/lwip2/include/lwip/sys.h +++ b/tools/sdk/lwip2/include/lwip/sys.h @@ -443,7 +443,7 @@ u32_t sys_jiffies(void); * Not implementing this function means you cannot use some modules (e.g. TCP * timestamps, internal timeouts for NO_SYS==1). */ -u32_t sys_now(void); +//u32_t sys_now(void); /* Critical Region Protection */ /* These functions must be implemented in the sys_arch.c file. diff --git a/tools/sdk/lwip2/include/lwipopts.h b/tools/sdk/lwip2/include/lwipopts.h index 508245e6c0..1f05c83f9f 100644 --- a/tools/sdk/lwip2/include/lwipopts.h +++ b/tools/sdk/lwip2/include/lwipopts.h @@ -3,11 +3,10 @@ #define __CUSTOM_EXTRA_DEFINES__ #endif - #ifndef MYLWIPOPTS_H #define MYLWIPOPTS_H -// opt.h version lwip-2.1.0rc1 for esp8266 +/* opt.h version lwip-2.1.3 for esp8266 */ /** * @file @@ -996,7 +995,7 @@ #if !LWIP_IPV4 /* disable AUTOIP when IPv4 is disabled */ #undef LWIP_AUTOIP -#define LWIP_AUTOIP 0 +#define LWIP_AUTOIP 0 #endif /* !LWIP_IPV4 */ /** @@ -1564,7 +1563,7 @@ * LWIP_PBUF_REF_T: Refcount type in pbuf. * Default width of u8_t can be increased if 255 refs are not enough for you. */ -#ifndef LWIP_PBUF_REF_T +#if !defined LWIP_PBUF_REF_T || defined __DOXYGEN__ #define LWIP_PBUF_REF_T u8_t #endif @@ -2447,7 +2446,7 @@ * LWIP_IPV6_FORWARD==1: Forward IPv6 packets across netifs */ #if !defined LWIP_IPV6_FORWARD || defined __DOXYGEN__ -#define LWIP_IPV6_FORWARD 0 // 0 +#define LWIP_IPV6_FORWARD 0 #endif /** @@ -2683,7 +2682,7 @@ * servers to the DNS module. */ #if !defined LWIP_ND6_RDNSS_MAX_DNS_SERVERS || defined __DOXYGEN__ -#define LWIP_ND6_RDNSS_MAX_DNS_SERVERS 0 // 0 +#define LWIP_ND6_RDNSS_MAX_DNS_SERVERS 0 #endif /** * @} @@ -2722,7 +2721,7 @@ * void dhcp6_set_ntp_servers(u8_t num_ntp_servers, ip_addr_t* ntp_server_addrs); */ #if !defined LWIP_DHCP6_GET_NTP_SRV || defined __DOXYGEN__ -#define LWIP_DHCP6_GET_NTP_SRV 1 // with 1: dhcp6_set_ntp_servers() must be implemented +#define LWIP_DHCP6_GET_NTP_SRV 1 #endif /** @@ -3509,9 +3508,6 @@ #if !defined DHCP6_DEBUG || defined __DOXYGEN__ #define DHCP6_DEBUG LWIP_DBG_OFF #endif -/** - * @} - */ /** * NAPT_DEBUG: Enable debugging for NAPT. @@ -3520,6 +3516,10 @@ #define NAPT_DEBUG LWIP_DBG_OFF #endif +/** + * @} + */ + /** * LWIP_TESTMODE: Changes to make unit test possible */ @@ -3566,9 +3566,13 @@ #define PPPOS_SUPPORT IP_NAPT // because we don't have proxyarp yet #define PPP_SUPPORT PPPOS_SUPPORT #define PPP_SERVER 1 -#define PPP_DEBUG ULWIPDEBUG #define PRINTPKT_SUPPORT ULWIPDEBUG +#if ULWIPDEBUG +#define PPP_DEBUG LWIP_DBG_ON +#define PING_DEBUG LWIP_DBG_ON +#endif + #ifdef __cplusplus extern "C" { #endif @@ -3710,4 +3714,4 @@ void tcp_kill_timewait (void); } // extern "C" #endif -#endif // MYLWIPOPTS_H +#endif /* MYLWIPOPTS_H */ diff --git a/tools/sdk/ssl/bearssl b/tools/sdk/ssl/bearssl index 6105635531..5166f2bb03 160000 --- a/tools/sdk/ssl/bearssl +++ b/tools/sdk/ssl/bearssl @@ -1 +1 @@ -Subproject commit 6105635531027f5b298aa656d44be2289b2d434f +Subproject commit 5166f2bb03fb03597b0f2c8c7fbcf01616df67c9 diff --git a/tools/signing.py b/tools/signing.py index 40405288c9..9274bb537a 100755 --- a/tools/signing.py +++ b/tools/signing.py @@ -4,6 +4,7 @@ import argparse import hashlib import os +import struct import subprocess import sys @@ -30,7 +31,7 @@ def sign_and_write(data, priv_key, out_file): with open(out_file, "wb") as out: out.write(data) out.write(signout) - out.write(b'\x00\x01\x00\x00') + out.write(struct.pack(". -from __future__ import print_function import argparse import os import subprocess import sys +import locale + +sys.stdout = sys.stderr + + +# retrieve *system* encoding, not the one used by python internally +if sys.version_info >= (3, 11): + def get_encoding(): + return locale.getencoding() +else: + def get_encoding(): + return locale.getdefaultlocale()[1] + + +def get_segment_sizes(elf, path, mmu): + iram_size = 0 + iheap_size = 0 + icache_size = 32168 + + for line in mmu.split(): + words = line.split("=") + if line.startswith("-DMMU_IRAM_SIZE"): + iram_size = int(words[1], 16) + elif line.startswith("-DMMU_ICACHE_SIZE"): + icache_size = int(words[1], 16) + elif line.startswith("-DMMU_SEC_HEAP_SIZE"): + iheap_size = int(words[1], 16) + + sizes = [ + [ + "Variables and constants in RAM (global, static)", + [ + { + "DATA": 0, + "RODATA": 0, + "BSS": 0, + }, + 80192, + ], + ], + [ + "Instruction RAM (IRAM_ATTR, ICACHE_RAM_ATTR)", + [ + { + "ICACHE": icache_size, + "IHEAP": iheap_size, + "IRAM": 0, + }, + 65536, + ], + ], + ["Code in flash (default, ICACHE_FLASH_ATTR)", [{"IROM": 0}, 1048576]], + ] + + section_mapping = ( + (".irom0.text", "IROM"), + (".text", "IRAM"), + (".data", "DATA"), + (".rodata", "RODATA"), + (".bss", "BSS"), + ) + + cmd = [os.path.join(path, "xtensa-lx106-elf-size"), "-A", elf] + with subprocess.Popen(cmd, stdout=subprocess.PIPE, universal_newlines=True, encoding=get_encoding()) as proc: + lines = proc.stdout.readlines() + for line in lines: + words = line.split() + for section, target in section_mapping: + if not line.startswith(section): + continue + for group, (segments, total) in sizes: + if target in segments: + segments[target] += int(words[1]) + assert segments[target] <= total -def get_segment_hints(iram): - hints = {} - hints['ICACHE'] = ' - flash instruction cache' - hints['IROM'] = ' - code in flash (default or ICACHE_FLASH_ATTR)' - hints['IRAM'] = ' / ' + str(iram) + ' - code in IRAM (IRAM_ATTR, ISRs...)' - hints['DATA'] = ') - initialized variables (global, static) in RAM/HEAP' - hints['RODATA'] = ') / 81920 - constants (global, static) in RAM/HEAP' - hints['BSS'] = ') - zeroed variables (global, static) in RAM/HEAP' - return hints - -def get_segment_sizes(elf, path): - sizes = {} - sizes['ICACHE'] = 0 - sizes['IROM'] = 0 - sizes['IRAM'] = 0 - sizes['DATA'] = 0 - sizes['RODATA'] = 0 - sizes['BSS'] = 0 - p = subprocess.Popen([path + "/xtensa-lx106-elf-size", '-A', elf], stdout=subprocess.PIPE, universal_newlines=True ) - lines = p.stdout.readlines() - for line in lines: - words = line.split() - if line.startswith('.irom0.text'): - sizes['IROM'] = sizes['IROM'] + int(words[1]) - elif line.startswith('.text'): # Gets .text and .text1 - sizes['IRAM'] = sizes['IRAM'] + int(words[1]) - elif line.startswith('.data'): # Gets .text and .text1 - sizes['DATA'] = sizes['DATA'] + int(words[1]) - elif line.startswith('.rodata'): # Gets .text and .text1 - sizes['RODATA'] = sizes['RODATA'] + int(words[1]) - elif line.startswith('.bss'): # Gets .text and .text1 - sizes['BSS'] = sizes['BSS'] + int(words[1]) return sizes -def get_mmu_sizes(mmu, sizes): - iram = 0x8000 - sizes['ICACHE'] = 0x8000 - lines = mmu.split(' ') - for line in lines: - words = line.split('=') - if line.startswith('-DMMU_IRAM_SIZE'): - iram = int(words[1], 16) - elif line.startswith('-DMMU_ICACHE_SIZE'): - sizes['ICACHE'] = int(words[1], 16) - return [iram, sizes] + +def percentage(lhs, rhs): + return "{}%".format(int(100.0 * float(lhs) / float(rhs))) + + +HINTS = { + "ICACHE": "reserved space for flash instruction cache", + "IRAM": "code in IRAM", + "IHEAP": "secondary heap space", + "IROM": "code in flash", + "DATA": "initialized variables", + "RODATA": "constants", + "BSS": "zeroed variables", +} + + +def safe_prefix(n, length): + if n == length: + return "`--" + + return "|--" + + +def prefix(n, length): + if n == length: + return "╚══" + + return "╠══" + + +def filter_segments(segments): + used = 0 + number = 0 + available = [] + + for (segment, size) in segments.items(): + if not size: + continue + used += size + number += 1 + available.append((number, segment, size)) + + return (number, used, available) + def main(): - parser = argparse.ArgumentParser(description='Report the different segment sizes of a compiled ELF file') - parser.add_argument('-e', '--elf', action='http://github.com/store', required=True, help='Path to the Arduino sketch ELF') - parser.add_argument('-p', '--path', action='http://github.com/store', required=True, help='Path to Xtensa toolchain binaries') - parser.add_argument('-i', '--mmu', action='http://github.com/store', required=False, help='MMU build options') + parser = argparse.ArgumentParser( + description="Report the different segment sizes of a compiled ELF file" + ) + parser.add_argument( + "-e", + "--elf", + action="http://github.com/store", + required=True, + help="Path to the Arduino sketch ELF", + ) + parser.add_argument( + "-p", + "--path", + action="http://github.com/store", + required=True, + help="Path to Xtensa toolchain binaries", + ) + parser.add_argument( + "-i", "--mmu", action="http://github.com/store", required=False, help="MMU build options" + ) args = parser.parse_args() - sizes = get_segment_sizes(args.elf, args.path) - [iram, sizes] = get_mmu_sizes(args.mmu, sizes) - hints = get_segment_hints(iram) + sizes = get_segment_sizes(args.elf, args.path, args.mmu) + + for group, (segments, total) in sizes: + number, used, segments = filter_segments(segments) - sys.stderr.write("Executable segment sizes:" + os.linesep) - for k in sizes.keys(): - sys.stderr.write("%-7s: %-5d %s %s" % (k, sizes[k], hints[k], os.linesep)) + print(f". {group:<8}, used {used} / {total} bytes ({percentage(used, total)})") + try: + print("║ SEGMENT BYTES DESCRIPTION") + except UnicodeEncodeError: + print("| SEGMENT BYTES DESCRIPTION") + for n, segment, size in segments: + try: + print(f"{prefix(n, number)} ", end="") + except UnicodeEncodeError: + print(f"{safe_prefix(n, number)} ", end="") + print(f"{segment:<8} {size:<8} {HINTS[segment]:<16}") - return 0 -if __name__ == '__main__': - sys.exit(main()) +if __name__ == "__main__": + main() diff --git a/tools/upload.py b/tools/upload.py index 2b7016cc95..760d4dbbef 100755 --- a/tools/upload.py +++ b/tools/upload.py @@ -6,17 +6,17 @@ # 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 -import sys import os +import sys import tempfile sys.argv.pop(0) # Remove executable name -toolspath = os.path.dirname(os.path.realpath(__file__)).replace('\\', '/') # CWD in UNIX format +toolspath = os.path.dirname(os.path.realpath(__file__)) try: - sys.path.insert(0, toolspath + "/pyserial") # Add pyserial dir to search path - sys.path.insert(0, toolspath + "/esptool") # Add esptool dir to search path + 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 -except Exception: +except ImportError: sys.stderr.write("pyserial or esptool directories not found next to this upload.py tool.\n") sys.exit(1) @@ -26,7 +26,7 @@ erase_addr = '' erase_len = '' -while len(sys.argv): +while sys.argv: thisarg = sys.argv.pop(0) # We silently replace the 921kbaud setting with 460k to enable backward @@ -45,25 +45,30 @@ elif thisarg == 'write_flash': write_addr = sys.argv.pop(0) binary = sys.argv.pop(0) - elif len(thisarg): + elif thisarg: cmdline = cmdline + [thisarg] cmdline = cmdline + ['write_flash'] -if len(write_option): +if write_option: cmdline = cmdline + [write_option] cmdline = cmdline + ['--flash_size', 'detect'] cmdline = cmdline + [write_addr, binary] erase_file = '' -if len(erase_addr): +if erase_addr: # Generate temporary empty (0xff) file eraser = tempfile.mkstemp() erase_file = eraser[1] - os.write(eraser[0], bytearray([255] * int(erase_len, 0))) + os.write(eraser[0], bytearray([0xff] * int(erase_len, 0))) os.close(eraser[0]) - cmdline = cmdline + [ erase_addr, erase_file ] + cmdline = cmdline + [erase_addr, erase_file] -esptool.main(cmdline) - -if len(erase_file): - os.remove(erase_file) +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) diff --git a/libraries/ESP8266WiFi/examples/BearSSL_Server/DO-NOT-USE-THESE-CERTS-IN-YOUR-OWN-APPS b/tools/warnings/default-cflags similarity index 100% rename from libraries/ESP8266WiFi/examples/BearSSL_Server/DO-NOT-USE-THESE-CERTS-IN-YOUR-OWN-APPS rename to tools/warnings/default-cflags diff --git a/tools/warnings/default-g++ b/tools/warnings/default-cppflags similarity index 100% rename from tools/warnings/default-g++ rename to tools/warnings/default-cppflags diff --git a/tools/warnings/default-gcc b/tools/warnings/default-gcc deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tools/warnings/extra-g++ b/tools/warnings/extra-cflags similarity index 100% rename from tools/warnings/extra-g++ rename to tools/warnings/extra-cflags diff --git a/tools/warnings/extra-gcc b/tools/warnings/extra-cppflags similarity index 100% rename from tools/warnings/extra-gcc rename to tools/warnings/extra-cppflags diff --git a/tools/warnings/more-g++ b/tools/warnings/more-cflags similarity index 100% rename from tools/warnings/more-g++ rename to tools/warnings/more-cflags diff --git a/tools/warnings/more-gcc b/tools/warnings/more-cppflags similarity index 100% rename from tools/warnings/more-gcc rename to tools/warnings/more-cppflags diff --git a/tools/warnings/none-gcc b/tools/warnings/none-cflags similarity index 100% rename from tools/warnings/none-gcc rename to tools/warnings/none-cflags diff --git a/tools/warnings/none-g++ b/tools/warnings/none-cppflags similarity index 100% rename from tools/warnings/none-g++ rename to tools/warnings/none-cppflags diff --git a/variants/generic/common.h b/variants/generic/common.h index 4d5ffcf4ab..ebff2ca9e8 100644 --- a/variants/generic/common.h +++ b/variants/generic/common.h @@ -30,7 +30,10 @@ #define NUM_DIGITAL_PINS 17 #define NUM_ANALOG_INPUTS 1 -#define isFlashInterfacePin(p) ((p) >= 6 && (p) <= 11) +#define isFlashInterfacePin(p)\ + (esp_is_8285()\ + ? ((p) == 6 || (p) == 7 || (p) == 8 || (p) == 11)\ + : ((p) >= 6 && (p) <= 11)) #define analogInputToDigitalPin(p) ((p > 0) ? NOT_A_PIN : 0) #define digitalPinToInterrupt(p) (((p) < EXTERNAL_NUM_INTERRUPTS)? (p) : NOT_AN_INTERRUPT) diff --git a/variants/mercury_v1/pins_arduino.h b/variants/mercury_v1/pins_arduino.h new file mode 100644 index 0000000000..747afb5913 --- /dev/null +++ b/variants/mercury_v1/pins_arduino.h @@ -0,0 +1,71 @@ +/* + 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 0 +#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 +#define MOTOR_1_DIR (16) +#define MOTOR_1_PWM (12) +#define MOTOR_2_DIR (5) +#define MOTOR_2_PWM (4) + +//Ultrasonic Sensor +static const uint8_t USST = D7; +static const uint8_t USSE = D8; + +//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; + +//IR +static const uint8_t IR1 = D9; +static const uint8_t IR2 = D10; + +#endif /* Pins_Arduino_h */