diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000000..d9be17ddf8 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,4 @@ +9bdcd4f36a2e5285267b69b17e8fc26482dc1c72 +eea9999dc5eaf464a432f77d5b65269f9baf198d +98125f88605cd7e46e9be4e1b3ad0600dd5d2b51 +19b7a29720a6f2c95d06e2ea4baa335dcf32e68f 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 5369369eca..0000000000 --- a/.github/workflows/pull-request.yml +++ /dev/null @@ -1,270 +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 - 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 - 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". - 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 - 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 - 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 - 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 - 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 - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Style check - env: - TRAVIS_BUILD_DIR: ${{ github.workspace }} - TRAVIS_TAG: ${{ github.ref }} - run: | - sudo apt update - sudo apt install astyle - bash ./tests/ci/style_check.sh - - -# Quick test that the mocking builds succeed - mock-check: - name: Mock trivial test - runs-on: ubuntu-latest - 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 - 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 diff --git a/.github/workflows/release-to-publish.yml b/.github/workflows/release-to-publish.yml index a2a27deb76..3a80412551 100644 --- a/.github/workflows/release-to-publish.yml +++ b/.github/workflows/release-to-publish.yml @@ -28,29 +28,22 @@ on: release: types: [published] +permissions: + contents: read + jobs: package: name: Update master JSON file runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - with: - submodules: true - - uses: actions/setup-python@v2 + - uses: actions/checkout@v4 with: - python-version: '3.x' - - name: Set GIT tag name - run: | - echo "::set-env name=TRAVIS_TAG::$(git describe --exact-match --tags)" + 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 }} run: | - bash ./tests/ci/build_package.sh - # Only the regenerated JSON file will be used, but it's simpler - # than looking for it in a GH release. bash ./package/deploy_package_index.sh - 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 265b709f7a..e9311f9fce 100644 --- a/.github/workflows/tag-to-draft-release.yml +++ b/.github/workflows/tag-to-draft-release.yml @@ -14,27 +14,38 @@ jobs: package: name: Package runs-on: ubuntu-latest + defaults: + run: + shell: bash steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: true - - uses: actions/setup-python@v2 + fetch-depth: 0 + - 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 - echo "::set-env name=TRAVIS_TAG::$(git describe --exact-match --tags)" + 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/.gitignore b/.gitignore index a7bcd54c23..6413393ea9 100644 --- a/.gitignore +++ b/.gitignore @@ -3,15 +3,9 @@ tools/dist/ tools/xtensa-lx106-elf/ tools/mkspiffs/ tools/mklittlefs/ -tools/python/ tools/python3/ package/versions/ exclude.txt -tools/sdk/lib/liblwip_src.a -tools/sdk/lwip/src/build -tools/sdk/lwip/src/liblwip_src.a -tools/sdk/ld/backup -tools/sdk/ld/eagle.app.v6.common.ld tests/hosts/lcov/ diff --git a/.gitmodules b/.gitmodules index 54e63d88ed..ae42999675 100644 --- a/.gitmodules +++ b/.gitmodules @@ -24,4 +24,4 @@ url = https://github.com/arduino-libraries/Ethernet.git [submodule "tools/sdk/uzlib"] path = tools/sdk/uzlib - url = https://github.com/earlephilhower/uzlib.git + url = https://github.com/pfalcon/uzlib.git 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 dc0ef30d65..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/2.7.4_a/) +- [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/2.7.4_a/](https://arduino-esp8266.readthedocs.io/en/2.7.4_a/) +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 12afade093..1b44fe3310 100644 --- a/boards.txt +++ b/boards.txt @@ -6,6 +6,7 @@ menu.BoardModel=Model menu.ESPModule=Module +menu.UploadTool=Upload Tool menu.led=Builtin Led menu.baud=Upload Speed menu.xtal=CPU Frequency @@ -16,14 +17,15 @@ 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=Exceptions +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.waveform=Waveform Flavour 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 @@ -66,10 +69,6 @@ generic.menu.ssl.all=All SSL ciphers (most compatible) generic.menu.ssl.all.build.sslflags= generic.menu.ssl.basic=Basic SSL ciphers (lower ROM use) generic.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -generic.menu.waveform.pwm=Locked PWM -generic.menu.waveform.pwm.build.waveform= -generic.menu.waveform.phase=Locked Phase -generic.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE generic.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) generic.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 generic.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -78,6 +77,10 @@ generic.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) generic.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP generic.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) generic.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +generic.menu.mmu.ext128k=128K Heap External 23LC1024 +generic.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +generic.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +generic.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 generic.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM generic.menu.non32xfer.fast.build.non32xferflags= generic.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -90,7 +93,7 @@ generic.menu.ResetMethod.nodtr_nosync=no dtr, no_sync generic.menu.ResetMethod.nodtr_nosync.upload.resetmethod=--before no_reset_no_sync --after soft_reset generic.menu.CrystalFreq.26=26 MHz generic.menu.CrystalFreq.40=40 MHz -generic.menu.CrystalFreq.40.build.extra_flags=-DF_CRYSTAL=40000000 -DESP8266 +generic.menu.CrystalFreq.40.build.extra_flags=-DF_CRYSTAL=40000000 generic.menu.FlashFreq.40=40MHz generic.menu.FlashFreq.40.build.flash_freq=40 generic.menu.FlashFreq.80=80MHz @@ -113,251 +116,209 @@ generic.menu.FlashMode.qio.build.flash_mode=qio generic.menu.FlashMode.qio.build.flash_flags=-DFLASHMODE_QIO generic.menu.eesz.1M64=1MB (FS:64KB OTA:~470KB) generic.menu.eesz.1M64.build.flash_size=1M -generic.menu.eesz.1M64.build.flash_size_bytes=0x100000 generic.menu.eesz.1M64.build.flash_ld=eagle.flash.1m64.ld generic.menu.eesz.1M64.build.spiffs_pagesize=256 -generic.menu.eesz.1M64.upload.maximum_size=958448 generic.menu.eesz.1M64.build.rfcal_addr=0xFC000 generic.menu.eesz.1M64.build.spiffs_start=0xEB000 generic.menu.eesz.1M64.build.spiffs_end=0xFB000 generic.menu.eesz.1M64.build.spiffs_blocksize=4096 generic.menu.eesz.1M128=1MB (FS:128KB OTA:~438KB) generic.menu.eesz.1M128.build.flash_size=1M -generic.menu.eesz.1M128.build.flash_size_bytes=0x100000 generic.menu.eesz.1M128.build.flash_ld=eagle.flash.1m128.ld generic.menu.eesz.1M128.build.spiffs_pagesize=256 -generic.menu.eesz.1M128.upload.maximum_size=892912 generic.menu.eesz.1M128.build.rfcal_addr=0xFC000 generic.menu.eesz.1M128.build.spiffs_start=0xDB000 generic.menu.eesz.1M128.build.spiffs_end=0xFB000 generic.menu.eesz.1M128.build.spiffs_blocksize=4096 generic.menu.eesz.1M144=1MB (FS:144KB OTA:~430KB) generic.menu.eesz.1M144.build.flash_size=1M -generic.menu.eesz.1M144.build.flash_size_bytes=0x100000 generic.menu.eesz.1M144.build.flash_ld=eagle.flash.1m144.ld generic.menu.eesz.1M144.build.spiffs_pagesize=256 -generic.menu.eesz.1M144.upload.maximum_size=876528 generic.menu.eesz.1M144.build.rfcal_addr=0xFC000 generic.menu.eesz.1M144.build.spiffs_start=0xD7000 generic.menu.eesz.1M144.build.spiffs_end=0xFB000 generic.menu.eesz.1M144.build.spiffs_blocksize=4096 generic.menu.eesz.1M160=1MB (FS:160KB OTA:~422KB) generic.menu.eesz.1M160.build.flash_size=1M -generic.menu.eesz.1M160.build.flash_size_bytes=0x100000 generic.menu.eesz.1M160.build.flash_ld=eagle.flash.1m160.ld generic.menu.eesz.1M160.build.spiffs_pagesize=256 -generic.menu.eesz.1M160.upload.maximum_size=860144 generic.menu.eesz.1M160.build.rfcal_addr=0xFC000 generic.menu.eesz.1M160.build.spiffs_start=0xD3000 generic.menu.eesz.1M160.build.spiffs_end=0xFB000 generic.menu.eesz.1M160.build.spiffs_blocksize=4096 generic.menu.eesz.1M192=1MB (FS:192KB OTA:~406KB) generic.menu.eesz.1M192.build.flash_size=1M -generic.menu.eesz.1M192.build.flash_size_bytes=0x100000 generic.menu.eesz.1M192.build.flash_ld=eagle.flash.1m192.ld generic.menu.eesz.1M192.build.spiffs_pagesize=256 -generic.menu.eesz.1M192.upload.maximum_size=827376 generic.menu.eesz.1M192.build.rfcal_addr=0xFC000 generic.menu.eesz.1M192.build.spiffs_start=0xCB000 generic.menu.eesz.1M192.build.spiffs_end=0xFB000 generic.menu.eesz.1M192.build.spiffs_blocksize=4096 generic.menu.eesz.1M256=1MB (FS:256KB OTA:~374KB) generic.menu.eesz.1M256.build.flash_size=1M -generic.menu.eesz.1M256.build.flash_size_bytes=0x100000 generic.menu.eesz.1M256.build.flash_ld=eagle.flash.1m256.ld generic.menu.eesz.1M256.build.spiffs_pagesize=256 -generic.menu.eesz.1M256.upload.maximum_size=761840 generic.menu.eesz.1M256.build.rfcal_addr=0xFC000 generic.menu.eesz.1M256.build.spiffs_start=0xBB000 generic.menu.eesz.1M256.build.spiffs_end=0xFB000 generic.menu.eesz.1M256.build.spiffs_blocksize=4096 generic.menu.eesz.1M512=1MB (FS:512KB OTA:~246KB) generic.menu.eesz.1M512.build.flash_size=1M -generic.menu.eesz.1M512.build.flash_size_bytes=0x100000 generic.menu.eesz.1M512.build.flash_ld=eagle.flash.1m512.ld generic.menu.eesz.1M512.build.spiffs_pagesize=256 -generic.menu.eesz.1M512.upload.maximum_size=499696 generic.menu.eesz.1M512.build.rfcal_addr=0xFC000 generic.menu.eesz.1M512.build.spiffs_start=0x7B000 generic.menu.eesz.1M512.build.spiffs_end=0xFB000 generic.menu.eesz.1M512.build.spiffs_blocksize=8192 generic.menu.eesz.1M=1MB (FS:none OTA:~502KB) generic.menu.eesz.1M.build.flash_size=1M -generic.menu.eesz.1M.build.flash_size_bytes=0x100000 generic.menu.eesz.1M.build.flash_ld=eagle.flash.1m.ld generic.menu.eesz.1M.build.spiffs_pagesize=256 -generic.menu.eesz.1M.upload.maximum_size=1023984 generic.menu.eesz.1M.build.rfcal_addr=0xFC000 generic.menu.eesz.2M64=2MB (FS:64KB OTA:~992KB) generic.menu.eesz.2M64.build.flash_size=2M -generic.menu.eesz.2M64.build.flash_size_bytes=0x200000 generic.menu.eesz.2M64.build.flash_ld=eagle.flash.2m64.ld generic.menu.eesz.2M64.build.spiffs_pagesize=256 -generic.menu.eesz.2M64.upload.maximum_size=1044464 generic.menu.eesz.2M64.build.rfcal_addr=0x1FC000 generic.menu.eesz.2M64.build.spiffs_start=0x1F0000 generic.menu.eesz.2M64.build.spiffs_end=0x1FB000 generic.menu.eesz.2M64.build.spiffs_blocksize=4096 generic.menu.eesz.2M128=2MB (FS:128KB OTA:~960KB) generic.menu.eesz.2M128.build.flash_size=2M -generic.menu.eesz.2M128.build.flash_size_bytes=0x200000 generic.menu.eesz.2M128.build.flash_ld=eagle.flash.2m128.ld generic.menu.eesz.2M128.build.spiffs_pagesize=256 -generic.menu.eesz.2M128.upload.maximum_size=1044464 generic.menu.eesz.2M128.build.rfcal_addr=0x1FC000 generic.menu.eesz.2M128.build.spiffs_start=0x1E0000 generic.menu.eesz.2M128.build.spiffs_end=0x1FB000 generic.menu.eesz.2M128.build.spiffs_blocksize=4096 generic.menu.eesz.2M256=2MB (FS:256KB OTA:~896KB) generic.menu.eesz.2M256.build.flash_size=2M -generic.menu.eesz.2M256.build.flash_size_bytes=0x200000 generic.menu.eesz.2M256.build.flash_ld=eagle.flash.2m256.ld generic.menu.eesz.2M256.build.spiffs_pagesize=256 -generic.menu.eesz.2M256.upload.maximum_size=1044464 generic.menu.eesz.2M256.build.rfcal_addr=0x1FC000 generic.menu.eesz.2M256.build.spiffs_start=0x1C0000 generic.menu.eesz.2M256.build.spiffs_end=0x1FB000 generic.menu.eesz.2M256.build.spiffs_blocksize=4096 generic.menu.eesz.2M512=2MB (FS:512KB OTA:~768KB) generic.menu.eesz.2M512.build.flash_size=2M -generic.menu.eesz.2M512.build.flash_size_bytes=0x200000 generic.menu.eesz.2M512.build.flash_ld=eagle.flash.2m512.ld generic.menu.eesz.2M512.build.spiffs_pagesize=256 -generic.menu.eesz.2M512.upload.maximum_size=1044464 generic.menu.eesz.2M512.build.rfcal_addr=0x1FC000 generic.menu.eesz.2M512.build.spiffs_start=0x180000 generic.menu.eesz.2M512.build.spiffs_end=0x1FA000 generic.menu.eesz.2M512.build.spiffs_blocksize=8192 generic.menu.eesz.2M1M=2MB (FS:1MB OTA:~512KB) generic.menu.eesz.2M1M.build.flash_size=2M -generic.menu.eesz.2M1M.build.flash_size_bytes=0x200000 generic.menu.eesz.2M1M.build.flash_ld=eagle.flash.2m1m.ld generic.menu.eesz.2M1M.build.spiffs_pagesize=256 -generic.menu.eesz.2M1M.upload.maximum_size=1044464 generic.menu.eesz.2M1M.build.rfcal_addr=0x1FC000 generic.menu.eesz.2M1M.build.spiffs_start=0x100000 generic.menu.eesz.2M1M.build.spiffs_end=0x1FA000 generic.menu.eesz.2M1M.build.spiffs_blocksize=8192 generic.menu.eesz.2M=2MB (FS:none OTA:~1019KB) generic.menu.eesz.2M.build.flash_size=2M -generic.menu.eesz.2M.build.flash_size_bytes=0x200000 generic.menu.eesz.2M.build.flash_ld=eagle.flash.2m.ld generic.menu.eesz.2M.build.spiffs_pagesize=256 -generic.menu.eesz.2M.upload.maximum_size=1044464 generic.menu.eesz.2M.build.rfcal_addr=0x1FC000 generic.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) generic.menu.eesz.4M2M.build.flash_size=4M -generic.menu.eesz.4M2M.build.flash_size_bytes=0x400000 generic.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld generic.menu.eesz.4M2M.build.spiffs_pagesize=256 -generic.menu.eesz.4M2M.upload.maximum_size=1044464 generic.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 generic.menu.eesz.4M2M.build.spiffs_start=0x200000 generic.menu.eesz.4M2M.build.spiffs_end=0x3FA000 generic.menu.eesz.4M2M.build.spiffs_blocksize=8192 generic.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) generic.menu.eesz.4M3M.build.flash_size=4M -generic.menu.eesz.4M3M.build.flash_size_bytes=0x400000 generic.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld generic.menu.eesz.4M3M.build.spiffs_pagesize=256 -generic.menu.eesz.4M3M.upload.maximum_size=1044464 generic.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 generic.menu.eesz.4M3M.build.spiffs_start=0x100000 generic.menu.eesz.4M3M.build.spiffs_end=0x3FA000 generic.menu.eesz.4M3M.build.spiffs_blocksize=8192 generic.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) generic.menu.eesz.4M1M.build.flash_size=4M -generic.menu.eesz.4M1M.build.flash_size_bytes=0x400000 generic.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld generic.menu.eesz.4M1M.build.spiffs_pagesize=256 -generic.menu.eesz.4M1M.upload.maximum_size=1044464 generic.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 generic.menu.eesz.4M1M.build.spiffs_start=0x300000 generic.menu.eesz.4M1M.build.spiffs_end=0x3FA000 generic.menu.eesz.4M1M.build.spiffs_blocksize=8192 generic.menu.eesz.4M=4MB (FS:none OTA:~1019KB) generic.menu.eesz.4M.build.flash_size=4M -generic.menu.eesz.4M.build.flash_size_bytes=0x400000 generic.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld generic.menu.eesz.4M.build.spiffs_pagesize=256 -generic.menu.eesz.4M.upload.maximum_size=1044464 generic.menu.eesz.4M.build.rfcal_addr=0x3FC000 generic.menu.eesz.8M6M=8MB (FS:6MB OTA:~1019KB) generic.menu.eesz.8M6M.build.flash_size=8M -generic.menu.eesz.8M6M.build.flash_size_bytes=0x800000 generic.menu.eesz.8M6M.build.flash_ld=eagle.flash.8m6m.ld generic.menu.eesz.8M6M.build.spiffs_pagesize=256 -generic.menu.eesz.8M6M.upload.maximum_size=1044464 generic.menu.eesz.8M6M.build.rfcal_addr=0x7FC000 generic.menu.eesz.8M6M.build.spiffs_start=0x200000 generic.menu.eesz.8M6M.build.spiffs_end=0x7FA000 generic.menu.eesz.8M6M.build.spiffs_blocksize=8192 generic.menu.eesz.8M7M=8MB (FS:7MB OTA:~512KB) generic.menu.eesz.8M7M.build.flash_size=8M -generic.menu.eesz.8M7M.build.flash_size_bytes=0x800000 generic.menu.eesz.8M7M.build.flash_ld=eagle.flash.8m7m.ld generic.menu.eesz.8M7M.build.spiffs_pagesize=256 -generic.menu.eesz.8M7M.upload.maximum_size=1044464 generic.menu.eesz.8M7M.build.rfcal_addr=0x7FC000 generic.menu.eesz.8M7M.build.spiffs_start=0x100000 generic.menu.eesz.8M7M.build.spiffs_end=0x7FA000 generic.menu.eesz.8M7M.build.spiffs_blocksize=8192 +generic.menu.eesz.8M=8MB (FS:none OTA:~1019KB) +generic.menu.eesz.8M.build.flash_size=8M +generic.menu.eesz.8M.build.flash_ld=eagle.flash.8m.ld +generic.menu.eesz.8M.build.spiffs_pagesize=256 +generic.menu.eesz.8M.build.rfcal_addr=0x7FC000 generic.menu.eesz.16M14M=16MB (FS:14MB OTA:~1019KB) generic.menu.eesz.16M14M.build.flash_size=16M -generic.menu.eesz.16M14M.build.flash_size_bytes=0x1000000 generic.menu.eesz.16M14M.build.flash_ld=eagle.flash.16m14m.ld generic.menu.eesz.16M14M.build.spiffs_pagesize=256 -generic.menu.eesz.16M14M.upload.maximum_size=1044464 generic.menu.eesz.16M14M.build.rfcal_addr=0xFFC000 generic.menu.eesz.16M14M.build.spiffs_start=0x200000 generic.menu.eesz.16M14M.build.spiffs_end=0xFFA000 generic.menu.eesz.16M14M.build.spiffs_blocksize=8192 generic.menu.eesz.16M15M=16MB (FS:15MB OTA:~512KB) generic.menu.eesz.16M15M.build.flash_size=16M -generic.menu.eesz.16M15M.build.flash_size_bytes=0x1000000 generic.menu.eesz.16M15M.build.flash_ld=eagle.flash.16m15m.ld generic.menu.eesz.16M15M.build.spiffs_pagesize=256 -generic.menu.eesz.16M15M.upload.maximum_size=1044464 generic.menu.eesz.16M15M.build.rfcal_addr=0xFFC000 generic.menu.eesz.16M15M.build.spiffs_start=0x100000 generic.menu.eesz.16M15M.build.spiffs_end=0xFFA000 generic.menu.eesz.16M15M.build.spiffs_blocksize=8192 +generic.menu.eesz.16M=16MB (FS:none OTA:~1019KB) +generic.menu.eesz.16M.build.flash_size=16M +generic.menu.eesz.16M.build.flash_ld=eagle.flash.16m.ld +generic.menu.eesz.16M.build.spiffs_pagesize=256 +generic.menu.eesz.16M.build.rfcal_addr=0xFFC000 generic.menu.eesz.512K32=512KB (FS:32KB OTA:~230KB) generic.menu.eesz.512K32.build.flash_size=512K -generic.menu.eesz.512K32.build.flash_size_bytes=0x80000 generic.menu.eesz.512K32.build.flash_ld=eagle.flash.512k32.ld generic.menu.eesz.512K32.build.spiffs_pagesize=256 -generic.menu.eesz.512K32.upload.maximum_size=466928 generic.menu.eesz.512K32.build.rfcal_addr=0x7C000 generic.menu.eesz.512K32.build.spiffs_start=0x73000 generic.menu.eesz.512K32.build.spiffs_end=0x7B000 generic.menu.eesz.512K32.build.spiffs_blocksize=4096 generic.menu.eesz.512K64=512KB (FS:64KB OTA:~214KB) generic.menu.eesz.512K64.build.flash_size=512K -generic.menu.eesz.512K64.build.flash_size_bytes=0x80000 generic.menu.eesz.512K64.build.flash_ld=eagle.flash.512k64.ld generic.menu.eesz.512K64.build.spiffs_pagesize=256 -generic.menu.eesz.512K64.upload.maximum_size=434160 generic.menu.eesz.512K64.build.rfcal_addr=0x7C000 generic.menu.eesz.512K64.build.spiffs_start=0x6B000 generic.menu.eesz.512K64.build.spiffs_end=0x7B000 generic.menu.eesz.512K64.build.spiffs_blocksize=4096 generic.menu.eesz.512K128=512KB (FS:128KB OTA:~182KB) generic.menu.eesz.512K128.build.flash_size=512K -generic.menu.eesz.512K128.build.flash_size_bytes=0x80000 generic.menu.eesz.512K128.build.flash_ld=eagle.flash.512k128.ld generic.menu.eesz.512K128.build.spiffs_pagesize=256 -generic.menu.eesz.512K128.upload.maximum_size=368624 generic.menu.eesz.512K128.build.rfcal_addr=0x7C000 generic.menu.eesz.512K128.build.spiffs_start=0x5B000 generic.menu.eesz.512K128.build.spiffs_end=0x7B000 generic.menu.eesz.512K128.build.spiffs_blocksize=4096 generic.menu.eesz.512K=512KB (FS:none OTA:~246KB) generic.menu.eesz.512K.build.flash_size=512K -generic.menu.eesz.512K.build.flash_size_bytes=0x80000 generic.menu.eesz.512K.build.flash_ld=eagle.flash.512k.ld generic.menu.eesz.512K.build.spiffs_pagesize=256 -generic.menu.eesz.512K.upload.maximum_size=499696 generic.menu.eesz.512K.build.rfcal_addr=0x7C000 generic.menu.led.2=2 generic.menu.led.2.build.led=-DLED_BUILTIN=2 @@ -401,10 +362,12 @@ generic.menu.sdk.nonosdk_191105=nonos-sdk 2.2.1+113 (191105) generic.menu.sdk.nonosdk_191105.build.sdk=NONOSDK22x_191105 generic.menu.sdk.nonosdk_191024=nonos-sdk 2.2.1+111 (191024) generic.menu.sdk.nonosdk_191024.build.sdk=NONOSDK22x_191024 +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 @@ -437,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 @@ -481,10 +450,22 @@ generic.menu.lvl.OOM=OOM generic.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM generic.menu.lvl.MDNS=MDNS generic.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +generic.menu.lvl.HWDT=HWDT +generic.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +generic.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +generic.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K generic.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS generic.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 +generic.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +generic.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 +generic.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +generic.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 generic.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS generic.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 +generic.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +generic.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 +generic.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 +generic.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 generic.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG generic.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG generic.menu.wipe.none=Only Sketch @@ -511,6 +492,15 @@ generic.menu.baud.921600=921600 generic.menu.baud.921600.upload.speed=921600 generic.menu.baud.3000000=3000000 generic.menu.baud.3000000.upload.speed=3000000 +generic.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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 @@ -525,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 @@ -551,10 +542,6 @@ esp8285.menu.ssl.all=All SSL ciphers (most compatible) esp8285.menu.ssl.all.build.sslflags= esp8285.menu.ssl.basic=Basic SSL ciphers (lower ROM use) esp8285.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -esp8285.menu.waveform.pwm=Locked PWM -esp8285.menu.waveform.pwm.build.waveform= -esp8285.menu.waveform.phase=Locked Phase -esp8285.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE esp8285.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) esp8285.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 esp8285.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -563,6 +550,10 @@ esp8285.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) esp8285.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP esp8285.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) esp8285.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +esp8285.menu.mmu.ext128k=128K Heap External 23LC1024 +esp8285.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +esp8285.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +esp8285.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 esp8285.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM esp8285.menu.non32xfer.fast.build.non32xferflags= esp8285.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -575,143 +566,115 @@ esp8285.menu.ResetMethod.nodtr_nosync=no dtr, no_sync esp8285.menu.ResetMethod.nodtr_nosync.upload.resetmethod=--before no_reset_no_sync --after soft_reset esp8285.menu.CrystalFreq.26=26 MHz esp8285.menu.CrystalFreq.40=40 MHz -esp8285.menu.CrystalFreq.40.build.extra_flags=-DF_CRYSTAL=40000000 -DESP8266 +esp8285.menu.CrystalFreq.40.build.extra_flags=-DF_CRYSTAL=40000000 esp8285.build.flash_mode=dout esp8285.build.flash_flags=-DFLASHMODE_DOUT esp8285.build.flash_freq=40 esp8285.menu.eesz.1M64=1MB (FS:64KB OTA:~470KB) esp8285.menu.eesz.1M64.build.flash_size=1M -esp8285.menu.eesz.1M64.build.flash_size_bytes=0x100000 esp8285.menu.eesz.1M64.build.flash_ld=eagle.flash.1m64.ld esp8285.menu.eesz.1M64.build.spiffs_pagesize=256 -esp8285.menu.eesz.1M64.upload.maximum_size=958448 esp8285.menu.eesz.1M64.build.rfcal_addr=0xFC000 esp8285.menu.eesz.1M64.build.spiffs_start=0xEB000 esp8285.menu.eesz.1M64.build.spiffs_end=0xFB000 esp8285.menu.eesz.1M64.build.spiffs_blocksize=4096 esp8285.menu.eesz.1M128=1MB (FS:128KB OTA:~438KB) esp8285.menu.eesz.1M128.build.flash_size=1M -esp8285.menu.eesz.1M128.build.flash_size_bytes=0x100000 esp8285.menu.eesz.1M128.build.flash_ld=eagle.flash.1m128.ld esp8285.menu.eesz.1M128.build.spiffs_pagesize=256 -esp8285.menu.eesz.1M128.upload.maximum_size=892912 esp8285.menu.eesz.1M128.build.rfcal_addr=0xFC000 esp8285.menu.eesz.1M128.build.spiffs_start=0xDB000 esp8285.menu.eesz.1M128.build.spiffs_end=0xFB000 esp8285.menu.eesz.1M128.build.spiffs_blocksize=4096 esp8285.menu.eesz.1M144=1MB (FS:144KB OTA:~430KB) esp8285.menu.eesz.1M144.build.flash_size=1M -esp8285.menu.eesz.1M144.build.flash_size_bytes=0x100000 esp8285.menu.eesz.1M144.build.flash_ld=eagle.flash.1m144.ld esp8285.menu.eesz.1M144.build.spiffs_pagesize=256 -esp8285.menu.eesz.1M144.upload.maximum_size=876528 esp8285.menu.eesz.1M144.build.rfcal_addr=0xFC000 esp8285.menu.eesz.1M144.build.spiffs_start=0xD7000 esp8285.menu.eesz.1M144.build.spiffs_end=0xFB000 esp8285.menu.eesz.1M144.build.spiffs_blocksize=4096 esp8285.menu.eesz.1M160=1MB (FS:160KB OTA:~422KB) esp8285.menu.eesz.1M160.build.flash_size=1M -esp8285.menu.eesz.1M160.build.flash_size_bytes=0x100000 esp8285.menu.eesz.1M160.build.flash_ld=eagle.flash.1m160.ld esp8285.menu.eesz.1M160.build.spiffs_pagesize=256 -esp8285.menu.eesz.1M160.upload.maximum_size=860144 esp8285.menu.eesz.1M160.build.rfcal_addr=0xFC000 esp8285.menu.eesz.1M160.build.spiffs_start=0xD3000 esp8285.menu.eesz.1M160.build.spiffs_end=0xFB000 esp8285.menu.eesz.1M160.build.spiffs_blocksize=4096 esp8285.menu.eesz.1M192=1MB (FS:192KB OTA:~406KB) esp8285.menu.eesz.1M192.build.flash_size=1M -esp8285.menu.eesz.1M192.build.flash_size_bytes=0x100000 esp8285.menu.eesz.1M192.build.flash_ld=eagle.flash.1m192.ld esp8285.menu.eesz.1M192.build.spiffs_pagesize=256 -esp8285.menu.eesz.1M192.upload.maximum_size=827376 esp8285.menu.eesz.1M192.build.rfcal_addr=0xFC000 esp8285.menu.eesz.1M192.build.spiffs_start=0xCB000 esp8285.menu.eesz.1M192.build.spiffs_end=0xFB000 esp8285.menu.eesz.1M192.build.spiffs_blocksize=4096 esp8285.menu.eesz.1M256=1MB (FS:256KB OTA:~374KB) esp8285.menu.eesz.1M256.build.flash_size=1M -esp8285.menu.eesz.1M256.build.flash_size_bytes=0x100000 esp8285.menu.eesz.1M256.build.flash_ld=eagle.flash.1m256.ld esp8285.menu.eesz.1M256.build.spiffs_pagesize=256 -esp8285.menu.eesz.1M256.upload.maximum_size=761840 esp8285.menu.eesz.1M256.build.rfcal_addr=0xFC000 esp8285.menu.eesz.1M256.build.spiffs_start=0xBB000 esp8285.menu.eesz.1M256.build.spiffs_end=0xFB000 esp8285.menu.eesz.1M256.build.spiffs_blocksize=4096 esp8285.menu.eesz.1M512=1MB (FS:512KB OTA:~246KB) esp8285.menu.eesz.1M512.build.flash_size=1M -esp8285.menu.eesz.1M512.build.flash_size_bytes=0x100000 esp8285.menu.eesz.1M512.build.flash_ld=eagle.flash.1m512.ld esp8285.menu.eesz.1M512.build.spiffs_pagesize=256 -esp8285.menu.eesz.1M512.upload.maximum_size=499696 esp8285.menu.eesz.1M512.build.rfcal_addr=0xFC000 esp8285.menu.eesz.1M512.build.spiffs_start=0x7B000 esp8285.menu.eesz.1M512.build.spiffs_end=0xFB000 esp8285.menu.eesz.1M512.build.spiffs_blocksize=8192 esp8285.menu.eesz.1M=1MB (FS:none OTA:~502KB) esp8285.menu.eesz.1M.build.flash_size=1M -esp8285.menu.eesz.1M.build.flash_size_bytes=0x100000 esp8285.menu.eesz.1M.build.flash_ld=eagle.flash.1m.ld esp8285.menu.eesz.1M.build.spiffs_pagesize=256 -esp8285.menu.eesz.1M.upload.maximum_size=1023984 esp8285.menu.eesz.1M.build.rfcal_addr=0xFC000 esp8285.menu.eesz.2M64=2MB (FS:64KB OTA:~992KB) esp8285.menu.eesz.2M64.build.flash_size=2M -esp8285.menu.eesz.2M64.build.flash_size_bytes=0x200000 esp8285.menu.eesz.2M64.build.flash_ld=eagle.flash.2m64.ld esp8285.menu.eesz.2M64.build.spiffs_pagesize=256 -esp8285.menu.eesz.2M64.upload.maximum_size=1044464 esp8285.menu.eesz.2M64.build.rfcal_addr=0x1FC000 esp8285.menu.eesz.2M64.build.spiffs_start=0x1F0000 esp8285.menu.eesz.2M64.build.spiffs_end=0x1FB000 esp8285.menu.eesz.2M64.build.spiffs_blocksize=4096 esp8285.menu.eesz.2M128=2MB (FS:128KB OTA:~960KB) esp8285.menu.eesz.2M128.build.flash_size=2M -esp8285.menu.eesz.2M128.build.flash_size_bytes=0x200000 esp8285.menu.eesz.2M128.build.flash_ld=eagle.flash.2m128.ld esp8285.menu.eesz.2M128.build.spiffs_pagesize=256 -esp8285.menu.eesz.2M128.upload.maximum_size=1044464 esp8285.menu.eesz.2M128.build.rfcal_addr=0x1FC000 esp8285.menu.eesz.2M128.build.spiffs_start=0x1E0000 esp8285.menu.eesz.2M128.build.spiffs_end=0x1FB000 esp8285.menu.eesz.2M128.build.spiffs_blocksize=4096 esp8285.menu.eesz.2M256=2MB (FS:256KB OTA:~896KB) esp8285.menu.eesz.2M256.build.flash_size=2M -esp8285.menu.eesz.2M256.build.flash_size_bytes=0x200000 esp8285.menu.eesz.2M256.build.flash_ld=eagle.flash.2m256.ld esp8285.menu.eesz.2M256.build.spiffs_pagesize=256 -esp8285.menu.eesz.2M256.upload.maximum_size=1044464 esp8285.menu.eesz.2M256.build.rfcal_addr=0x1FC000 esp8285.menu.eesz.2M256.build.spiffs_start=0x1C0000 esp8285.menu.eesz.2M256.build.spiffs_end=0x1FB000 esp8285.menu.eesz.2M256.build.spiffs_blocksize=4096 esp8285.menu.eesz.2M512=2MB (FS:512KB OTA:~768KB) esp8285.menu.eesz.2M512.build.flash_size=2M -esp8285.menu.eesz.2M512.build.flash_size_bytes=0x200000 esp8285.menu.eesz.2M512.build.flash_ld=eagle.flash.2m512.ld esp8285.menu.eesz.2M512.build.spiffs_pagesize=256 -esp8285.menu.eesz.2M512.upload.maximum_size=1044464 esp8285.menu.eesz.2M512.build.rfcal_addr=0x1FC000 esp8285.menu.eesz.2M512.build.spiffs_start=0x180000 esp8285.menu.eesz.2M512.build.spiffs_end=0x1FA000 esp8285.menu.eesz.2M512.build.spiffs_blocksize=8192 esp8285.menu.eesz.2M1M=2MB (FS:1MB OTA:~512KB) esp8285.menu.eesz.2M1M.build.flash_size=2M -esp8285.menu.eesz.2M1M.build.flash_size_bytes=0x200000 esp8285.menu.eesz.2M1M.build.flash_ld=eagle.flash.2m1m.ld esp8285.menu.eesz.2M1M.build.spiffs_pagesize=256 -esp8285.menu.eesz.2M1M.upload.maximum_size=1044464 esp8285.menu.eesz.2M1M.build.rfcal_addr=0x1FC000 esp8285.menu.eesz.2M1M.build.spiffs_start=0x100000 esp8285.menu.eesz.2M1M.build.spiffs_end=0x1FA000 esp8285.menu.eesz.2M1M.build.spiffs_blocksize=8192 esp8285.menu.eesz.2M=2MB (FS:none OTA:~1019KB) esp8285.menu.eesz.2M.build.flash_size=2M -esp8285.menu.eesz.2M.build.flash_size_bytes=0x200000 esp8285.menu.eesz.2M.build.flash_ld=eagle.flash.2m.ld esp8285.menu.eesz.2M.build.spiffs_pagesize=256 -esp8285.menu.eesz.2M.upload.maximum_size=1044464 esp8285.menu.eesz.2M.build.rfcal_addr=0x1FC000 esp8285.menu.led.2=2 esp8285.menu.led.2.build.led=-DLED_BUILTIN=2 @@ -755,10 +718,12 @@ esp8285.menu.sdk.nonosdk_191105=nonos-sdk 2.2.1+113 (191105) esp8285.menu.sdk.nonosdk_191105.build.sdk=NONOSDK22x_191105 esp8285.menu.sdk.nonosdk_191024=nonos-sdk 2.2.1+111 (191024) esp8285.menu.sdk.nonosdk_191024.build.sdk=NONOSDK22x_191024 +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 @@ -791,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 @@ -835,10 +806,22 @@ esp8285.menu.lvl.OOM=OOM esp8285.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM esp8285.menu.lvl.MDNS=MDNS esp8285.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +esp8285.menu.lvl.HWDT=HWDT +esp8285.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +esp8285.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +esp8285.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K esp8285.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS esp8285.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 +esp8285.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +esp8285.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 +esp8285.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +esp8285.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 esp8285.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS esp8285.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 +esp8285.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +esp8285.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 +esp8285.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 +esp8285.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 esp8285.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG esp8285.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG esp8285.menu.wipe.none=Only Sketch @@ -865,6 +848,15 @@ esp8285.menu.baud.921600=921600 esp8285.menu.baud.921600.upload.speed=921600 esp8285.menu.baud.3000000=3000000 esp8285.menu.baud.3000000.upload.speed=3000000 +esp8285.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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 @@ -880,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 @@ -906,10 +899,6 @@ gen4iod.menu.ssl.all=All SSL ciphers (most compatible) gen4iod.menu.ssl.all.build.sslflags= gen4iod.menu.ssl.basic=Basic SSL ciphers (lower ROM use) gen4iod.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -gen4iod.menu.waveform.pwm=Locked PWM -gen4iod.menu.waveform.pwm.build.waveform= -gen4iod.menu.waveform.phase=Locked Phase -gen4iod.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE gen4iod.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) gen4iod.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 gen4iod.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -918,6 +907,10 @@ gen4iod.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) gen4iod.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP gen4iod.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) gen4iod.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +gen4iod.menu.mmu.ext128k=128K Heap External 23LC1024 +gen4iod.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +gen4iod.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +gen4iod.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 gen4iod.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM gen4iod.menu.non32xfer.fast.build.non32xferflags= gen4iod.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -938,97 +931,77 @@ gen4iod.menu.FlashMode.qio.build.flash_flags=-DFLASHMODE_QIO gen4iod.build.flash_freq=80 gen4iod.menu.eesz.2M64=2MB (FS:64KB OTA:~992KB) gen4iod.menu.eesz.2M64.build.flash_size=2M -gen4iod.menu.eesz.2M64.build.flash_size_bytes=0x200000 gen4iod.menu.eesz.2M64.build.flash_ld=eagle.flash.2m64.ld gen4iod.menu.eesz.2M64.build.spiffs_pagesize=256 -gen4iod.menu.eesz.2M64.upload.maximum_size=1044464 gen4iod.menu.eesz.2M64.build.rfcal_addr=0x1FC000 gen4iod.menu.eesz.2M64.build.spiffs_start=0x1F0000 gen4iod.menu.eesz.2M64.build.spiffs_end=0x1FB000 gen4iod.menu.eesz.2M64.build.spiffs_blocksize=4096 gen4iod.menu.eesz.2M128=2MB (FS:128KB OTA:~960KB) gen4iod.menu.eesz.2M128.build.flash_size=2M -gen4iod.menu.eesz.2M128.build.flash_size_bytes=0x200000 gen4iod.menu.eesz.2M128.build.flash_ld=eagle.flash.2m128.ld gen4iod.menu.eesz.2M128.build.spiffs_pagesize=256 -gen4iod.menu.eesz.2M128.upload.maximum_size=1044464 gen4iod.menu.eesz.2M128.build.rfcal_addr=0x1FC000 gen4iod.menu.eesz.2M128.build.spiffs_start=0x1E0000 gen4iod.menu.eesz.2M128.build.spiffs_end=0x1FB000 gen4iod.menu.eesz.2M128.build.spiffs_blocksize=4096 gen4iod.menu.eesz.2M256=2MB (FS:256KB OTA:~896KB) gen4iod.menu.eesz.2M256.build.flash_size=2M -gen4iod.menu.eesz.2M256.build.flash_size_bytes=0x200000 gen4iod.menu.eesz.2M256.build.flash_ld=eagle.flash.2m256.ld gen4iod.menu.eesz.2M256.build.spiffs_pagesize=256 -gen4iod.menu.eesz.2M256.upload.maximum_size=1044464 gen4iod.menu.eesz.2M256.build.rfcal_addr=0x1FC000 gen4iod.menu.eesz.2M256.build.spiffs_start=0x1C0000 gen4iod.menu.eesz.2M256.build.spiffs_end=0x1FB000 gen4iod.menu.eesz.2M256.build.spiffs_blocksize=4096 gen4iod.menu.eesz.2M512=2MB (FS:512KB OTA:~768KB) gen4iod.menu.eesz.2M512.build.flash_size=2M -gen4iod.menu.eesz.2M512.build.flash_size_bytes=0x200000 gen4iod.menu.eesz.2M512.build.flash_ld=eagle.flash.2m512.ld gen4iod.menu.eesz.2M512.build.spiffs_pagesize=256 -gen4iod.menu.eesz.2M512.upload.maximum_size=1044464 gen4iod.menu.eesz.2M512.build.rfcal_addr=0x1FC000 gen4iod.menu.eesz.2M512.build.spiffs_start=0x180000 gen4iod.menu.eesz.2M512.build.spiffs_end=0x1FA000 gen4iod.menu.eesz.2M512.build.spiffs_blocksize=8192 gen4iod.menu.eesz.2M1M=2MB (FS:1MB OTA:~512KB) gen4iod.menu.eesz.2M1M.build.flash_size=2M -gen4iod.menu.eesz.2M1M.build.flash_size_bytes=0x200000 gen4iod.menu.eesz.2M1M.build.flash_ld=eagle.flash.2m1m.ld gen4iod.menu.eesz.2M1M.build.spiffs_pagesize=256 -gen4iod.menu.eesz.2M1M.upload.maximum_size=1044464 gen4iod.menu.eesz.2M1M.build.rfcal_addr=0x1FC000 gen4iod.menu.eesz.2M1M.build.spiffs_start=0x100000 gen4iod.menu.eesz.2M1M.build.spiffs_end=0x1FA000 gen4iod.menu.eesz.2M1M.build.spiffs_blocksize=8192 gen4iod.menu.eesz.2M=2MB (FS:none OTA:~1019KB) gen4iod.menu.eesz.2M.build.flash_size=2M -gen4iod.menu.eesz.2M.build.flash_size_bytes=0x200000 gen4iod.menu.eesz.2M.build.flash_ld=eagle.flash.2m.ld gen4iod.menu.eesz.2M.build.spiffs_pagesize=256 -gen4iod.menu.eesz.2M.upload.maximum_size=1044464 gen4iod.menu.eesz.2M.build.rfcal_addr=0x1FC000 gen4iod.menu.eesz.512K32=512KB (FS:32KB OTA:~230KB) gen4iod.menu.eesz.512K32.build.flash_size=512K -gen4iod.menu.eesz.512K32.build.flash_size_bytes=0x80000 gen4iod.menu.eesz.512K32.build.flash_ld=eagle.flash.512k32.ld gen4iod.menu.eesz.512K32.build.spiffs_pagesize=256 -gen4iod.menu.eesz.512K32.upload.maximum_size=466928 gen4iod.menu.eesz.512K32.build.rfcal_addr=0x7C000 gen4iod.menu.eesz.512K32.build.spiffs_start=0x73000 gen4iod.menu.eesz.512K32.build.spiffs_end=0x7B000 gen4iod.menu.eesz.512K32.build.spiffs_blocksize=4096 gen4iod.menu.eesz.512K64=512KB (FS:64KB OTA:~214KB) gen4iod.menu.eesz.512K64.build.flash_size=512K -gen4iod.menu.eesz.512K64.build.flash_size_bytes=0x80000 gen4iod.menu.eesz.512K64.build.flash_ld=eagle.flash.512k64.ld gen4iod.menu.eesz.512K64.build.spiffs_pagesize=256 -gen4iod.menu.eesz.512K64.upload.maximum_size=434160 gen4iod.menu.eesz.512K64.build.rfcal_addr=0x7C000 gen4iod.menu.eesz.512K64.build.spiffs_start=0x6B000 gen4iod.menu.eesz.512K64.build.spiffs_end=0x7B000 gen4iod.menu.eesz.512K64.build.spiffs_blocksize=4096 gen4iod.menu.eesz.512K128=512KB (FS:128KB OTA:~182KB) gen4iod.menu.eesz.512K128.build.flash_size=512K -gen4iod.menu.eesz.512K128.build.flash_size_bytes=0x80000 gen4iod.menu.eesz.512K128.build.flash_ld=eagle.flash.512k128.ld gen4iod.menu.eesz.512K128.build.spiffs_pagesize=256 -gen4iod.menu.eesz.512K128.upload.maximum_size=368624 gen4iod.menu.eesz.512K128.build.rfcal_addr=0x7C000 gen4iod.menu.eesz.512K128.build.spiffs_start=0x5B000 gen4iod.menu.eesz.512K128.build.spiffs_end=0x7B000 gen4iod.menu.eesz.512K128.build.spiffs_blocksize=4096 gen4iod.menu.eesz.512K=512KB (FS:none OTA:~246KB) gen4iod.menu.eesz.512K.build.flash_size=512K -gen4iod.menu.eesz.512K.build.flash_size_bytes=0x80000 gen4iod.menu.eesz.512K.build.flash_ld=eagle.flash.512k.ld gen4iod.menu.eesz.512K.build.spiffs_pagesize=256 -gen4iod.menu.eesz.512K.upload.maximum_size=499696 gen4iod.menu.eesz.512K.build.rfcal_addr=0x7C000 gen4iod.menu.ip.lm2f=v2 Lower Memory gen4iod.menu.ip.lm2f.build.lwip_include=lwip2/include @@ -1062,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 @@ -1106,10 +1085,22 @@ gen4iod.menu.lvl.OOM=OOM gen4iod.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM gen4iod.menu.lvl.MDNS=MDNS gen4iod.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +gen4iod.menu.lvl.HWDT=HWDT +gen4iod.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +gen4iod.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +gen4iod.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K gen4iod.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS gen4iod.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 +gen4iod.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +gen4iod.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 +gen4iod.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +gen4iod.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 gen4iod.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS gen4iod.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 +gen4iod.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +gen4iod.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 +gen4iod.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 +gen4iod.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 gen4iod.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG gen4iod.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG gen4iod.menu.wipe.none=Only Sketch @@ -1136,10 +1127,19 @@ gen4iod.menu.baud.921600=921600 gen4iod.menu.baud.921600.upload.speed=921600 gen4iod.menu.baud.3000000=3000000 gen4iod.menu.baud.3000000.upload.speed=3000000 +gen4iod.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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 -huzzah.build.board=ESP8266_ESP12 +huzzah.build.board=ESP8266_ADAFRUIT_HUZZAH huzzah.build.variant=adafruit huzzah.upload.tool=esptool huzzah.upload.maximum_data_size=81920 @@ -1150,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 @@ -1176,10 +1177,6 @@ huzzah.menu.ssl.all=All SSL ciphers (most compatible) huzzah.menu.ssl.all.build.sslflags= huzzah.menu.ssl.basic=Basic SSL ciphers (lower ROM use) huzzah.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -huzzah.menu.waveform.pwm=Locked PWM -huzzah.menu.waveform.pwm.build.waveform= -huzzah.menu.waveform.phase=Locked Phase -huzzah.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE huzzah.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) huzzah.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 huzzah.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -1188,6 +1185,10 @@ huzzah.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) huzzah.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP huzzah.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) huzzah.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +huzzah.menu.mmu.ext128k=128K Heap External 23LC1024 +huzzah.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +huzzah.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +huzzah.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 huzzah.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM huzzah.menu.non32xfer.fast.build.non32xferflags= huzzah.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -1198,40 +1199,32 @@ huzzah.build.flash_flags=-DFLASHMODE_QIO huzzah.build.flash_freq=40 huzzah.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) huzzah.menu.eesz.4M2M.build.flash_size=4M -huzzah.menu.eesz.4M2M.build.flash_size_bytes=0x400000 huzzah.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld huzzah.menu.eesz.4M2M.build.spiffs_pagesize=256 -huzzah.menu.eesz.4M2M.upload.maximum_size=1044464 huzzah.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 huzzah.menu.eesz.4M2M.build.spiffs_start=0x200000 huzzah.menu.eesz.4M2M.build.spiffs_end=0x3FA000 huzzah.menu.eesz.4M2M.build.spiffs_blocksize=8192 huzzah.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) huzzah.menu.eesz.4M3M.build.flash_size=4M -huzzah.menu.eesz.4M3M.build.flash_size_bytes=0x400000 huzzah.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld huzzah.menu.eesz.4M3M.build.spiffs_pagesize=256 -huzzah.menu.eesz.4M3M.upload.maximum_size=1044464 huzzah.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 huzzah.menu.eesz.4M3M.build.spiffs_start=0x100000 huzzah.menu.eesz.4M3M.build.spiffs_end=0x3FA000 huzzah.menu.eesz.4M3M.build.spiffs_blocksize=8192 huzzah.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) huzzah.menu.eesz.4M1M.build.flash_size=4M -huzzah.menu.eesz.4M1M.build.flash_size_bytes=0x400000 huzzah.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld huzzah.menu.eesz.4M1M.build.spiffs_pagesize=256 -huzzah.menu.eesz.4M1M.upload.maximum_size=1044464 huzzah.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 huzzah.menu.eesz.4M1M.build.spiffs_start=0x300000 huzzah.menu.eesz.4M1M.build.spiffs_end=0x3FA000 huzzah.menu.eesz.4M1M.build.spiffs_blocksize=8192 huzzah.menu.eesz.4M=4MB (FS:none OTA:~1019KB) huzzah.menu.eesz.4M.build.flash_size=4M -huzzah.menu.eesz.4M.build.flash_size_bytes=0x400000 huzzah.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld huzzah.menu.eesz.4M.build.spiffs_pagesize=256 -huzzah.menu.eesz.4M.upload.maximum_size=1044464 huzzah.menu.eesz.4M.build.rfcal_addr=0x3FC000 huzzah.menu.ip.lm2f=v2 Lower Memory huzzah.menu.ip.lm2f.build.lwip_include=lwip2/include @@ -1265,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 @@ -1309,10 +1308,22 @@ huzzah.menu.lvl.OOM=OOM huzzah.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM huzzah.menu.lvl.MDNS=MDNS huzzah.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +huzzah.menu.lvl.HWDT=HWDT +huzzah.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +huzzah.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +huzzah.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K huzzah.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS huzzah.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 +huzzah.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +huzzah.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 +huzzah.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +huzzah.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 huzzah.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS huzzah.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 +huzzah.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +huzzah.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 +huzzah.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 +huzzah.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 huzzah.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG huzzah.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG huzzah.menu.wipe.none=Only Sketch @@ -1339,6 +1350,15 @@ huzzah.menu.baud.921600=921600 huzzah.menu.baud.921600.upload.speed=921600 huzzah.menu.baud.3000000=3000000 huzzah.menu.baud.3000000.upload.speed=3000000 +huzzah.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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 @@ -1353,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 @@ -1379,10 +1400,6 @@ wifi_slot.menu.ssl.all=All SSL ciphers (most compatible) wifi_slot.menu.ssl.all.build.sslflags= wifi_slot.menu.ssl.basic=Basic SSL ciphers (lower ROM use) wifi_slot.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -wifi_slot.menu.waveform.pwm=Locked PWM -wifi_slot.menu.waveform.pwm.build.waveform= -wifi_slot.menu.waveform.phase=Locked Phase -wifi_slot.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE wifi_slot.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) wifi_slot.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 wifi_slot.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -1391,6 +1408,10 @@ wifi_slot.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) wifi_slot.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP wifi_slot.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) wifi_slot.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +wifi_slot.menu.mmu.ext128k=128K Heap External 23LC1024 +wifi_slot.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +wifi_slot.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +wifi_slot.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 wifi_slot.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM wifi_slot.menu.non32xfer.fast.build.non32xferflags= wifi_slot.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -1418,137 +1439,109 @@ wifi_slot.menu.FlashMode.qio.build.flash_mode=qio wifi_slot.menu.FlashMode.qio.build.flash_flags=-DFLASHMODE_QIO wifi_slot.menu.eesz.1M64=1MB (FS:64KB OTA:~470KB) wifi_slot.menu.eesz.1M64.build.flash_size=1M -wifi_slot.menu.eesz.1M64.build.flash_size_bytes=0x100000 wifi_slot.menu.eesz.1M64.build.flash_ld=eagle.flash.1m64.ld wifi_slot.menu.eesz.1M64.build.spiffs_pagesize=256 -wifi_slot.menu.eesz.1M64.upload.maximum_size=958448 wifi_slot.menu.eesz.1M64.build.rfcal_addr=0xFC000 wifi_slot.menu.eesz.1M64.build.spiffs_start=0xEB000 wifi_slot.menu.eesz.1M64.build.spiffs_end=0xFB000 wifi_slot.menu.eesz.1M64.build.spiffs_blocksize=4096 wifi_slot.menu.eesz.1M128=1MB (FS:128KB OTA:~438KB) wifi_slot.menu.eesz.1M128.build.flash_size=1M -wifi_slot.menu.eesz.1M128.build.flash_size_bytes=0x100000 wifi_slot.menu.eesz.1M128.build.flash_ld=eagle.flash.1m128.ld wifi_slot.menu.eesz.1M128.build.spiffs_pagesize=256 -wifi_slot.menu.eesz.1M128.upload.maximum_size=892912 wifi_slot.menu.eesz.1M128.build.rfcal_addr=0xFC000 wifi_slot.menu.eesz.1M128.build.spiffs_start=0xDB000 wifi_slot.menu.eesz.1M128.build.spiffs_end=0xFB000 wifi_slot.menu.eesz.1M128.build.spiffs_blocksize=4096 wifi_slot.menu.eesz.1M144=1MB (FS:144KB OTA:~430KB) wifi_slot.menu.eesz.1M144.build.flash_size=1M -wifi_slot.menu.eesz.1M144.build.flash_size_bytes=0x100000 wifi_slot.menu.eesz.1M144.build.flash_ld=eagle.flash.1m144.ld wifi_slot.menu.eesz.1M144.build.spiffs_pagesize=256 -wifi_slot.menu.eesz.1M144.upload.maximum_size=876528 wifi_slot.menu.eesz.1M144.build.rfcal_addr=0xFC000 wifi_slot.menu.eesz.1M144.build.spiffs_start=0xD7000 wifi_slot.menu.eesz.1M144.build.spiffs_end=0xFB000 wifi_slot.menu.eesz.1M144.build.spiffs_blocksize=4096 wifi_slot.menu.eesz.1M160=1MB (FS:160KB OTA:~422KB) wifi_slot.menu.eesz.1M160.build.flash_size=1M -wifi_slot.menu.eesz.1M160.build.flash_size_bytes=0x100000 wifi_slot.menu.eesz.1M160.build.flash_ld=eagle.flash.1m160.ld wifi_slot.menu.eesz.1M160.build.spiffs_pagesize=256 -wifi_slot.menu.eesz.1M160.upload.maximum_size=860144 wifi_slot.menu.eesz.1M160.build.rfcal_addr=0xFC000 wifi_slot.menu.eesz.1M160.build.spiffs_start=0xD3000 wifi_slot.menu.eesz.1M160.build.spiffs_end=0xFB000 wifi_slot.menu.eesz.1M160.build.spiffs_blocksize=4096 wifi_slot.menu.eesz.1M192=1MB (FS:192KB OTA:~406KB) wifi_slot.menu.eesz.1M192.build.flash_size=1M -wifi_slot.menu.eesz.1M192.build.flash_size_bytes=0x100000 wifi_slot.menu.eesz.1M192.build.flash_ld=eagle.flash.1m192.ld wifi_slot.menu.eesz.1M192.build.spiffs_pagesize=256 -wifi_slot.menu.eesz.1M192.upload.maximum_size=827376 wifi_slot.menu.eesz.1M192.build.rfcal_addr=0xFC000 wifi_slot.menu.eesz.1M192.build.spiffs_start=0xCB000 wifi_slot.menu.eesz.1M192.build.spiffs_end=0xFB000 wifi_slot.menu.eesz.1M192.build.spiffs_blocksize=4096 wifi_slot.menu.eesz.1M256=1MB (FS:256KB OTA:~374KB) wifi_slot.menu.eesz.1M256.build.flash_size=1M -wifi_slot.menu.eesz.1M256.build.flash_size_bytes=0x100000 wifi_slot.menu.eesz.1M256.build.flash_ld=eagle.flash.1m256.ld wifi_slot.menu.eesz.1M256.build.spiffs_pagesize=256 -wifi_slot.menu.eesz.1M256.upload.maximum_size=761840 wifi_slot.menu.eesz.1M256.build.rfcal_addr=0xFC000 wifi_slot.menu.eesz.1M256.build.spiffs_start=0xBB000 wifi_slot.menu.eesz.1M256.build.spiffs_end=0xFB000 wifi_slot.menu.eesz.1M256.build.spiffs_blocksize=4096 wifi_slot.menu.eesz.1M512=1MB (FS:512KB OTA:~246KB) wifi_slot.menu.eesz.1M512.build.flash_size=1M -wifi_slot.menu.eesz.1M512.build.flash_size_bytes=0x100000 wifi_slot.menu.eesz.1M512.build.flash_ld=eagle.flash.1m512.ld wifi_slot.menu.eesz.1M512.build.spiffs_pagesize=256 -wifi_slot.menu.eesz.1M512.upload.maximum_size=499696 wifi_slot.menu.eesz.1M512.build.rfcal_addr=0xFC000 wifi_slot.menu.eesz.1M512.build.spiffs_start=0x7B000 wifi_slot.menu.eesz.1M512.build.spiffs_end=0xFB000 wifi_slot.menu.eesz.1M512.build.spiffs_blocksize=8192 wifi_slot.menu.eesz.1M=1MB (FS:none OTA:~502KB) wifi_slot.menu.eesz.1M.build.flash_size=1M -wifi_slot.menu.eesz.1M.build.flash_size_bytes=0x100000 wifi_slot.menu.eesz.1M.build.flash_ld=eagle.flash.1m.ld wifi_slot.menu.eesz.1M.build.spiffs_pagesize=256 -wifi_slot.menu.eesz.1M.upload.maximum_size=1023984 wifi_slot.menu.eesz.1M.build.rfcal_addr=0xFC000 wifi_slot.menu.eesz.2M64=2MB (FS:64KB OTA:~992KB) wifi_slot.menu.eesz.2M64.build.flash_size=2M -wifi_slot.menu.eesz.2M64.build.flash_size_bytes=0x200000 wifi_slot.menu.eesz.2M64.build.flash_ld=eagle.flash.2m64.ld wifi_slot.menu.eesz.2M64.build.spiffs_pagesize=256 -wifi_slot.menu.eesz.2M64.upload.maximum_size=1044464 wifi_slot.menu.eesz.2M64.build.rfcal_addr=0x1FC000 wifi_slot.menu.eesz.2M64.build.spiffs_start=0x1F0000 wifi_slot.menu.eesz.2M64.build.spiffs_end=0x1FB000 wifi_slot.menu.eesz.2M64.build.spiffs_blocksize=4096 wifi_slot.menu.eesz.2M128=2MB (FS:128KB OTA:~960KB) wifi_slot.menu.eesz.2M128.build.flash_size=2M -wifi_slot.menu.eesz.2M128.build.flash_size_bytes=0x200000 wifi_slot.menu.eesz.2M128.build.flash_ld=eagle.flash.2m128.ld wifi_slot.menu.eesz.2M128.build.spiffs_pagesize=256 -wifi_slot.menu.eesz.2M128.upload.maximum_size=1044464 wifi_slot.menu.eesz.2M128.build.rfcal_addr=0x1FC000 wifi_slot.menu.eesz.2M128.build.spiffs_start=0x1E0000 wifi_slot.menu.eesz.2M128.build.spiffs_end=0x1FB000 wifi_slot.menu.eesz.2M128.build.spiffs_blocksize=4096 wifi_slot.menu.eesz.2M256=2MB (FS:256KB OTA:~896KB) wifi_slot.menu.eesz.2M256.build.flash_size=2M -wifi_slot.menu.eesz.2M256.build.flash_size_bytes=0x200000 wifi_slot.menu.eesz.2M256.build.flash_ld=eagle.flash.2m256.ld wifi_slot.menu.eesz.2M256.build.spiffs_pagesize=256 -wifi_slot.menu.eesz.2M256.upload.maximum_size=1044464 wifi_slot.menu.eesz.2M256.build.rfcal_addr=0x1FC000 wifi_slot.menu.eesz.2M256.build.spiffs_start=0x1C0000 wifi_slot.menu.eesz.2M256.build.spiffs_end=0x1FB000 wifi_slot.menu.eesz.2M256.build.spiffs_blocksize=4096 wifi_slot.menu.eesz.2M512=2MB (FS:512KB OTA:~768KB) wifi_slot.menu.eesz.2M512.build.flash_size=2M -wifi_slot.menu.eesz.2M512.build.flash_size_bytes=0x200000 wifi_slot.menu.eesz.2M512.build.flash_ld=eagle.flash.2m512.ld wifi_slot.menu.eesz.2M512.build.spiffs_pagesize=256 -wifi_slot.menu.eesz.2M512.upload.maximum_size=1044464 wifi_slot.menu.eesz.2M512.build.rfcal_addr=0x1FC000 wifi_slot.menu.eesz.2M512.build.spiffs_start=0x180000 wifi_slot.menu.eesz.2M512.build.spiffs_end=0x1FA000 wifi_slot.menu.eesz.2M512.build.spiffs_blocksize=8192 wifi_slot.menu.eesz.2M1M=2MB (FS:1MB OTA:~512KB) wifi_slot.menu.eesz.2M1M.build.flash_size=2M -wifi_slot.menu.eesz.2M1M.build.flash_size_bytes=0x200000 wifi_slot.menu.eesz.2M1M.build.flash_ld=eagle.flash.2m1m.ld wifi_slot.menu.eesz.2M1M.build.spiffs_pagesize=256 -wifi_slot.menu.eesz.2M1M.upload.maximum_size=1044464 wifi_slot.menu.eesz.2M1M.build.rfcal_addr=0x1FC000 wifi_slot.menu.eesz.2M1M.build.spiffs_start=0x100000 wifi_slot.menu.eesz.2M1M.build.spiffs_end=0x1FA000 wifi_slot.menu.eesz.2M1M.build.spiffs_blocksize=8192 wifi_slot.menu.eesz.2M=2MB (FS:none OTA:~1019KB) wifi_slot.menu.eesz.2M.build.flash_size=2M -wifi_slot.menu.eesz.2M.build.flash_size_bytes=0x200000 wifi_slot.menu.eesz.2M.build.flash_ld=eagle.flash.2m.ld wifi_slot.menu.eesz.2M.build.spiffs_pagesize=256 -wifi_slot.menu.eesz.2M.upload.maximum_size=1044464 wifi_slot.menu.eesz.2M.build.rfcal_addr=0x1FC000 wifi_slot.menu.ip.lm2f=v2 Lower Memory wifi_slot.menu.ip.lm2f.build.lwip_include=lwip2/include @@ -1582,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 @@ -1626,10 +1625,22 @@ wifi_slot.menu.lvl.OOM=OOM wifi_slot.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM wifi_slot.menu.lvl.MDNS=MDNS wifi_slot.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +wifi_slot.menu.lvl.HWDT=HWDT +wifi_slot.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +wifi_slot.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +wifi_slot.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K wifi_slot.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS wifi_slot.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 +wifi_slot.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +wifi_slot.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 +wifi_slot.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +wifi_slot.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 wifi_slot.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS wifi_slot.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 +wifi_slot.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +wifi_slot.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 +wifi_slot.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 +wifi_slot.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 wifi_slot.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG wifi_slot.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG wifi_slot.menu.wipe.none=Only Sketch @@ -1656,21 +1667,30 @@ wifi_slot.menu.baud.921600=921600 wifi_slot.menu.baud.921600.upload.speed=921600 wifi_slot.menu.baud.3000000=3000000 wifi_slot.menu.baud.3000000.upload.speed=3000000 +wifi_slot.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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 arduino-esp8266.build.board=ESP8266_ARDUINO arduino-esp8266.menu.BoardModel.primo=Primo arduino-esp8266.menu.BoardModel.primo.build.board=ESP8266_ARDUINO_PRIMO -arduino-esp8266.menu.BoardModel.primo.build.extra_flags=-DF_CRYSTAL=40000000 -DESP8266 +arduino-esp8266.menu.BoardModel.primo.build.extra_flags=-DF_CRYSTAL=40000000 arduino-esp8266.menu.BoardModel.primo.build.variant=arduino_spi arduino-esp8266.menu.BoardModel.starottodeved=Star OTTO arduino-esp8266.menu.BoardModel.starottodeved.build.board=ESP8266_ARDUINO_STAR_OTTO -arduino-esp8266.menu.BoardModel.starottodeved.build.extra_flags=-DF_CRYSTAL=40000000 -DESP8266 +arduino-esp8266.menu.BoardModel.starottodeved.build.extra_flags=-DF_CRYSTAL=40000000 arduino-esp8266.menu.BoardModel.starottodeved.build.variant=arduino_uart arduino-esp8266.menu.BoardModel.unowifideved=Uno WiFi arduino-esp8266.menu.BoardModel.unowifideved.build.board=ESP8266_ARDUINO_UNOWIFI -arduino-esp8266.menu.BoardModel.unowifideved.build.extra_flags=-DF_CRYSTAL=40000000 -DESP8266 +arduino-esp8266.menu.BoardModel.unowifideved.build.extra_flags=-DF_CRYSTAL=40000000 arduino-esp8266.menu.BoardModel.unowifideved.build.variant=arduino_uart arduino-esp8266.upload.tool=esptool arduino-esp8266.upload.maximum_data_size=81920 @@ -1682,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 @@ -1708,10 +1729,6 @@ arduino-esp8266.menu.ssl.all=All SSL ciphers (most compatible) arduino-esp8266.menu.ssl.all.build.sslflags= arduino-esp8266.menu.ssl.basic=Basic SSL ciphers (lower ROM use) arduino-esp8266.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -arduino-esp8266.menu.waveform.pwm=Locked PWM -arduino-esp8266.menu.waveform.pwm.build.waveform= -arduino-esp8266.menu.waveform.phase=Locked Phase -arduino-esp8266.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE arduino-esp8266.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) arduino-esp8266.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 arduino-esp8266.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -1720,6 +1737,10 @@ arduino-esp8266.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) arduino-esp8266.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP arduino-esp8266.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) arduino-esp8266.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +arduino-esp8266.menu.mmu.ext128k=128K Heap External 23LC1024 +arduino-esp8266.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +arduino-esp8266.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +arduino-esp8266.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 arduino-esp8266.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM arduino-esp8266.menu.non32xfer.fast.build.non32xferflags= arduino-esp8266.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -1730,40 +1751,32 @@ arduino-esp8266.build.flash_flags=-DFLASHMODE_QIO arduino-esp8266.build.flash_freq=40 arduino-esp8266.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) arduino-esp8266.menu.eesz.4M2M.build.flash_size=4M -arduino-esp8266.menu.eesz.4M2M.build.flash_size_bytes=0x400000 arduino-esp8266.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld arduino-esp8266.menu.eesz.4M2M.build.spiffs_pagesize=256 -arduino-esp8266.menu.eesz.4M2M.upload.maximum_size=1044464 arduino-esp8266.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 arduino-esp8266.menu.eesz.4M2M.build.spiffs_start=0x200000 arduino-esp8266.menu.eesz.4M2M.build.spiffs_end=0x3FA000 arduino-esp8266.menu.eesz.4M2M.build.spiffs_blocksize=8192 arduino-esp8266.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) arduino-esp8266.menu.eesz.4M3M.build.flash_size=4M -arduino-esp8266.menu.eesz.4M3M.build.flash_size_bytes=0x400000 arduino-esp8266.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld arduino-esp8266.menu.eesz.4M3M.build.spiffs_pagesize=256 -arduino-esp8266.menu.eesz.4M3M.upload.maximum_size=1044464 arduino-esp8266.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 arduino-esp8266.menu.eesz.4M3M.build.spiffs_start=0x100000 arduino-esp8266.menu.eesz.4M3M.build.spiffs_end=0x3FA000 arduino-esp8266.menu.eesz.4M3M.build.spiffs_blocksize=8192 arduino-esp8266.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) arduino-esp8266.menu.eesz.4M1M.build.flash_size=4M -arduino-esp8266.menu.eesz.4M1M.build.flash_size_bytes=0x400000 arduino-esp8266.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld arduino-esp8266.menu.eesz.4M1M.build.spiffs_pagesize=256 -arduino-esp8266.menu.eesz.4M1M.upload.maximum_size=1044464 arduino-esp8266.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 arduino-esp8266.menu.eesz.4M1M.build.spiffs_start=0x300000 arduino-esp8266.menu.eesz.4M1M.build.spiffs_end=0x3FA000 arduino-esp8266.menu.eesz.4M1M.build.spiffs_blocksize=8192 arduino-esp8266.menu.eesz.4M=4MB (FS:none OTA:~1019KB) arduino-esp8266.menu.eesz.4M.build.flash_size=4M -arduino-esp8266.menu.eesz.4M.build.flash_size_bytes=0x400000 arduino-esp8266.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld arduino-esp8266.menu.eesz.4M.build.spiffs_pagesize=256 -arduino-esp8266.menu.eesz.4M.upload.maximum_size=1044464 arduino-esp8266.menu.eesz.4M.build.rfcal_addr=0x3FC000 arduino-esp8266.menu.ip.lm2f=v2 Lower Memory arduino-esp8266.menu.ip.lm2f.build.lwip_include=lwip2/include @@ -1797,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 @@ -1841,10 +1860,22 @@ arduino-esp8266.menu.lvl.OOM=OOM arduino-esp8266.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM arduino-esp8266.menu.lvl.MDNS=MDNS arduino-esp8266.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +arduino-esp8266.menu.lvl.HWDT=HWDT +arduino-esp8266.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +arduino-esp8266.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +arduino-esp8266.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K arduino-esp8266.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS arduino-esp8266.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 +arduino-esp8266.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +arduino-esp8266.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 +arduino-esp8266.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +arduino-esp8266.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 arduino-esp8266.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS arduino-esp8266.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 +arduino-esp8266.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +arduino-esp8266.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 +arduino-esp8266.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 +arduino-esp8266.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 arduino-esp8266.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG arduino-esp8266.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG arduino-esp8266.menu.wipe.none=Only Sketch @@ -1871,6 +1902,15 @@ arduino-esp8266.menu.baud.921600=921600 arduino-esp8266.menu.baud.921600.upload.speed=921600 arduino-esp8266.menu.baud.3000000=3000000 arduino-esp8266.menu.baud.3000000.upload.speed=3000000 +arduino-esp8266.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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) @@ -1886,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 @@ -1912,10 +1953,6 @@ espmxdevkit.menu.ssl.all=All SSL ciphers (most compatible) espmxdevkit.menu.ssl.all.build.sslflags= espmxdevkit.menu.ssl.basic=Basic SSL ciphers (lower ROM use) espmxdevkit.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -espmxdevkit.menu.waveform.pwm=Locked PWM -espmxdevkit.menu.waveform.pwm.build.waveform= -espmxdevkit.menu.waveform.phase=Locked Phase -espmxdevkit.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE espmxdevkit.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) espmxdevkit.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 espmxdevkit.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -1924,6 +1961,10 @@ espmxdevkit.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) espmxdevkit.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP espmxdevkit.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) espmxdevkit.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +espmxdevkit.menu.mmu.ext128k=128K Heap External 23LC1024 +espmxdevkit.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +espmxdevkit.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +espmxdevkit.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 espmxdevkit.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM espmxdevkit.menu.non32xfer.fast.build.non32xferflags= espmxdevkit.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -1934,80 +1975,64 @@ espmxdevkit.build.flash_flags=-DFLASHMODE_DOUT espmxdevkit.build.flash_freq=40 espmxdevkit.menu.eesz.1M64=1MB (FS:64KB OTA:~470KB) espmxdevkit.menu.eesz.1M64.build.flash_size=1M -espmxdevkit.menu.eesz.1M64.build.flash_size_bytes=0x100000 espmxdevkit.menu.eesz.1M64.build.flash_ld=eagle.flash.1m64.ld espmxdevkit.menu.eesz.1M64.build.spiffs_pagesize=256 -espmxdevkit.menu.eesz.1M64.upload.maximum_size=958448 espmxdevkit.menu.eesz.1M64.build.rfcal_addr=0xFC000 espmxdevkit.menu.eesz.1M64.build.spiffs_start=0xEB000 espmxdevkit.menu.eesz.1M64.build.spiffs_end=0xFB000 espmxdevkit.menu.eesz.1M64.build.spiffs_blocksize=4096 espmxdevkit.menu.eesz.1M128=1MB (FS:128KB OTA:~438KB) espmxdevkit.menu.eesz.1M128.build.flash_size=1M -espmxdevkit.menu.eesz.1M128.build.flash_size_bytes=0x100000 espmxdevkit.menu.eesz.1M128.build.flash_ld=eagle.flash.1m128.ld espmxdevkit.menu.eesz.1M128.build.spiffs_pagesize=256 -espmxdevkit.menu.eesz.1M128.upload.maximum_size=892912 espmxdevkit.menu.eesz.1M128.build.rfcal_addr=0xFC000 espmxdevkit.menu.eesz.1M128.build.spiffs_start=0xDB000 espmxdevkit.menu.eesz.1M128.build.spiffs_end=0xFB000 espmxdevkit.menu.eesz.1M128.build.spiffs_blocksize=4096 espmxdevkit.menu.eesz.1M144=1MB (FS:144KB OTA:~430KB) espmxdevkit.menu.eesz.1M144.build.flash_size=1M -espmxdevkit.menu.eesz.1M144.build.flash_size_bytes=0x100000 espmxdevkit.menu.eesz.1M144.build.flash_ld=eagle.flash.1m144.ld espmxdevkit.menu.eesz.1M144.build.spiffs_pagesize=256 -espmxdevkit.menu.eesz.1M144.upload.maximum_size=876528 espmxdevkit.menu.eesz.1M144.build.rfcal_addr=0xFC000 espmxdevkit.menu.eesz.1M144.build.spiffs_start=0xD7000 espmxdevkit.menu.eesz.1M144.build.spiffs_end=0xFB000 espmxdevkit.menu.eesz.1M144.build.spiffs_blocksize=4096 espmxdevkit.menu.eesz.1M160=1MB (FS:160KB OTA:~422KB) espmxdevkit.menu.eesz.1M160.build.flash_size=1M -espmxdevkit.menu.eesz.1M160.build.flash_size_bytes=0x100000 espmxdevkit.menu.eesz.1M160.build.flash_ld=eagle.flash.1m160.ld espmxdevkit.menu.eesz.1M160.build.spiffs_pagesize=256 -espmxdevkit.menu.eesz.1M160.upload.maximum_size=860144 espmxdevkit.menu.eesz.1M160.build.rfcal_addr=0xFC000 espmxdevkit.menu.eesz.1M160.build.spiffs_start=0xD3000 espmxdevkit.menu.eesz.1M160.build.spiffs_end=0xFB000 espmxdevkit.menu.eesz.1M160.build.spiffs_blocksize=4096 espmxdevkit.menu.eesz.1M192=1MB (FS:192KB OTA:~406KB) espmxdevkit.menu.eesz.1M192.build.flash_size=1M -espmxdevkit.menu.eesz.1M192.build.flash_size_bytes=0x100000 espmxdevkit.menu.eesz.1M192.build.flash_ld=eagle.flash.1m192.ld espmxdevkit.menu.eesz.1M192.build.spiffs_pagesize=256 -espmxdevkit.menu.eesz.1M192.upload.maximum_size=827376 espmxdevkit.menu.eesz.1M192.build.rfcal_addr=0xFC000 espmxdevkit.menu.eesz.1M192.build.spiffs_start=0xCB000 espmxdevkit.menu.eesz.1M192.build.spiffs_end=0xFB000 espmxdevkit.menu.eesz.1M192.build.spiffs_blocksize=4096 espmxdevkit.menu.eesz.1M256=1MB (FS:256KB OTA:~374KB) espmxdevkit.menu.eesz.1M256.build.flash_size=1M -espmxdevkit.menu.eesz.1M256.build.flash_size_bytes=0x100000 espmxdevkit.menu.eesz.1M256.build.flash_ld=eagle.flash.1m256.ld espmxdevkit.menu.eesz.1M256.build.spiffs_pagesize=256 -espmxdevkit.menu.eesz.1M256.upload.maximum_size=761840 espmxdevkit.menu.eesz.1M256.build.rfcal_addr=0xFC000 espmxdevkit.menu.eesz.1M256.build.spiffs_start=0xBB000 espmxdevkit.menu.eesz.1M256.build.spiffs_end=0xFB000 espmxdevkit.menu.eesz.1M256.build.spiffs_blocksize=4096 espmxdevkit.menu.eesz.1M512=1MB (FS:512KB OTA:~246KB) espmxdevkit.menu.eesz.1M512.build.flash_size=1M -espmxdevkit.menu.eesz.1M512.build.flash_size_bytes=0x100000 espmxdevkit.menu.eesz.1M512.build.flash_ld=eagle.flash.1m512.ld espmxdevkit.menu.eesz.1M512.build.spiffs_pagesize=256 -espmxdevkit.menu.eesz.1M512.upload.maximum_size=499696 espmxdevkit.menu.eesz.1M512.build.rfcal_addr=0xFC000 espmxdevkit.menu.eesz.1M512.build.spiffs_start=0x7B000 espmxdevkit.menu.eesz.1M512.build.spiffs_end=0xFB000 espmxdevkit.menu.eesz.1M512.build.spiffs_blocksize=8192 espmxdevkit.menu.eesz.1M=1MB (FS:none OTA:~502KB) espmxdevkit.menu.eesz.1M.build.flash_size=1M -espmxdevkit.menu.eesz.1M.build.flash_size_bytes=0x100000 espmxdevkit.menu.eesz.1M.build.flash_ld=eagle.flash.1m.ld espmxdevkit.menu.eesz.1M.build.spiffs_pagesize=256 -espmxdevkit.menu.eesz.1M.upload.maximum_size=1023984 espmxdevkit.menu.eesz.1M.build.rfcal_addr=0xFC000 espmxdevkit.menu.ip.lm2f=v2 Lower Memory espmxdevkit.menu.ip.lm2f.build.lwip_include=lwip2/include @@ -2041,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 @@ -2085,10 +2116,22 @@ espmxdevkit.menu.lvl.OOM=OOM espmxdevkit.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM espmxdevkit.menu.lvl.MDNS=MDNS espmxdevkit.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +espmxdevkit.menu.lvl.HWDT=HWDT +espmxdevkit.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +espmxdevkit.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +espmxdevkit.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K espmxdevkit.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS espmxdevkit.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 +espmxdevkit.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +espmxdevkit.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 +espmxdevkit.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +espmxdevkit.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 espmxdevkit.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS espmxdevkit.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 +espmxdevkit.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +espmxdevkit.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 +espmxdevkit.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 +espmxdevkit.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 espmxdevkit.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG espmxdevkit.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG espmxdevkit.menu.wipe.none=Only Sketch @@ -2115,6 +2158,15 @@ espmxdevkit.menu.baud.921600=921600 espmxdevkit.menu.baud.921600.upload.speed=921600 espmxdevkit.menu.baud.3000000=3000000 espmxdevkit.menu.baud.3000000.upload.speed=3000000 +espmxdevkit.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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 @@ -2130,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 @@ -2156,10 +2209,6 @@ oak.menu.ssl.all=All SSL ciphers (most compatible) oak.menu.ssl.all.build.sslflags= oak.menu.ssl.basic=Basic SSL ciphers (lower ROM use) oak.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -oak.menu.waveform.pwm=Locked PWM -oak.menu.waveform.pwm.build.waveform= -oak.menu.waveform.phase=Locked Phase -oak.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE oak.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) oak.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 oak.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -2168,6 +2217,10 @@ oak.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) oak.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP oak.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) oak.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +oak.menu.mmu.ext128k=128K Heap External 23LC1024 +oak.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +oak.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +oak.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 oak.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM oak.menu.non32xfer.fast.build.non32xferflags= oak.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -2178,40 +2231,32 @@ oak.build.flash_flags=-DFLASHMODE_DIO oak.build.flash_freq=40 oak.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) oak.menu.eesz.4M2M.build.flash_size=4M -oak.menu.eesz.4M2M.build.flash_size_bytes=0x400000 oak.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld oak.menu.eesz.4M2M.build.spiffs_pagesize=256 -oak.menu.eesz.4M2M.upload.maximum_size=1044464 oak.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 oak.menu.eesz.4M2M.build.spiffs_start=0x200000 oak.menu.eesz.4M2M.build.spiffs_end=0x3FA000 oak.menu.eesz.4M2M.build.spiffs_blocksize=8192 oak.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) oak.menu.eesz.4M3M.build.flash_size=4M -oak.menu.eesz.4M3M.build.flash_size_bytes=0x400000 oak.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld oak.menu.eesz.4M3M.build.spiffs_pagesize=256 -oak.menu.eesz.4M3M.upload.maximum_size=1044464 oak.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 oak.menu.eesz.4M3M.build.spiffs_start=0x100000 oak.menu.eesz.4M3M.build.spiffs_end=0x3FA000 oak.menu.eesz.4M3M.build.spiffs_blocksize=8192 oak.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) oak.menu.eesz.4M1M.build.flash_size=4M -oak.menu.eesz.4M1M.build.flash_size_bytes=0x400000 oak.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld oak.menu.eesz.4M1M.build.spiffs_pagesize=256 -oak.menu.eesz.4M1M.upload.maximum_size=1044464 oak.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 oak.menu.eesz.4M1M.build.spiffs_start=0x300000 oak.menu.eesz.4M1M.build.spiffs_end=0x3FA000 oak.menu.eesz.4M1M.build.spiffs_blocksize=8192 oak.menu.eesz.4M=4MB (FS:none OTA:~1019KB) oak.menu.eesz.4M.build.flash_size=4M -oak.menu.eesz.4M.build.flash_size_bytes=0x400000 oak.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld oak.menu.eesz.4M.build.spiffs_pagesize=256 -oak.menu.eesz.4M.upload.maximum_size=1044464 oak.menu.eesz.4M.build.rfcal_addr=0x3FC000 oak.menu.ip.lm2f=v2 Lower Memory oak.menu.ip.lm2f.build.lwip_include=lwip2/include @@ -2245,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 @@ -2289,10 +2340,22 @@ oak.menu.lvl.OOM=OOM oak.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM oak.menu.lvl.MDNS=MDNS oak.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +oak.menu.lvl.HWDT=HWDT +oak.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +oak.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +oak.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K oak.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS oak.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 +oak.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +oak.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 +oak.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +oak.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 oak.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS oak.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 +oak.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +oak.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 +oak.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 +oak.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 oak.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG oak.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG oak.menu.wipe.none=Only Sketch @@ -2319,6 +2382,15 @@ oak.menu.baud.512000.windows=512000 oak.menu.baud.512000.upload.speed=512000 oak.menu.baud.3000000=3000000 oak.menu.baud.3000000.upload.speed=3000000 +oak.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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) @@ -2342,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 @@ -2368,10 +2441,6 @@ espduino.menu.ssl.all=All SSL ciphers (most compatible) espduino.menu.ssl.all.build.sslflags= espduino.menu.ssl.basic=Basic SSL ciphers (lower ROM use) espduino.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -espduino.menu.waveform.pwm=Locked PWM -espduino.menu.waveform.pwm.build.waveform= -espduino.menu.waveform.phase=Locked Phase -espduino.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE espduino.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) espduino.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 espduino.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -2380,6 +2449,10 @@ espduino.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) espduino.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP espduino.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) espduino.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +espduino.menu.mmu.ext128k=128K Heap External 23LC1024 +espduino.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +espduino.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +espduino.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 espduino.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM espduino.menu.non32xfer.fast.build.non32xferflags= espduino.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -2389,40 +2462,32 @@ espduino.build.flash_flags=-DFLASHMODE_DIO espduino.build.flash_freq=40 espduino.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) espduino.menu.eesz.4M2M.build.flash_size=4M -espduino.menu.eesz.4M2M.build.flash_size_bytes=0x400000 espduino.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld espduino.menu.eesz.4M2M.build.spiffs_pagesize=256 -espduino.menu.eesz.4M2M.upload.maximum_size=1044464 espduino.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 espduino.menu.eesz.4M2M.build.spiffs_start=0x200000 espduino.menu.eesz.4M2M.build.spiffs_end=0x3FA000 espduino.menu.eesz.4M2M.build.spiffs_blocksize=8192 espduino.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) espduino.menu.eesz.4M3M.build.flash_size=4M -espduino.menu.eesz.4M3M.build.flash_size_bytes=0x400000 espduino.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld espduino.menu.eesz.4M3M.build.spiffs_pagesize=256 -espduino.menu.eesz.4M3M.upload.maximum_size=1044464 espduino.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 espduino.menu.eesz.4M3M.build.spiffs_start=0x100000 espduino.menu.eesz.4M3M.build.spiffs_end=0x3FA000 espduino.menu.eesz.4M3M.build.spiffs_blocksize=8192 espduino.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) espduino.menu.eesz.4M1M.build.flash_size=4M -espduino.menu.eesz.4M1M.build.flash_size_bytes=0x400000 espduino.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld espduino.menu.eesz.4M1M.build.spiffs_pagesize=256 -espduino.menu.eesz.4M1M.upload.maximum_size=1044464 espduino.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 espduino.menu.eesz.4M1M.build.spiffs_start=0x300000 espduino.menu.eesz.4M1M.build.spiffs_end=0x3FA000 espduino.menu.eesz.4M1M.build.spiffs_blocksize=8192 espduino.menu.eesz.4M=4MB (FS:none OTA:~1019KB) espduino.menu.eesz.4M.build.flash_size=4M -espduino.menu.eesz.4M.build.flash_size_bytes=0x400000 espduino.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld espduino.menu.eesz.4M.build.spiffs_pagesize=256 -espduino.menu.eesz.4M.upload.maximum_size=1044464 espduino.menu.eesz.4M.build.rfcal_addr=0x3FC000 espduino.menu.ip.lm2f=v2 Lower Memory espduino.menu.ip.lm2f.build.lwip_include=lwip2/include @@ -2456,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 @@ -2500,10 +2571,22 @@ espduino.menu.lvl.OOM=OOM espduino.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM espduino.menu.lvl.MDNS=MDNS espduino.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +espduino.menu.lvl.HWDT=HWDT +espduino.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +espduino.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +espduino.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K espduino.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS espduino.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 +espduino.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +espduino.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 +espduino.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +espduino.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 espduino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS espduino.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 +espduino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +espduino.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 +espduino.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 +espduino.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 espduino.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG espduino.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG espduino.menu.wipe.none=Only Sketch @@ -2530,6 +2613,15 @@ espduino.menu.baud.921600=921600 espduino.menu.baud.921600.upload.speed=921600 espduino.menu.baud.3000000=3000000 espduino.menu.baud.3000000.upload.speed=3000000 +espduino.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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 @@ -2544,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 @@ -2570,10 +2663,6 @@ espectro.menu.ssl.all=All SSL ciphers (most compatible) espectro.menu.ssl.all.build.sslflags= espectro.menu.ssl.basic=Basic SSL ciphers (lower ROM use) espectro.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -espectro.menu.waveform.pwm=Locked PWM -espectro.menu.waveform.pwm.build.waveform= -espectro.menu.waveform.phase=Locked Phase -espectro.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE espectro.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) espectro.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 espectro.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -2582,6 +2671,10 @@ espectro.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) espectro.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP espectro.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) espectro.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +espectro.menu.mmu.ext128k=128K Heap External 23LC1024 +espectro.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +espectro.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +espectro.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 espectro.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM espectro.menu.non32xfer.fast.build.non32xferflags= espectro.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -2592,40 +2685,32 @@ espectro.build.flash_flags=-DFLASHMODE_DIO espectro.build.flash_freq=40 espectro.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) espectro.menu.eesz.4M2M.build.flash_size=4M -espectro.menu.eesz.4M2M.build.flash_size_bytes=0x400000 espectro.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld espectro.menu.eesz.4M2M.build.spiffs_pagesize=256 -espectro.menu.eesz.4M2M.upload.maximum_size=1044464 espectro.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 espectro.menu.eesz.4M2M.build.spiffs_start=0x200000 espectro.menu.eesz.4M2M.build.spiffs_end=0x3FA000 espectro.menu.eesz.4M2M.build.spiffs_blocksize=8192 espectro.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) espectro.menu.eesz.4M3M.build.flash_size=4M -espectro.menu.eesz.4M3M.build.flash_size_bytes=0x400000 espectro.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld espectro.menu.eesz.4M3M.build.spiffs_pagesize=256 -espectro.menu.eesz.4M3M.upload.maximum_size=1044464 espectro.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 espectro.menu.eesz.4M3M.build.spiffs_start=0x100000 espectro.menu.eesz.4M3M.build.spiffs_end=0x3FA000 espectro.menu.eesz.4M3M.build.spiffs_blocksize=8192 espectro.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) espectro.menu.eesz.4M1M.build.flash_size=4M -espectro.menu.eesz.4M1M.build.flash_size_bytes=0x400000 espectro.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld espectro.menu.eesz.4M1M.build.spiffs_pagesize=256 -espectro.menu.eesz.4M1M.upload.maximum_size=1044464 espectro.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 espectro.menu.eesz.4M1M.build.spiffs_start=0x300000 espectro.menu.eesz.4M1M.build.spiffs_end=0x3FA000 espectro.menu.eesz.4M1M.build.spiffs_blocksize=8192 espectro.menu.eesz.4M=4MB (FS:none OTA:~1019KB) espectro.menu.eesz.4M.build.flash_size=4M -espectro.menu.eesz.4M.build.flash_size_bytes=0x400000 espectro.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld espectro.menu.eesz.4M.build.spiffs_pagesize=256 -espectro.menu.eesz.4M.upload.maximum_size=1044464 espectro.menu.eesz.4M.build.rfcal_addr=0x3FC000 espectro.menu.ip.lm2f=v2 Lower Memory espectro.menu.ip.lm2f.build.lwip_include=lwip2/include @@ -2659,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 @@ -2703,10 +2794,22 @@ espectro.menu.lvl.OOM=OOM espectro.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM espectro.menu.lvl.MDNS=MDNS espectro.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +espectro.menu.lvl.HWDT=HWDT +espectro.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +espectro.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +espectro.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K espectro.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS espectro.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 +espectro.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +espectro.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 +espectro.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +espectro.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 espectro.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS espectro.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 +espectro.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +espectro.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 +espectro.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 +espectro.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 espectro.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG espectro.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG espectro.menu.wipe.none=Only Sketch @@ -2733,10 +2836,19 @@ espectro.menu.baud.921600=921600 espectro.menu.baud.921600.upload.speed=921600 espectro.menu.baud.3000000=3000000 espectro.menu.baud.3000000.upload.speed=3000000 +espectro.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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) -espino.build.board=ESP8266_ESP12 +espino.build.board=ESP8266_ESPINO_ESP12 espino.build.variant=espino espino.upload.tool=esptool espino.upload.maximum_data_size=81920 @@ -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 @@ -2773,10 +2886,6 @@ espino.menu.ssl.all=All SSL ciphers (most compatible) espino.menu.ssl.all.build.sslflags= espino.menu.ssl.basic=Basic SSL ciphers (lower ROM use) espino.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -espino.menu.waveform.pwm=Locked PWM -espino.menu.waveform.pwm.build.waveform= -espino.menu.waveform.phase=Locked Phase -espino.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE espino.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) espino.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 espino.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -2785,6 +2894,10 @@ espino.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) espino.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP espino.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) espino.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +espino.menu.mmu.ext128k=128K Heap External 23LC1024 +espino.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +espino.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +espino.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 espino.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM espino.menu.non32xfer.fast.build.non32xferflags= espino.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -2798,40 +2911,32 @@ espino.build.flash_flags=-DFLASHMODE_QIO espino.build.flash_freq=40 espino.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) espino.menu.eesz.4M2M.build.flash_size=4M -espino.menu.eesz.4M2M.build.flash_size_bytes=0x400000 espino.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld espino.menu.eesz.4M2M.build.spiffs_pagesize=256 -espino.menu.eesz.4M2M.upload.maximum_size=1044464 espino.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 espino.menu.eesz.4M2M.build.spiffs_start=0x200000 espino.menu.eesz.4M2M.build.spiffs_end=0x3FA000 espino.menu.eesz.4M2M.build.spiffs_blocksize=8192 espino.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) espino.menu.eesz.4M3M.build.flash_size=4M -espino.menu.eesz.4M3M.build.flash_size_bytes=0x400000 espino.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld espino.menu.eesz.4M3M.build.spiffs_pagesize=256 -espino.menu.eesz.4M3M.upload.maximum_size=1044464 espino.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 espino.menu.eesz.4M3M.build.spiffs_start=0x100000 espino.menu.eesz.4M3M.build.spiffs_end=0x3FA000 espino.menu.eesz.4M3M.build.spiffs_blocksize=8192 espino.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) espino.menu.eesz.4M1M.build.flash_size=4M -espino.menu.eesz.4M1M.build.flash_size_bytes=0x400000 espino.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld espino.menu.eesz.4M1M.build.spiffs_pagesize=256 -espino.menu.eesz.4M1M.upload.maximum_size=1044464 espino.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 espino.menu.eesz.4M1M.build.spiffs_start=0x300000 espino.menu.eesz.4M1M.build.spiffs_end=0x3FA000 espino.menu.eesz.4M1M.build.spiffs_blocksize=8192 espino.menu.eesz.4M=4MB (FS:none OTA:~1019KB) espino.menu.eesz.4M.build.flash_size=4M -espino.menu.eesz.4M.build.flash_size_bytes=0x400000 espino.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld espino.menu.eesz.4M.build.spiffs_pagesize=256 -espino.menu.eesz.4M.upload.maximum_size=1044464 espino.menu.eesz.4M.build.rfcal_addr=0x3FC000 espino.menu.ip.lm2f=v2 Lower Memory espino.menu.ip.lm2f.build.lwip_include=lwip2/include @@ -2865,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 @@ -2909,10 +3020,22 @@ espino.menu.lvl.OOM=OOM espino.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM espino.menu.lvl.MDNS=MDNS espino.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +espino.menu.lvl.HWDT=HWDT +espino.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +espino.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +espino.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K espino.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS espino.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 +espino.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +espino.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 +espino.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +espino.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 espino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS espino.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 +espino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +espino.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 +espino.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 +espino.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 espino.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG espino.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG espino.menu.wipe.none=Only Sketch @@ -2939,6 +3062,15 @@ espino.menu.baud.921600=921600 espino.menu.baud.921600.upload.speed=921600 espino.menu.baud.3000000=3000000 espino.menu.baud.3000000.upload.speed=3000000 +espino.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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 @@ -2953,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 @@ -2979,10 +3112,6 @@ espresso_lite_v1.menu.ssl.all=All SSL ciphers (most compatible) espresso_lite_v1.menu.ssl.all.build.sslflags= espresso_lite_v1.menu.ssl.basic=Basic SSL ciphers (lower ROM use) espresso_lite_v1.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -espresso_lite_v1.menu.waveform.pwm=Locked PWM -espresso_lite_v1.menu.waveform.pwm.build.waveform= -espresso_lite_v1.menu.waveform.phase=Locked Phase -espresso_lite_v1.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE espresso_lite_v1.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) espresso_lite_v1.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 espresso_lite_v1.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -2991,6 +3120,10 @@ espresso_lite_v1.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) espresso_lite_v1.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP espresso_lite_v1.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) espresso_lite_v1.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +espresso_lite_v1.menu.mmu.ext128k=128K Heap External 23LC1024 +espresso_lite_v1.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +espresso_lite_v1.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +espresso_lite_v1.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 espresso_lite_v1.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM espresso_lite_v1.menu.non32xfer.fast.build.non32xferflags= espresso_lite_v1.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -3000,40 +3133,32 @@ espresso_lite_v1.build.flash_flags=-DFLASHMODE_DIO espresso_lite_v1.build.flash_freq=40 espresso_lite_v1.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) espresso_lite_v1.menu.eesz.4M2M.build.flash_size=4M -espresso_lite_v1.menu.eesz.4M2M.build.flash_size_bytes=0x400000 espresso_lite_v1.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld espresso_lite_v1.menu.eesz.4M2M.build.spiffs_pagesize=256 -espresso_lite_v1.menu.eesz.4M2M.upload.maximum_size=1044464 espresso_lite_v1.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 espresso_lite_v1.menu.eesz.4M2M.build.spiffs_start=0x200000 espresso_lite_v1.menu.eesz.4M2M.build.spiffs_end=0x3FA000 espresso_lite_v1.menu.eesz.4M2M.build.spiffs_blocksize=8192 espresso_lite_v1.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) espresso_lite_v1.menu.eesz.4M3M.build.flash_size=4M -espresso_lite_v1.menu.eesz.4M3M.build.flash_size_bytes=0x400000 espresso_lite_v1.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld espresso_lite_v1.menu.eesz.4M3M.build.spiffs_pagesize=256 -espresso_lite_v1.menu.eesz.4M3M.upload.maximum_size=1044464 espresso_lite_v1.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 espresso_lite_v1.menu.eesz.4M3M.build.spiffs_start=0x100000 espresso_lite_v1.menu.eesz.4M3M.build.spiffs_end=0x3FA000 espresso_lite_v1.menu.eesz.4M3M.build.spiffs_blocksize=8192 espresso_lite_v1.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) espresso_lite_v1.menu.eesz.4M1M.build.flash_size=4M -espresso_lite_v1.menu.eesz.4M1M.build.flash_size_bytes=0x400000 espresso_lite_v1.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld espresso_lite_v1.menu.eesz.4M1M.build.spiffs_pagesize=256 -espresso_lite_v1.menu.eesz.4M1M.upload.maximum_size=1044464 espresso_lite_v1.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 espresso_lite_v1.menu.eesz.4M1M.build.spiffs_start=0x300000 espresso_lite_v1.menu.eesz.4M1M.build.spiffs_end=0x3FA000 espresso_lite_v1.menu.eesz.4M1M.build.spiffs_blocksize=8192 espresso_lite_v1.menu.eesz.4M=4MB (FS:none OTA:~1019KB) espresso_lite_v1.menu.eesz.4M.build.flash_size=4M -espresso_lite_v1.menu.eesz.4M.build.flash_size_bytes=0x400000 espresso_lite_v1.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld espresso_lite_v1.menu.eesz.4M.build.spiffs_pagesize=256 -espresso_lite_v1.menu.eesz.4M.upload.maximum_size=1044464 espresso_lite_v1.menu.eesz.4M.build.rfcal_addr=0x3FC000 espresso_lite_v1.menu.ResetMethod.nodemcu=dtr (aka nodemcu) espresso_lite_v1.menu.ResetMethod.nodemcu.upload.resetmethod=--before default_reset --after hard_reset @@ -3071,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 @@ -3115,10 +3246,22 @@ espresso_lite_v1.menu.lvl.OOM=OOM espresso_lite_v1.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM espresso_lite_v1.menu.lvl.MDNS=MDNS espresso_lite_v1.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +espresso_lite_v1.menu.lvl.HWDT=HWDT +espresso_lite_v1.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +espresso_lite_v1.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +espresso_lite_v1.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K espresso_lite_v1.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS espresso_lite_v1.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 +espresso_lite_v1.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +espresso_lite_v1.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 +espresso_lite_v1.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +espresso_lite_v1.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 espresso_lite_v1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS espresso_lite_v1.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 +espresso_lite_v1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +espresso_lite_v1.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 +espresso_lite_v1.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 +espresso_lite_v1.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 espresso_lite_v1.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG espresso_lite_v1.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG espresso_lite_v1.menu.wipe.none=Only Sketch @@ -3145,6 +3288,15 @@ espresso_lite_v1.menu.baud.921600=921600 espresso_lite_v1.menu.baud.921600.upload.speed=921600 espresso_lite_v1.menu.baud.3000000=3000000 espresso_lite_v1.menu.baud.3000000.upload.speed=3000000 +espresso_lite_v1.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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 @@ -3159,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 @@ -3185,10 +3338,6 @@ espresso_lite_v2.menu.ssl.all=All SSL ciphers (most compatible) espresso_lite_v2.menu.ssl.all.build.sslflags= espresso_lite_v2.menu.ssl.basic=Basic SSL ciphers (lower ROM use) espresso_lite_v2.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -espresso_lite_v2.menu.waveform.pwm=Locked PWM -espresso_lite_v2.menu.waveform.pwm.build.waveform= -espresso_lite_v2.menu.waveform.phase=Locked Phase -espresso_lite_v2.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE espresso_lite_v2.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) espresso_lite_v2.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 espresso_lite_v2.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -3197,6 +3346,10 @@ espresso_lite_v2.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) espresso_lite_v2.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP espresso_lite_v2.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) espresso_lite_v2.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +espresso_lite_v2.menu.mmu.ext128k=128K Heap External 23LC1024 +espresso_lite_v2.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +espresso_lite_v2.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +espresso_lite_v2.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 espresso_lite_v2.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM espresso_lite_v2.menu.non32xfer.fast.build.non32xferflags= espresso_lite_v2.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -3206,40 +3359,32 @@ espresso_lite_v2.build.flash_flags=-DFLASHMODE_DIO espresso_lite_v2.build.flash_freq=40 espresso_lite_v2.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) espresso_lite_v2.menu.eesz.4M2M.build.flash_size=4M -espresso_lite_v2.menu.eesz.4M2M.build.flash_size_bytes=0x400000 espresso_lite_v2.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld espresso_lite_v2.menu.eesz.4M2M.build.spiffs_pagesize=256 -espresso_lite_v2.menu.eesz.4M2M.upload.maximum_size=1044464 espresso_lite_v2.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 espresso_lite_v2.menu.eesz.4M2M.build.spiffs_start=0x200000 espresso_lite_v2.menu.eesz.4M2M.build.spiffs_end=0x3FA000 espresso_lite_v2.menu.eesz.4M2M.build.spiffs_blocksize=8192 espresso_lite_v2.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) espresso_lite_v2.menu.eesz.4M3M.build.flash_size=4M -espresso_lite_v2.menu.eesz.4M3M.build.flash_size_bytes=0x400000 espresso_lite_v2.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld espresso_lite_v2.menu.eesz.4M3M.build.spiffs_pagesize=256 -espresso_lite_v2.menu.eesz.4M3M.upload.maximum_size=1044464 espresso_lite_v2.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 espresso_lite_v2.menu.eesz.4M3M.build.spiffs_start=0x100000 espresso_lite_v2.menu.eesz.4M3M.build.spiffs_end=0x3FA000 espresso_lite_v2.menu.eesz.4M3M.build.spiffs_blocksize=8192 espresso_lite_v2.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) espresso_lite_v2.menu.eesz.4M1M.build.flash_size=4M -espresso_lite_v2.menu.eesz.4M1M.build.flash_size_bytes=0x400000 espresso_lite_v2.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld espresso_lite_v2.menu.eesz.4M1M.build.spiffs_pagesize=256 -espresso_lite_v2.menu.eesz.4M1M.upload.maximum_size=1044464 espresso_lite_v2.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 espresso_lite_v2.menu.eesz.4M1M.build.spiffs_start=0x300000 espresso_lite_v2.menu.eesz.4M1M.build.spiffs_end=0x3FA000 espresso_lite_v2.menu.eesz.4M1M.build.spiffs_blocksize=8192 espresso_lite_v2.menu.eesz.4M=4MB (FS:none OTA:~1019KB) espresso_lite_v2.menu.eesz.4M.build.flash_size=4M -espresso_lite_v2.menu.eesz.4M.build.flash_size_bytes=0x400000 espresso_lite_v2.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld espresso_lite_v2.menu.eesz.4M.build.spiffs_pagesize=256 -espresso_lite_v2.menu.eesz.4M.upload.maximum_size=1044464 espresso_lite_v2.menu.eesz.4M.build.rfcal_addr=0x3FC000 espresso_lite_v2.menu.ResetMethod.nodemcu=dtr (aka nodemcu) espresso_lite_v2.menu.ResetMethod.nodemcu.upload.resetmethod=--before default_reset --after hard_reset @@ -3277,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 @@ -3321,10 +3472,22 @@ espresso_lite_v2.menu.lvl.OOM=OOM espresso_lite_v2.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM espresso_lite_v2.menu.lvl.MDNS=MDNS espresso_lite_v2.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +espresso_lite_v2.menu.lvl.HWDT=HWDT +espresso_lite_v2.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +espresso_lite_v2.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +espresso_lite_v2.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K espresso_lite_v2.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS espresso_lite_v2.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 +espresso_lite_v2.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +espresso_lite_v2.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 +espresso_lite_v2.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +espresso_lite_v2.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 espresso_lite_v2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS espresso_lite_v2.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 +espresso_lite_v2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +espresso_lite_v2.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 +espresso_lite_v2.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 +espresso_lite_v2.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 espresso_lite_v2.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG espresso_lite_v2.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG espresso_lite_v2.menu.wipe.none=Only Sketch @@ -3351,11 +3514,19 @@ espresso_lite_v2.menu.baud.921600=921600 espresso_lite_v2.menu.baud.921600.upload.speed=921600 espresso_lite_v2.menu.baud.3000000=3000000 espresso_lite_v2.menu.baud.3000000.upload.speed=3000000 +espresso_lite_v2.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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 sonoff.build.board=ESP8266_SONOFF_SV -sonoff.build.extra_flags=-DESP8266 sonoff.build.flash_size=1M sonoff.build.variant=itead sonoff.menu.BoardModel.sonoffBasic=ITEAD Sonoff Basic @@ -3375,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 @@ -3401,10 +3573,6 @@ sonoff.menu.ssl.all=All SSL ciphers (most compatible) sonoff.menu.ssl.all.build.sslflags= sonoff.menu.ssl.basic=Basic SSL ciphers (lower ROM use) sonoff.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -sonoff.menu.waveform.pwm=Locked PWM -sonoff.menu.waveform.pwm.build.waveform= -sonoff.menu.waveform.phase=Locked Phase -sonoff.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE sonoff.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) sonoff.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 sonoff.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -3413,6 +3581,10 @@ sonoff.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) sonoff.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP sonoff.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) sonoff.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +sonoff.menu.mmu.ext128k=128K Heap External 23LC1024 +sonoff.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +sonoff.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +sonoff.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 sonoff.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM sonoff.menu.non32xfer.fast.build.non32xferflags= sonoff.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -3423,80 +3595,64 @@ sonoff.build.flash_flags=-DFLASHMODE_DOUT sonoff.build.flash_freq=40 sonoff.menu.eesz.1M64=1MB (FS:64KB OTA:~470KB) sonoff.menu.eesz.1M64.build.flash_size=1M -sonoff.menu.eesz.1M64.build.flash_size_bytes=0x100000 sonoff.menu.eesz.1M64.build.flash_ld=eagle.flash.1m64.ld sonoff.menu.eesz.1M64.build.spiffs_pagesize=256 -sonoff.menu.eesz.1M64.upload.maximum_size=958448 sonoff.menu.eesz.1M64.build.rfcal_addr=0xFC000 sonoff.menu.eesz.1M64.build.spiffs_start=0xEB000 sonoff.menu.eesz.1M64.build.spiffs_end=0xFB000 sonoff.menu.eesz.1M64.build.spiffs_blocksize=4096 sonoff.menu.eesz.1M128=1MB (FS:128KB OTA:~438KB) sonoff.menu.eesz.1M128.build.flash_size=1M -sonoff.menu.eesz.1M128.build.flash_size_bytes=0x100000 sonoff.menu.eesz.1M128.build.flash_ld=eagle.flash.1m128.ld sonoff.menu.eesz.1M128.build.spiffs_pagesize=256 -sonoff.menu.eesz.1M128.upload.maximum_size=892912 sonoff.menu.eesz.1M128.build.rfcal_addr=0xFC000 sonoff.menu.eesz.1M128.build.spiffs_start=0xDB000 sonoff.menu.eesz.1M128.build.spiffs_end=0xFB000 sonoff.menu.eesz.1M128.build.spiffs_blocksize=4096 sonoff.menu.eesz.1M144=1MB (FS:144KB OTA:~430KB) sonoff.menu.eesz.1M144.build.flash_size=1M -sonoff.menu.eesz.1M144.build.flash_size_bytes=0x100000 sonoff.menu.eesz.1M144.build.flash_ld=eagle.flash.1m144.ld sonoff.menu.eesz.1M144.build.spiffs_pagesize=256 -sonoff.menu.eesz.1M144.upload.maximum_size=876528 sonoff.menu.eesz.1M144.build.rfcal_addr=0xFC000 sonoff.menu.eesz.1M144.build.spiffs_start=0xD7000 sonoff.menu.eesz.1M144.build.spiffs_end=0xFB000 sonoff.menu.eesz.1M144.build.spiffs_blocksize=4096 sonoff.menu.eesz.1M160=1MB (FS:160KB OTA:~422KB) sonoff.menu.eesz.1M160.build.flash_size=1M -sonoff.menu.eesz.1M160.build.flash_size_bytes=0x100000 sonoff.menu.eesz.1M160.build.flash_ld=eagle.flash.1m160.ld sonoff.menu.eesz.1M160.build.spiffs_pagesize=256 -sonoff.menu.eesz.1M160.upload.maximum_size=860144 sonoff.menu.eesz.1M160.build.rfcal_addr=0xFC000 sonoff.menu.eesz.1M160.build.spiffs_start=0xD3000 sonoff.menu.eesz.1M160.build.spiffs_end=0xFB000 sonoff.menu.eesz.1M160.build.spiffs_blocksize=4096 sonoff.menu.eesz.1M192=1MB (FS:192KB OTA:~406KB) sonoff.menu.eesz.1M192.build.flash_size=1M -sonoff.menu.eesz.1M192.build.flash_size_bytes=0x100000 sonoff.menu.eesz.1M192.build.flash_ld=eagle.flash.1m192.ld sonoff.menu.eesz.1M192.build.spiffs_pagesize=256 -sonoff.menu.eesz.1M192.upload.maximum_size=827376 sonoff.menu.eesz.1M192.build.rfcal_addr=0xFC000 sonoff.menu.eesz.1M192.build.spiffs_start=0xCB000 sonoff.menu.eesz.1M192.build.spiffs_end=0xFB000 sonoff.menu.eesz.1M192.build.spiffs_blocksize=4096 sonoff.menu.eesz.1M256=1MB (FS:256KB OTA:~374KB) sonoff.menu.eesz.1M256.build.flash_size=1M -sonoff.menu.eesz.1M256.build.flash_size_bytes=0x100000 sonoff.menu.eesz.1M256.build.flash_ld=eagle.flash.1m256.ld sonoff.menu.eesz.1M256.build.spiffs_pagesize=256 -sonoff.menu.eesz.1M256.upload.maximum_size=761840 sonoff.menu.eesz.1M256.build.rfcal_addr=0xFC000 sonoff.menu.eesz.1M256.build.spiffs_start=0xBB000 sonoff.menu.eesz.1M256.build.spiffs_end=0xFB000 sonoff.menu.eesz.1M256.build.spiffs_blocksize=4096 sonoff.menu.eesz.1M512=1MB (FS:512KB OTA:~246KB) sonoff.menu.eesz.1M512.build.flash_size=1M -sonoff.menu.eesz.1M512.build.flash_size_bytes=0x100000 sonoff.menu.eesz.1M512.build.flash_ld=eagle.flash.1m512.ld sonoff.menu.eesz.1M512.build.spiffs_pagesize=256 -sonoff.menu.eesz.1M512.upload.maximum_size=499696 sonoff.menu.eesz.1M512.build.rfcal_addr=0xFC000 sonoff.menu.eesz.1M512.build.spiffs_start=0x7B000 sonoff.menu.eesz.1M512.build.spiffs_end=0xFB000 sonoff.menu.eesz.1M512.build.spiffs_blocksize=8192 sonoff.menu.eesz.1M=1MB (FS:none OTA:~502KB) sonoff.menu.eesz.1M.build.flash_size=1M -sonoff.menu.eesz.1M.build.flash_size_bytes=0x100000 sonoff.menu.eesz.1M.build.flash_ld=eagle.flash.1m.ld sonoff.menu.eesz.1M.build.spiffs_pagesize=256 -sonoff.menu.eesz.1M.upload.maximum_size=1023984 sonoff.menu.eesz.1M.build.rfcal_addr=0xFC000 sonoff.menu.ip.lm2f=v2 Lower Memory sonoff.menu.ip.lm2f.build.lwip_include=lwip2/include @@ -3530,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 @@ -3574,10 +3736,22 @@ sonoff.menu.lvl.OOM=OOM sonoff.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM sonoff.menu.lvl.MDNS=MDNS sonoff.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +sonoff.menu.lvl.HWDT=HWDT +sonoff.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +sonoff.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +sonoff.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K sonoff.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS sonoff.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 +sonoff.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +sonoff.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 +sonoff.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +sonoff.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 sonoff.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS sonoff.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 +sonoff.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +sonoff.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 +sonoff.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 +sonoff.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 sonoff.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG sonoff.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG sonoff.menu.wipe.none=Only Sketch @@ -3604,10 +3778,19 @@ sonoff.menu.baud.921600=921600 sonoff.menu.baud.921600.upload.speed=921600 sonoff.menu.baud.3000000=3000000 sonoff.menu.baud.3000000.upload.speed=3000000 +sonoff.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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 -inventone.build.board=ESP8266_GENERIC +inventone.build.board=ESP8266_INVENT_ONE inventone.build.variant=inventone inventone.upload.tool=esptool inventone.upload.maximum_data_size=81920 @@ -3618,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 @@ -3644,10 +3828,6 @@ inventone.menu.ssl.all=All SSL ciphers (most compatible) inventone.menu.ssl.all.build.sslflags= inventone.menu.ssl.basic=Basic SSL ciphers (lower ROM use) inventone.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -inventone.menu.waveform.pwm=Locked PWM -inventone.menu.waveform.pwm.build.waveform= -inventone.menu.waveform.phase=Locked Phase -inventone.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE inventone.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) inventone.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 inventone.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -3656,6 +3836,10 @@ inventone.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) inventone.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP inventone.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) inventone.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +inventone.menu.mmu.ext128k=128K Heap External 23LC1024 +inventone.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +inventone.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +inventone.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 inventone.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM inventone.menu.non32xfer.fast.build.non32xferflags= inventone.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -3666,40 +3850,32 @@ inventone.build.flash_flags=-DFLASHMODE_DIO inventone.build.flash_freq=40 inventone.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) inventone.menu.eesz.4M2M.build.flash_size=4M -inventone.menu.eesz.4M2M.build.flash_size_bytes=0x400000 inventone.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld inventone.menu.eesz.4M2M.build.spiffs_pagesize=256 -inventone.menu.eesz.4M2M.upload.maximum_size=1044464 inventone.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 inventone.menu.eesz.4M2M.build.spiffs_start=0x200000 inventone.menu.eesz.4M2M.build.spiffs_end=0x3FA000 inventone.menu.eesz.4M2M.build.spiffs_blocksize=8192 inventone.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) inventone.menu.eesz.4M3M.build.flash_size=4M -inventone.menu.eesz.4M3M.build.flash_size_bytes=0x400000 inventone.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld inventone.menu.eesz.4M3M.build.spiffs_pagesize=256 -inventone.menu.eesz.4M3M.upload.maximum_size=1044464 inventone.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 inventone.menu.eesz.4M3M.build.spiffs_start=0x100000 inventone.menu.eesz.4M3M.build.spiffs_end=0x3FA000 inventone.menu.eesz.4M3M.build.spiffs_blocksize=8192 inventone.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) inventone.menu.eesz.4M1M.build.flash_size=4M -inventone.menu.eesz.4M1M.build.flash_size_bytes=0x400000 inventone.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld inventone.menu.eesz.4M1M.build.spiffs_pagesize=256 -inventone.menu.eesz.4M1M.upload.maximum_size=1044464 inventone.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 inventone.menu.eesz.4M1M.build.spiffs_start=0x300000 inventone.menu.eesz.4M1M.build.spiffs_end=0x3FA000 inventone.menu.eesz.4M1M.build.spiffs_blocksize=8192 inventone.menu.eesz.4M=4MB (FS:none OTA:~1019KB) inventone.menu.eesz.4M.build.flash_size=4M -inventone.menu.eesz.4M.build.flash_size_bytes=0x400000 inventone.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld inventone.menu.eesz.4M.build.spiffs_pagesize=256 -inventone.menu.eesz.4M.upload.maximum_size=1044464 inventone.menu.eesz.4M.build.rfcal_addr=0x3FC000 inventone.menu.ip.lm2f=v2 Lower Memory inventone.menu.ip.lm2f.build.lwip_include=lwip2/include @@ -3733,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 @@ -3777,10 +3959,22 @@ inventone.menu.lvl.OOM=OOM inventone.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM inventone.menu.lvl.MDNS=MDNS inventone.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +inventone.menu.lvl.HWDT=HWDT +inventone.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +inventone.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +inventone.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K inventone.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS inventone.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 +inventone.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +inventone.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 +inventone.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +inventone.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 inventone.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS inventone.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 +inventone.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +inventone.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 +inventone.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 +inventone.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 inventone.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG inventone.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG inventone.menu.wipe.none=Only Sketch @@ -3807,6 +4001,254 @@ inventone.menu.baud.921600=921600 inventone.menu.baud.921600.upload.speed=921600 inventone.menu.baud.3000000=3000000 inventone.menu.baud.3000000.upload.speed=3000000 +inventone.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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 @@ -3821,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 @@ -3847,10 +4290,6 @@ d1_mini.menu.ssl.all=All SSL ciphers (most compatible) d1_mini.menu.ssl.all.build.sslflags= d1_mini.menu.ssl.basic=Basic SSL ciphers (lower ROM use) d1_mini.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -d1_mini.menu.waveform.pwm=Locked PWM -d1_mini.menu.waveform.pwm.build.waveform= -d1_mini.menu.waveform.phase=Locked Phase -d1_mini.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE d1_mini.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) d1_mini.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 d1_mini.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -3859,6 +4298,10 @@ d1_mini.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) d1_mini.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP d1_mini.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) d1_mini.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +d1_mini.menu.mmu.ext128k=128K Heap External 23LC1024 +d1_mini.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +d1_mini.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +d1_mini.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 d1_mini.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM d1_mini.menu.non32xfer.fast.build.non32xferflags= d1_mini.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -3869,40 +4312,32 @@ d1_mini.build.flash_flags=-DFLASHMODE_DIO d1_mini.build.flash_freq=40 d1_mini.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) d1_mini.menu.eesz.4M2M.build.flash_size=4M -d1_mini.menu.eesz.4M2M.build.flash_size_bytes=0x400000 d1_mini.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld d1_mini.menu.eesz.4M2M.build.spiffs_pagesize=256 -d1_mini.menu.eesz.4M2M.upload.maximum_size=1044464 d1_mini.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 d1_mini.menu.eesz.4M2M.build.spiffs_start=0x200000 d1_mini.menu.eesz.4M2M.build.spiffs_end=0x3FA000 d1_mini.menu.eesz.4M2M.build.spiffs_blocksize=8192 d1_mini.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) d1_mini.menu.eesz.4M3M.build.flash_size=4M -d1_mini.menu.eesz.4M3M.build.flash_size_bytes=0x400000 d1_mini.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld d1_mini.menu.eesz.4M3M.build.spiffs_pagesize=256 -d1_mini.menu.eesz.4M3M.upload.maximum_size=1044464 d1_mini.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 d1_mini.menu.eesz.4M3M.build.spiffs_start=0x100000 d1_mini.menu.eesz.4M3M.build.spiffs_end=0x3FA000 d1_mini.menu.eesz.4M3M.build.spiffs_blocksize=8192 d1_mini.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) d1_mini.menu.eesz.4M1M.build.flash_size=4M -d1_mini.menu.eesz.4M1M.build.flash_size_bytes=0x400000 d1_mini.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld d1_mini.menu.eesz.4M1M.build.spiffs_pagesize=256 -d1_mini.menu.eesz.4M1M.upload.maximum_size=1044464 d1_mini.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 d1_mini.menu.eesz.4M1M.build.spiffs_start=0x300000 d1_mini.menu.eesz.4M1M.build.spiffs_end=0x3FA000 d1_mini.menu.eesz.4M1M.build.spiffs_blocksize=8192 d1_mini.menu.eesz.4M=4MB (FS:none OTA:~1019KB) d1_mini.menu.eesz.4M.build.flash_size=4M -d1_mini.menu.eesz.4M.build.flash_size_bytes=0x400000 d1_mini.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld d1_mini.menu.eesz.4M.build.spiffs_pagesize=256 -d1_mini.menu.eesz.4M.upload.maximum_size=1044464 d1_mini.menu.eesz.4M.build.rfcal_addr=0x3FC000 d1_mini.menu.ip.lm2f=v2 Lower Memory d1_mini.menu.ip.lm2f.build.lwip_include=lwip2/include @@ -3936,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 @@ -3980,10 +4421,22 @@ d1_mini.menu.lvl.OOM=OOM d1_mini.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM d1_mini.menu.lvl.MDNS=MDNS d1_mini.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +d1_mini.menu.lvl.HWDT=HWDT +d1_mini.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +d1_mini.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +d1_mini.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K d1_mini.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS d1_mini.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_mini.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +d1_mini.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_mini.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +d1_mini.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_mini.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS d1_mini.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_mini.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +d1_mini.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_mini.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_mini.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_mini.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG d1_mini.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG d1_mini.menu.wipe.none=Only Sketch @@ -4010,6 +4463,255 @@ d1_mini.menu.baud.512000.windows=512000 d1_mini.menu.baud.512000.upload.speed=512000 d1_mini.menu.baud.3000000=3000000 d1_mini.menu.baud.3000000.upload.speed=3000000 +d1_mini.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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) +d1_mini_clone.build.board=ESP8266_WEMOS_D1MINI +d1_mini_clone.build.variant=d1_mini +d1_mini_clone.upload.tool=esptool +d1_mini_clone.upload.maximum_data_size=81920 +d1_mini_clone.upload.wait_for_upload_port=true +d1_mini_clone.upload.erase_cmd= +d1_mini_clone.serial.disableDTR=true +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 +d1_mini_clone.menu.xtal.80.build.f_cpu=80000000L +d1_mini_clone.menu.xtal.160=160 MHz +d1_mini_clone.menu.xtal.160.build.f_cpu=160000000L +d1_mini_clone.menu.vt.flash=Flash +d1_mini_clone.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +d1_mini_clone.menu.vt.heap=Heap +d1_mini_clone.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +d1_mini_clone.menu.vt.iram=IRAM +d1_mini_clone.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +d1_mini_clone.menu.exception.disabled=Disabled (new aborts on oom) +d1_mini_clone.menu.exception.disabled.build.exception_flags=-fno-exceptions +d1_mini_clone.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +d1_mini_clone.menu.exception.enabled=Enabled +d1_mini_clone.menu.exception.enabled.build.exception_flags=-fexceptions +d1_mini_clone.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +d1_mini_clone.menu.stacksmash.disabled=Disabled +d1_mini_clone.menu.stacksmash.disabled.build.stacksmash_flags= +d1_mini_clone.menu.stacksmash.enabled=Enabled +d1_mini_clone.menu.stacksmash.enabled.build.stacksmash_flags=-fstack-protector +d1_mini_clone.menu.ssl.all=All SSL ciphers (most compatible) +d1_mini_clone.menu.ssl.all.build.sslflags= +d1_mini_clone.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +d1_mini_clone.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +d1_mini_clone.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +d1_mini_clone.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +d1_mini_clone.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +d1_mini_clone.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +d1_mini_clone.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +d1_mini_clone.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +d1_mini_clone.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +d1_mini_clone.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +d1_mini_clone.menu.mmu.ext128k=128K Heap External 23LC1024 +d1_mini_clone.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +d1_mini_clone.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +d1_mini_clone.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +d1_mini_clone.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +d1_mini_clone.menu.non32xfer.fast.build.non32xferflags= +d1_mini_clone.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +d1_mini_clone.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER +d1_mini_clone.upload.resetmethod=--before default_reset --after hard_reset +d1_mini_clone.menu.FlashMode.dout=DOUT (compatible) +d1_mini_clone.menu.FlashMode.dout.build.flash_mode=dout +d1_mini_clone.menu.FlashMode.dout.build.flash_flags=-DFLASHMODE_DOUT +d1_mini_clone.menu.FlashMode.dio=DIO +d1_mini_clone.menu.FlashMode.dio.build.flash_mode=dio +d1_mini_clone.menu.FlashMode.dio.build.flash_flags=-DFLASHMODE_DIO +d1_mini_clone.menu.FlashMode.qout=QOUT +d1_mini_clone.menu.FlashMode.qout.build.flash_mode=qout +d1_mini_clone.menu.FlashMode.qout.build.flash_flags=-DFLASHMODE_QOUT +d1_mini_clone.menu.FlashMode.qio=QIO (fast) +d1_mini_clone.menu.FlashMode.qio.build.flash_mode=qio +d1_mini_clone.menu.FlashMode.qio.build.flash_flags=-DFLASHMODE_QIO +d1_mini_clone.menu.FlashFreq.40=40MHz +d1_mini_clone.menu.FlashFreq.40.build.flash_freq=40 +d1_mini_clone.menu.FlashFreq.80=80MHz +d1_mini_clone.menu.FlashFreq.80.build.flash_freq=80 +d1_mini_clone.menu.FlashFreq.20=20MHz +d1_mini_clone.menu.FlashFreq.20.build.flash_freq=20 +d1_mini_clone.menu.FlashFreq.26=26MHz +d1_mini_clone.menu.FlashFreq.26.build.flash_freq=26 +d1_mini_clone.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) +d1_mini_clone.menu.eesz.4M2M.build.flash_size=4M +d1_mini_clone.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +d1_mini_clone.menu.eesz.4M2M.build.spiffs_pagesize=256 +d1_mini_clone.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +d1_mini_clone.menu.eesz.4M2M.build.spiffs_start=0x200000 +d1_mini_clone.menu.eesz.4M2M.build.spiffs_end=0x3FA000 +d1_mini_clone.menu.eesz.4M2M.build.spiffs_blocksize=8192 +d1_mini_clone.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) +d1_mini_clone.menu.eesz.4M3M.build.flash_size=4M +d1_mini_clone.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +d1_mini_clone.menu.eesz.4M3M.build.spiffs_pagesize=256 +d1_mini_clone.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +d1_mini_clone.menu.eesz.4M3M.build.spiffs_start=0x100000 +d1_mini_clone.menu.eesz.4M3M.build.spiffs_end=0x3FA000 +d1_mini_clone.menu.eesz.4M3M.build.spiffs_blocksize=8192 +d1_mini_clone.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) +d1_mini_clone.menu.eesz.4M1M.build.flash_size=4M +d1_mini_clone.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +d1_mini_clone.menu.eesz.4M1M.build.spiffs_pagesize=256 +d1_mini_clone.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +d1_mini_clone.menu.eesz.4M1M.build.spiffs_start=0x300000 +d1_mini_clone.menu.eesz.4M1M.build.spiffs_end=0x3FA000 +d1_mini_clone.menu.eesz.4M1M.build.spiffs_blocksize=8192 +d1_mini_clone.menu.eesz.4M=4MB (FS:none OTA:~1019KB) +d1_mini_clone.menu.eesz.4M.build.flash_size=4M +d1_mini_clone.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +d1_mini_clone.menu.eesz.4M.build.spiffs_pagesize=256 +d1_mini_clone.menu.eesz.4M.build.rfcal_addr=0x3FC000 +d1_mini_clone.menu.ip.lm2f=v2 Lower Memory +d1_mini_clone.menu.ip.lm2f.build.lwip_include=lwip2/include +d1_mini_clone.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +d1_mini_clone.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +d1_mini_clone.menu.ip.hb2f=v2 Higher Bandwidth +d1_mini_clone.menu.ip.hb2f.build.lwip_include=lwip2/include +d1_mini_clone.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +d1_mini_clone.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +d1_mini_clone.menu.ip.lm2n=v2 Lower Memory (no features) +d1_mini_clone.menu.ip.lm2n.build.lwip_include=lwip2/include +d1_mini_clone.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +d1_mini_clone.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +d1_mini_clone.menu.ip.hb2n=v2 Higher Bandwidth (no features) +d1_mini_clone.menu.ip.hb2n.build.lwip_include=lwip2/include +d1_mini_clone.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +d1_mini_clone.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +d1_mini_clone.menu.ip.lm6f=v2 IPv6 Lower Memory +d1_mini_clone.menu.ip.lm6f.build.lwip_include=lwip2/include +d1_mini_clone.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +d1_mini_clone.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +d1_mini_clone.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +d1_mini_clone.menu.ip.hb6f.build.lwip_include=lwip2/include +d1_mini_clone.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +d1_mini_clone.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +d1_mini_clone.menu.dbg.Disabled=Disabled +d1_mini_clone.menu.dbg.Disabled.build.debug_port= +d1_mini_clone.menu.dbg.Serial=Serial +d1_mini_clone.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +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 +d1_mini_clone.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +d1_mini_clone.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +d1_mini_clone.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +d1_mini_clone.menu.lvl.HTTP_SERVER=HTTP_SERVER +d1_mini_clone.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +d1_mini_clone.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +d1_mini_clone.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +d1_mini_clone.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +d1_mini_clone.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +d1_mini_clone.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +d1_mini_clone.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +d1_mini_clone.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +d1_mini_clone.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +d1_mini_clone.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +d1_mini_clone.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +d1_mini_clone.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +d1_mini_clone.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_mini_clone.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +d1_mini_clone.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +d1_mini_clone.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +d1_mini_clone.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +d1_mini_clone.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +d1_mini_clone.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_mini_clone.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +d1_mini_clone.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +d1_mini_clone.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +d1_mini_clone.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_mini_clone.menu.lvl.CORE=CORE +d1_mini_clone.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +d1_mini_clone.menu.lvl.WIFI=WIFI +d1_mini_clone.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +d1_mini_clone.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +d1_mini_clone.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +d1_mini_clone.menu.lvl.UPDATER=UPDATER +d1_mini_clone.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +d1_mini_clone.menu.lvl.OTA=OTA +d1_mini_clone.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +d1_mini_clone.menu.lvl.OOM=OOM +d1_mini_clone.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +d1_mini_clone.menu.lvl.MDNS=MDNS +d1_mini_clone.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +d1_mini_clone.menu.lvl.HWDT=HWDT +d1_mini_clone.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +d1_mini_clone.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +d1_mini_clone.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K +d1_mini_clone.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +d1_mini_clone.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_mini_clone.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +d1_mini_clone.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_mini_clone.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +d1_mini_clone.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_mini_clone.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +d1_mini_clone.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_mini_clone.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +d1_mini_clone.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_mini_clone.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_mini_clone.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_mini_clone.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +d1_mini_clone.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +d1_mini_clone.menu.wipe.none=Only Sketch +d1_mini_clone.menu.wipe.none.upload.erase_cmd= +d1_mini_clone.menu.wipe.sdk=Sketch + WiFi Settings +d1_mini_clone.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +d1_mini_clone.menu.wipe.all=All Flash Contents +d1_mini_clone.menu.wipe.all.upload.erase_cmd=erase_flash +d1_mini_clone.menu.baud.921600=921600 +d1_mini_clone.menu.baud.921600.upload.speed=921600 +d1_mini_clone.menu.baud.57600=57600 +d1_mini_clone.menu.baud.57600.upload.speed=57600 +d1_mini_clone.menu.baud.115200=115200 +d1_mini_clone.menu.baud.115200.upload.speed=115200 +d1_mini_clone.menu.baud.230400.linux=230400 +d1_mini_clone.menu.baud.230400.macosx=230400 +d1_mini_clone.menu.baud.230400.upload.speed=230400 +d1_mini_clone.menu.baud.256000.windows=256000 +d1_mini_clone.menu.baud.256000.upload.speed=256000 +d1_mini_clone.menu.baud.460800.linux=460800 +d1_mini_clone.menu.baud.460800.macosx=460800 +d1_mini_clone.menu.baud.460800.upload.speed=460800 +d1_mini_clone.menu.baud.512000.windows=512000 +d1_mini_clone.menu.baud.512000.upload.speed=512000 +d1_mini_clone.menu.baud.3000000=3000000 +d1_mini_clone.menu.baud.3000000.upload.speed=3000000 +d1_mini_clone.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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 @@ -4024,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 @@ -4050,10 +4753,6 @@ d1_mini_lite.menu.ssl.all=All SSL ciphers (most compatible) d1_mini_lite.menu.ssl.all.build.sslflags= d1_mini_lite.menu.ssl.basic=Basic SSL ciphers (lower ROM use) d1_mini_lite.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -d1_mini_lite.menu.waveform.pwm=Locked PWM -d1_mini_lite.menu.waveform.pwm.build.waveform= -d1_mini_lite.menu.waveform.phase=Locked Phase -d1_mini_lite.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE d1_mini_lite.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) d1_mini_lite.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 d1_mini_lite.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -4062,6 +4761,10 @@ d1_mini_lite.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) d1_mini_lite.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP d1_mini_lite.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) d1_mini_lite.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +d1_mini_lite.menu.mmu.ext128k=128K Heap External 23LC1024 +d1_mini_lite.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +d1_mini_lite.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +d1_mini_lite.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 d1_mini_lite.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM d1_mini_lite.menu.non32xfer.fast.build.non32xferflags= d1_mini_lite.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -4072,80 +4775,64 @@ d1_mini_lite.build.flash_flags=-DFLASHMODE_DOUT d1_mini_lite.build.flash_freq=40 d1_mini_lite.menu.eesz.1M64=1MB (FS:64KB OTA:~470KB) d1_mini_lite.menu.eesz.1M64.build.flash_size=1M -d1_mini_lite.menu.eesz.1M64.build.flash_size_bytes=0x100000 d1_mini_lite.menu.eesz.1M64.build.flash_ld=eagle.flash.1m64.ld d1_mini_lite.menu.eesz.1M64.build.spiffs_pagesize=256 -d1_mini_lite.menu.eesz.1M64.upload.maximum_size=958448 d1_mini_lite.menu.eesz.1M64.build.rfcal_addr=0xFC000 d1_mini_lite.menu.eesz.1M64.build.spiffs_start=0xEB000 d1_mini_lite.menu.eesz.1M64.build.spiffs_end=0xFB000 d1_mini_lite.menu.eesz.1M64.build.spiffs_blocksize=4096 d1_mini_lite.menu.eesz.1M128=1MB (FS:128KB OTA:~438KB) d1_mini_lite.menu.eesz.1M128.build.flash_size=1M -d1_mini_lite.menu.eesz.1M128.build.flash_size_bytes=0x100000 d1_mini_lite.menu.eesz.1M128.build.flash_ld=eagle.flash.1m128.ld d1_mini_lite.menu.eesz.1M128.build.spiffs_pagesize=256 -d1_mini_lite.menu.eesz.1M128.upload.maximum_size=892912 d1_mini_lite.menu.eesz.1M128.build.rfcal_addr=0xFC000 d1_mini_lite.menu.eesz.1M128.build.spiffs_start=0xDB000 d1_mini_lite.menu.eesz.1M128.build.spiffs_end=0xFB000 d1_mini_lite.menu.eesz.1M128.build.spiffs_blocksize=4096 d1_mini_lite.menu.eesz.1M144=1MB (FS:144KB OTA:~430KB) d1_mini_lite.menu.eesz.1M144.build.flash_size=1M -d1_mini_lite.menu.eesz.1M144.build.flash_size_bytes=0x100000 d1_mini_lite.menu.eesz.1M144.build.flash_ld=eagle.flash.1m144.ld d1_mini_lite.menu.eesz.1M144.build.spiffs_pagesize=256 -d1_mini_lite.menu.eesz.1M144.upload.maximum_size=876528 d1_mini_lite.menu.eesz.1M144.build.rfcal_addr=0xFC000 d1_mini_lite.menu.eesz.1M144.build.spiffs_start=0xD7000 d1_mini_lite.menu.eesz.1M144.build.spiffs_end=0xFB000 d1_mini_lite.menu.eesz.1M144.build.spiffs_blocksize=4096 d1_mini_lite.menu.eesz.1M160=1MB (FS:160KB OTA:~422KB) d1_mini_lite.menu.eesz.1M160.build.flash_size=1M -d1_mini_lite.menu.eesz.1M160.build.flash_size_bytes=0x100000 d1_mini_lite.menu.eesz.1M160.build.flash_ld=eagle.flash.1m160.ld d1_mini_lite.menu.eesz.1M160.build.spiffs_pagesize=256 -d1_mini_lite.menu.eesz.1M160.upload.maximum_size=860144 d1_mini_lite.menu.eesz.1M160.build.rfcal_addr=0xFC000 d1_mini_lite.menu.eesz.1M160.build.spiffs_start=0xD3000 d1_mini_lite.menu.eesz.1M160.build.spiffs_end=0xFB000 d1_mini_lite.menu.eesz.1M160.build.spiffs_blocksize=4096 d1_mini_lite.menu.eesz.1M192=1MB (FS:192KB OTA:~406KB) d1_mini_lite.menu.eesz.1M192.build.flash_size=1M -d1_mini_lite.menu.eesz.1M192.build.flash_size_bytes=0x100000 d1_mini_lite.menu.eesz.1M192.build.flash_ld=eagle.flash.1m192.ld d1_mini_lite.menu.eesz.1M192.build.spiffs_pagesize=256 -d1_mini_lite.menu.eesz.1M192.upload.maximum_size=827376 d1_mini_lite.menu.eesz.1M192.build.rfcal_addr=0xFC000 d1_mini_lite.menu.eesz.1M192.build.spiffs_start=0xCB000 d1_mini_lite.menu.eesz.1M192.build.spiffs_end=0xFB000 d1_mini_lite.menu.eesz.1M192.build.spiffs_blocksize=4096 d1_mini_lite.menu.eesz.1M256=1MB (FS:256KB OTA:~374KB) d1_mini_lite.menu.eesz.1M256.build.flash_size=1M -d1_mini_lite.menu.eesz.1M256.build.flash_size_bytes=0x100000 d1_mini_lite.menu.eesz.1M256.build.flash_ld=eagle.flash.1m256.ld d1_mini_lite.menu.eesz.1M256.build.spiffs_pagesize=256 -d1_mini_lite.menu.eesz.1M256.upload.maximum_size=761840 d1_mini_lite.menu.eesz.1M256.build.rfcal_addr=0xFC000 d1_mini_lite.menu.eesz.1M256.build.spiffs_start=0xBB000 d1_mini_lite.menu.eesz.1M256.build.spiffs_end=0xFB000 d1_mini_lite.menu.eesz.1M256.build.spiffs_blocksize=4096 d1_mini_lite.menu.eesz.1M512=1MB (FS:512KB OTA:~246KB) d1_mini_lite.menu.eesz.1M512.build.flash_size=1M -d1_mini_lite.menu.eesz.1M512.build.flash_size_bytes=0x100000 d1_mini_lite.menu.eesz.1M512.build.flash_ld=eagle.flash.1m512.ld d1_mini_lite.menu.eesz.1M512.build.spiffs_pagesize=256 -d1_mini_lite.menu.eesz.1M512.upload.maximum_size=499696 d1_mini_lite.menu.eesz.1M512.build.rfcal_addr=0xFC000 d1_mini_lite.menu.eesz.1M512.build.spiffs_start=0x7B000 d1_mini_lite.menu.eesz.1M512.build.spiffs_end=0xFB000 d1_mini_lite.menu.eesz.1M512.build.spiffs_blocksize=8192 d1_mini_lite.menu.eesz.1M=1MB (FS:none OTA:~502KB) d1_mini_lite.menu.eesz.1M.build.flash_size=1M -d1_mini_lite.menu.eesz.1M.build.flash_size_bytes=0x100000 d1_mini_lite.menu.eesz.1M.build.flash_ld=eagle.flash.1m.ld d1_mini_lite.menu.eesz.1M.build.spiffs_pagesize=256 -d1_mini_lite.menu.eesz.1M.upload.maximum_size=1023984 d1_mini_lite.menu.eesz.1M.build.rfcal_addr=0xFC000 d1_mini_lite.menu.ip.lm2f=v2 Lower Memory d1_mini_lite.menu.ip.lm2f.build.lwip_include=lwip2/include @@ -4179,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 @@ -4223,10 +4916,22 @@ d1_mini_lite.menu.lvl.OOM=OOM d1_mini_lite.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM d1_mini_lite.menu.lvl.MDNS=MDNS d1_mini_lite.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +d1_mini_lite.menu.lvl.HWDT=HWDT +d1_mini_lite.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +d1_mini_lite.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +d1_mini_lite.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K d1_mini_lite.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS d1_mini_lite.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_mini_lite.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +d1_mini_lite.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_mini_lite.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +d1_mini_lite.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_mini_lite.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS d1_mini_lite.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_mini_lite.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +d1_mini_lite.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_mini_lite.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_mini_lite.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_mini_lite.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG d1_mini_lite.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG d1_mini_lite.menu.wipe.none=Only Sketch @@ -4253,6 +4958,15 @@ d1_mini_lite.menu.baud.512000.windows=512000 d1_mini_lite.menu.baud.512000.upload.speed=512000 d1_mini_lite.menu.baud.3000000=3000000 d1_mini_lite.menu.baud.3000000.upload.speed=3000000 +d1_mini_lite.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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 @@ -4267,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 @@ -4293,10 +5008,6 @@ d1_mini_pro.menu.ssl.all=All SSL ciphers (most compatible) d1_mini_pro.menu.ssl.all.build.sslflags= d1_mini_pro.menu.ssl.basic=Basic SSL ciphers (lower ROM use) d1_mini_pro.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -d1_mini_pro.menu.waveform.pwm=Locked PWM -d1_mini_pro.menu.waveform.pwm.build.waveform= -d1_mini_pro.menu.waveform.phase=Locked Phase -d1_mini_pro.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE d1_mini_pro.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) d1_mini_pro.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 d1_mini_pro.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -4305,6 +5016,10 @@ d1_mini_pro.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) d1_mini_pro.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP d1_mini_pro.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) d1_mini_pro.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +d1_mini_pro.menu.mmu.ext128k=128K Heap External 23LC1024 +d1_mini_pro.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +d1_mini_pro.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +d1_mini_pro.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 d1_mini_pro.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM d1_mini_pro.menu.non32xfer.fast.build.non32xferflags= d1_mini_pro.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -4315,24 +5030,25 @@ d1_mini_pro.build.flash_flags=-DFLASHMODE_DIO d1_mini_pro.build.flash_freq=40 d1_mini_pro.menu.eesz.16M14M=16MB (FS:14MB OTA:~1019KB) d1_mini_pro.menu.eesz.16M14M.build.flash_size=16M -d1_mini_pro.menu.eesz.16M14M.build.flash_size_bytes=0x1000000 d1_mini_pro.menu.eesz.16M14M.build.flash_ld=eagle.flash.16m14m.ld d1_mini_pro.menu.eesz.16M14M.build.spiffs_pagesize=256 -d1_mini_pro.menu.eesz.16M14M.upload.maximum_size=1044464 d1_mini_pro.menu.eesz.16M14M.build.rfcal_addr=0xFFC000 d1_mini_pro.menu.eesz.16M14M.build.spiffs_start=0x200000 d1_mini_pro.menu.eesz.16M14M.build.spiffs_end=0xFFA000 d1_mini_pro.menu.eesz.16M14M.build.spiffs_blocksize=8192 d1_mini_pro.menu.eesz.16M15M=16MB (FS:15MB OTA:~512KB) d1_mini_pro.menu.eesz.16M15M.build.flash_size=16M -d1_mini_pro.menu.eesz.16M15M.build.flash_size_bytes=0x1000000 d1_mini_pro.menu.eesz.16M15M.build.flash_ld=eagle.flash.16m15m.ld d1_mini_pro.menu.eesz.16M15M.build.spiffs_pagesize=256 -d1_mini_pro.menu.eesz.16M15M.upload.maximum_size=1044464 d1_mini_pro.menu.eesz.16M15M.build.rfcal_addr=0xFFC000 d1_mini_pro.menu.eesz.16M15M.build.spiffs_start=0x100000 d1_mini_pro.menu.eesz.16M15M.build.spiffs_end=0xFFA000 d1_mini_pro.menu.eesz.16M15M.build.spiffs_blocksize=8192 +d1_mini_pro.menu.eesz.16M=16MB (FS:none OTA:~1019KB) +d1_mini_pro.menu.eesz.16M.build.flash_size=16M +d1_mini_pro.menu.eesz.16M.build.flash_ld=eagle.flash.16m.ld +d1_mini_pro.menu.eesz.16M.build.spiffs_pagesize=256 +d1_mini_pro.menu.eesz.16M.build.rfcal_addr=0xFFC000 d1_mini_pro.menu.ip.lm2f=v2 Lower Memory d1_mini_pro.menu.ip.lm2f.build.lwip_include=lwip2/include d1_mini_pro.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat @@ -4365,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 @@ -4409,10 +5131,22 @@ d1_mini_pro.menu.lvl.OOM=OOM d1_mini_pro.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM d1_mini_pro.menu.lvl.MDNS=MDNS d1_mini_pro.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +d1_mini_pro.menu.lvl.HWDT=HWDT +d1_mini_pro.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +d1_mini_pro.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +d1_mini_pro.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K d1_mini_pro.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS d1_mini_pro.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_mini_pro.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +d1_mini_pro.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_mini_pro.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +d1_mini_pro.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_mini_pro.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS d1_mini_pro.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_mini_pro.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +d1_mini_pro.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_mini_pro.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_mini_pro.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_mini_pro.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG d1_mini_pro.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG d1_mini_pro.menu.wipe.none=Only Sketch @@ -4439,6 +5173,15 @@ d1_mini_pro.menu.baud.512000.windows=512000 d1_mini_pro.menu.baud.512000.upload.speed=512000 d1_mini_pro.menu.baud.3000000=3000000 d1_mini_pro.menu.baud.3000000.upload.speed=3000000 +d1_mini_pro.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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 @@ -4453,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 @@ -4479,10 +5223,6 @@ d1.menu.ssl.all=All SSL ciphers (most compatible) d1.menu.ssl.all.build.sslflags= d1.menu.ssl.basic=Basic SSL ciphers (lower ROM use) d1.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -d1.menu.waveform.pwm=Locked PWM -d1.menu.waveform.pwm.build.waveform= -d1.menu.waveform.phase=Locked Phase -d1.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE d1.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) d1.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 d1.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -4491,6 +5231,10 @@ d1.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) d1.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP d1.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) d1.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +d1.menu.mmu.ext128k=128K Heap External 23LC1024 +d1.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +d1.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +d1.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 d1.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM d1.menu.non32xfer.fast.build.non32xferflags= d1.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -4501,40 +5245,32 @@ d1.build.flash_flags=-DFLASHMODE_DIO d1.build.flash_freq=40 d1.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) d1.menu.eesz.4M2M.build.flash_size=4M -d1.menu.eesz.4M2M.build.flash_size_bytes=0x400000 d1.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld d1.menu.eesz.4M2M.build.spiffs_pagesize=256 -d1.menu.eesz.4M2M.upload.maximum_size=1044464 d1.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 d1.menu.eesz.4M2M.build.spiffs_start=0x200000 d1.menu.eesz.4M2M.build.spiffs_end=0x3FA000 d1.menu.eesz.4M2M.build.spiffs_blocksize=8192 d1.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) d1.menu.eesz.4M3M.build.flash_size=4M -d1.menu.eesz.4M3M.build.flash_size_bytes=0x400000 d1.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld d1.menu.eesz.4M3M.build.spiffs_pagesize=256 -d1.menu.eesz.4M3M.upload.maximum_size=1044464 d1.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 d1.menu.eesz.4M3M.build.spiffs_start=0x100000 d1.menu.eesz.4M3M.build.spiffs_end=0x3FA000 d1.menu.eesz.4M3M.build.spiffs_blocksize=8192 d1.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) d1.menu.eesz.4M1M.build.flash_size=4M -d1.menu.eesz.4M1M.build.flash_size_bytes=0x400000 d1.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld d1.menu.eesz.4M1M.build.spiffs_pagesize=256 -d1.menu.eesz.4M1M.upload.maximum_size=1044464 d1.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 d1.menu.eesz.4M1M.build.spiffs_start=0x300000 d1.menu.eesz.4M1M.build.spiffs_end=0x3FA000 d1.menu.eesz.4M1M.build.spiffs_blocksize=8192 d1.menu.eesz.4M=4MB (FS:none OTA:~1019KB) d1.menu.eesz.4M.build.flash_size=4M -d1.menu.eesz.4M.build.flash_size_bytes=0x400000 d1.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld d1.menu.eesz.4M.build.spiffs_pagesize=256 -d1.menu.eesz.4M.upload.maximum_size=1044464 d1.menu.eesz.4M.build.rfcal_addr=0x3FC000 d1.menu.ip.lm2f=v2 Lower Memory d1.menu.ip.lm2f.build.lwip_include=lwip2/include @@ -4568,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 @@ -4612,10 +5354,22 @@ d1.menu.lvl.OOM=OOM d1.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM d1.menu.lvl.MDNS=MDNS d1.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +d1.menu.lvl.HWDT=HWDT +d1.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +d1.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +d1.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K d1.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS d1.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.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +d1.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.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +d1.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.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS d1.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.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +d1.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.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.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.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG d1.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG d1.menu.wipe.none=Only Sketch @@ -4642,10 +5396,481 @@ d1.menu.baud.512000.windows=512000 d1.menu.baud.512000.upload.speed=512000 d1.menu.baud.3000000=3000000 d1.menu.baud.3000000.upload.speed=3000000 +d1.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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 +agruminolemon.build.board=ESP8266_AGRUMINO_LEMON_V4 +agruminolemon.build.variant=agruminolemonv4 +agruminolemon.upload.tool=esptool +agruminolemon.upload.maximum_data_size=81920 +agruminolemon.upload.wait_for_upload_port=true +agruminolemon.upload.erase_cmd= +agruminolemon.serial.disableDTR=true +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 +agruminolemon.menu.xtal.80.build.f_cpu=80000000L +agruminolemon.menu.xtal.160=160 MHz +agruminolemon.menu.xtal.160.build.f_cpu=160000000L +agruminolemon.menu.vt.flash=Flash +agruminolemon.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +agruminolemon.menu.vt.heap=Heap +agruminolemon.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +agruminolemon.menu.vt.iram=IRAM +agruminolemon.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +agruminolemon.menu.exception.disabled=Disabled (new aborts on oom) +agruminolemon.menu.exception.disabled.build.exception_flags=-fno-exceptions +agruminolemon.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +agruminolemon.menu.exception.enabled=Enabled +agruminolemon.menu.exception.enabled.build.exception_flags=-fexceptions +agruminolemon.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +agruminolemon.menu.stacksmash.disabled=Disabled +agruminolemon.menu.stacksmash.disabled.build.stacksmash_flags= +agruminolemon.menu.stacksmash.enabled=Enabled +agruminolemon.menu.stacksmash.enabled.build.stacksmash_flags=-fstack-protector +agruminolemon.menu.ssl.all=All SSL ciphers (most compatible) +agruminolemon.menu.ssl.all.build.sslflags= +agruminolemon.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +agruminolemon.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +agruminolemon.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +agruminolemon.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +agruminolemon.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +agruminolemon.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +agruminolemon.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +agruminolemon.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +agruminolemon.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +agruminolemon.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +agruminolemon.menu.mmu.ext128k=128K Heap External 23LC1024 +agruminolemon.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +agruminolemon.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +agruminolemon.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +agruminolemon.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +agruminolemon.menu.non32xfer.fast.build.non32xferflags= +agruminolemon.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +agruminolemon.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER +agruminolemon.upload.resetmethod=--before default_reset --after hard_reset +agruminolemon.build.flash_mode=dio +agruminolemon.build.flash_flags=-DFLASHMODE_DIO +agruminolemon.build.flash_freq=40 +agruminolemon.menu.eesz.2M64=2MB (FS:64KB OTA:~992KB) +agruminolemon.menu.eesz.2M64.build.flash_size=2M +agruminolemon.menu.eesz.2M64.build.flash_ld=eagle.flash.2m64.ld +agruminolemon.menu.eesz.2M64.build.spiffs_pagesize=256 +agruminolemon.menu.eesz.2M64.build.rfcal_addr=0x1FC000 +agruminolemon.menu.eesz.2M64.build.spiffs_start=0x1F0000 +agruminolemon.menu.eesz.2M64.build.spiffs_end=0x1FB000 +agruminolemon.menu.eesz.2M64.build.spiffs_blocksize=4096 +agruminolemon.menu.eesz.2M128=2MB (FS:128KB OTA:~960KB) +agruminolemon.menu.eesz.2M128.build.flash_size=2M +agruminolemon.menu.eesz.2M128.build.flash_ld=eagle.flash.2m128.ld +agruminolemon.menu.eesz.2M128.build.spiffs_pagesize=256 +agruminolemon.menu.eesz.2M128.build.rfcal_addr=0x1FC000 +agruminolemon.menu.eesz.2M128.build.spiffs_start=0x1E0000 +agruminolemon.menu.eesz.2M128.build.spiffs_end=0x1FB000 +agruminolemon.menu.eesz.2M128.build.spiffs_blocksize=4096 +agruminolemon.menu.eesz.2M256=2MB (FS:256KB OTA:~896KB) +agruminolemon.menu.eesz.2M256.build.flash_size=2M +agruminolemon.menu.eesz.2M256.build.flash_ld=eagle.flash.2m256.ld +agruminolemon.menu.eesz.2M256.build.spiffs_pagesize=256 +agruminolemon.menu.eesz.2M256.build.rfcal_addr=0x1FC000 +agruminolemon.menu.eesz.2M256.build.spiffs_start=0x1C0000 +agruminolemon.menu.eesz.2M256.build.spiffs_end=0x1FB000 +agruminolemon.menu.eesz.2M256.build.spiffs_blocksize=4096 +agruminolemon.menu.eesz.2M512=2MB (FS:512KB OTA:~768KB) +agruminolemon.menu.eesz.2M512.build.flash_size=2M +agruminolemon.menu.eesz.2M512.build.flash_ld=eagle.flash.2m512.ld +agruminolemon.menu.eesz.2M512.build.spiffs_pagesize=256 +agruminolemon.menu.eesz.2M512.build.rfcal_addr=0x1FC000 +agruminolemon.menu.eesz.2M512.build.spiffs_start=0x180000 +agruminolemon.menu.eesz.2M512.build.spiffs_end=0x1FA000 +agruminolemon.menu.eesz.2M512.build.spiffs_blocksize=8192 +agruminolemon.menu.eesz.2M1M=2MB (FS:1MB OTA:~512KB) +agruminolemon.menu.eesz.2M1M.build.flash_size=2M +agruminolemon.menu.eesz.2M1M.build.flash_ld=eagle.flash.2m1m.ld +agruminolemon.menu.eesz.2M1M.build.spiffs_pagesize=256 +agruminolemon.menu.eesz.2M1M.build.rfcal_addr=0x1FC000 +agruminolemon.menu.eesz.2M1M.build.spiffs_start=0x100000 +agruminolemon.menu.eesz.2M1M.build.spiffs_end=0x1FA000 +agruminolemon.menu.eesz.2M1M.build.spiffs_blocksize=8192 +agruminolemon.menu.eesz.2M=2MB (FS:none OTA:~1019KB) +agruminolemon.menu.eesz.2M.build.flash_size=2M +agruminolemon.menu.eesz.2M.build.flash_ld=eagle.flash.2m.ld +agruminolemon.menu.eesz.2M.build.spiffs_pagesize=256 +agruminolemon.menu.eesz.2M.build.rfcal_addr=0x1FC000 +agruminolemon.menu.ip.lm2f=v2 Lower Memory +agruminolemon.menu.ip.lm2f.build.lwip_include=lwip2/include +agruminolemon.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +agruminolemon.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +agruminolemon.menu.ip.hb2f=v2 Higher Bandwidth +agruminolemon.menu.ip.hb2f.build.lwip_include=lwip2/include +agruminolemon.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +agruminolemon.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +agruminolemon.menu.ip.lm2n=v2 Lower Memory (no features) +agruminolemon.menu.ip.lm2n.build.lwip_include=lwip2/include +agruminolemon.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +agruminolemon.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +agruminolemon.menu.ip.hb2n=v2 Higher Bandwidth (no features) +agruminolemon.menu.ip.hb2n.build.lwip_include=lwip2/include +agruminolemon.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +agruminolemon.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +agruminolemon.menu.ip.lm6f=v2 IPv6 Lower Memory +agruminolemon.menu.ip.lm6f.build.lwip_include=lwip2/include +agruminolemon.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +agruminolemon.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +agruminolemon.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +agruminolemon.menu.ip.hb6f.build.lwip_include=lwip2/include +agruminolemon.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +agruminolemon.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +agruminolemon.menu.dbg.Disabled=Disabled +agruminolemon.menu.dbg.Disabled.build.debug_port= +agruminolemon.menu.dbg.Serial=Serial +agruminolemon.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +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 +agruminolemon.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +agruminolemon.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +agruminolemon.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +agruminolemon.menu.lvl.HTTP_SERVER=HTTP_SERVER +agruminolemon.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +agruminolemon.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +agruminolemon.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +agruminolemon.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +agruminolemon.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +agruminolemon.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +agruminolemon.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +agruminolemon.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +agruminolemon.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +agruminolemon.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +agruminolemon.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +agruminolemon.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +agruminolemon.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +agruminolemon.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +agruminolemon.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +agruminolemon.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +agruminolemon.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +agruminolemon.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +agruminolemon.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +agruminolemon.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +agruminolemon.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +agruminolemon.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +agruminolemon.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +agruminolemon.menu.lvl.CORE=CORE +agruminolemon.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +agruminolemon.menu.lvl.WIFI=WIFI +agruminolemon.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +agruminolemon.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +agruminolemon.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +agruminolemon.menu.lvl.UPDATER=UPDATER +agruminolemon.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +agruminolemon.menu.lvl.OTA=OTA +agruminolemon.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +agruminolemon.menu.lvl.OOM=OOM +agruminolemon.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +agruminolemon.menu.lvl.MDNS=MDNS +agruminolemon.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +agruminolemon.menu.lvl.HWDT=HWDT +agruminolemon.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +agruminolemon.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +agruminolemon.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K +agruminolemon.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +agruminolemon.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 +agruminolemon.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +agruminolemon.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 +agruminolemon.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +agruminolemon.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 +agruminolemon.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +agruminolemon.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 +agruminolemon.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +agruminolemon.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 +agruminolemon.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 +agruminolemon.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 +agruminolemon.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +agruminolemon.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +agruminolemon.menu.wipe.none=Only Sketch +agruminolemon.menu.wipe.none.upload.erase_cmd= +agruminolemon.menu.wipe.sdk=Sketch + WiFi Settings +agruminolemon.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +agruminolemon.menu.wipe.all=All Flash Contents +agruminolemon.menu.wipe.all.upload.erase_cmd=erase_flash +agruminolemon.menu.baud.115200=115200 +agruminolemon.menu.baud.115200.upload.speed=115200 +agruminolemon.menu.baud.57600=57600 +agruminolemon.menu.baud.57600.upload.speed=57600 +agruminolemon.menu.baud.230400.linux=230400 +agruminolemon.menu.baud.230400.macosx=230400 +agruminolemon.menu.baud.230400.upload.speed=230400 +agruminolemon.menu.baud.256000.windows=256000 +agruminolemon.menu.baud.256000.upload.speed=256000 +agruminolemon.menu.baud.460800.linux=460800 +agruminolemon.menu.baud.460800.macosx=460800 +agruminolemon.menu.baud.460800.upload.speed=460800 +agruminolemon.menu.baud.512000.windows=512000 +agruminolemon.menu.baud.512000.upload.speed=512000 +agruminolemon.menu.baud.921600=921600 +agruminolemon.menu.baud.921600.upload.speed=921600 +agruminolemon.menu.baud.3000000=3000000 +agruminolemon.menu.baud.3000000.upload.speed=3000000 +agruminolemon.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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) -nodemcu.build.board=ESP8266_NODEMCU +nodemcu.build.board=ESP8266_NODEMCU_ESP12 nodemcu.build.variant=nodemcu nodemcu.upload.tool=esptool nodemcu.upload.maximum_data_size=81920 @@ -4656,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 @@ -4682,10 +5908,6 @@ nodemcu.menu.ssl.all=All SSL ciphers (most compatible) nodemcu.menu.ssl.all.build.sslflags= nodemcu.menu.ssl.basic=Basic SSL ciphers (lower ROM use) nodemcu.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -nodemcu.menu.waveform.pwm=Locked PWM -nodemcu.menu.waveform.pwm.build.waveform= -nodemcu.menu.waveform.phase=Locked Phase -nodemcu.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE nodemcu.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) nodemcu.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 nodemcu.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -4694,6 +5916,10 @@ nodemcu.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) nodemcu.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP nodemcu.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) nodemcu.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +nodemcu.menu.mmu.ext128k=128K Heap External 23LC1024 +nodemcu.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +nodemcu.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +nodemcu.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 nodemcu.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM nodemcu.menu.non32xfer.fast.build.non32xferflags= nodemcu.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -4704,40 +5930,32 @@ nodemcu.build.flash_flags=-DFLASHMODE_QIO nodemcu.build.flash_freq=40 nodemcu.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) nodemcu.menu.eesz.4M2M.build.flash_size=4M -nodemcu.menu.eesz.4M2M.build.flash_size_bytes=0x400000 nodemcu.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld nodemcu.menu.eesz.4M2M.build.spiffs_pagesize=256 -nodemcu.menu.eesz.4M2M.upload.maximum_size=1044464 nodemcu.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 nodemcu.menu.eesz.4M2M.build.spiffs_start=0x200000 nodemcu.menu.eesz.4M2M.build.spiffs_end=0x3FA000 nodemcu.menu.eesz.4M2M.build.spiffs_blocksize=8192 nodemcu.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) nodemcu.menu.eesz.4M3M.build.flash_size=4M -nodemcu.menu.eesz.4M3M.build.flash_size_bytes=0x400000 nodemcu.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld nodemcu.menu.eesz.4M3M.build.spiffs_pagesize=256 -nodemcu.menu.eesz.4M3M.upload.maximum_size=1044464 nodemcu.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 nodemcu.menu.eesz.4M3M.build.spiffs_start=0x100000 nodemcu.menu.eesz.4M3M.build.spiffs_end=0x3FA000 nodemcu.menu.eesz.4M3M.build.spiffs_blocksize=8192 nodemcu.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) nodemcu.menu.eesz.4M1M.build.flash_size=4M -nodemcu.menu.eesz.4M1M.build.flash_size_bytes=0x400000 nodemcu.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld nodemcu.menu.eesz.4M1M.build.spiffs_pagesize=256 -nodemcu.menu.eesz.4M1M.upload.maximum_size=1044464 nodemcu.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 nodemcu.menu.eesz.4M1M.build.spiffs_start=0x300000 nodemcu.menu.eesz.4M1M.build.spiffs_end=0x3FA000 nodemcu.menu.eesz.4M1M.build.spiffs_blocksize=8192 nodemcu.menu.eesz.4M=4MB (FS:none OTA:~1019KB) nodemcu.menu.eesz.4M.build.flash_size=4M -nodemcu.menu.eesz.4M.build.flash_size_bytes=0x400000 nodemcu.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld nodemcu.menu.eesz.4M.build.spiffs_pagesize=256 -nodemcu.menu.eesz.4M.upload.maximum_size=1044464 nodemcu.menu.eesz.4M.build.rfcal_addr=0x3FC000 nodemcu.menu.ip.lm2f=v2 Lower Memory nodemcu.menu.ip.lm2f.build.lwip_include=lwip2/include @@ -4771,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 @@ -4815,10 +6039,22 @@ nodemcu.menu.lvl.OOM=OOM nodemcu.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM nodemcu.menu.lvl.MDNS=MDNS nodemcu.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +nodemcu.menu.lvl.HWDT=HWDT +nodemcu.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +nodemcu.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +nodemcu.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K nodemcu.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS nodemcu.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 +nodemcu.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +nodemcu.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 +nodemcu.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +nodemcu.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 nodemcu.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS nodemcu.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 +nodemcu.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +nodemcu.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 +nodemcu.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 +nodemcu.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 nodemcu.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG nodemcu.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG nodemcu.menu.wipe.none=Only Sketch @@ -4845,10 +6081,19 @@ nodemcu.menu.baud.921600=921600 nodemcu.menu.baud.921600.upload.speed=921600 nodemcu.menu.baud.3000000=3000000 nodemcu.menu.baud.3000000.upload.speed=3000000 +nodemcu.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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) -nodemcuv2.build.board=ESP8266_NODEMCU +nodemcuv2.build.board=ESP8266_NODEMCU_ESP12E nodemcuv2.build.variant=nodemcu nodemcuv2.upload.tool=esptool nodemcuv2.upload.maximum_data_size=81920 @@ -4859,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 @@ -4885,10 +6131,6 @@ nodemcuv2.menu.ssl.all=All SSL ciphers (most compatible) nodemcuv2.menu.ssl.all.build.sslflags= nodemcuv2.menu.ssl.basic=Basic SSL ciphers (lower ROM use) nodemcuv2.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -nodemcuv2.menu.waveform.pwm=Locked PWM -nodemcuv2.menu.waveform.pwm.build.waveform= -nodemcuv2.menu.waveform.phase=Locked Phase -nodemcuv2.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE nodemcuv2.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) nodemcuv2.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 nodemcuv2.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -4897,6 +6139,10 @@ nodemcuv2.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) nodemcuv2.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP nodemcuv2.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) nodemcuv2.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +nodemcuv2.menu.mmu.ext128k=128K Heap External 23LC1024 +nodemcuv2.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +nodemcuv2.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +nodemcuv2.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 nodemcuv2.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM nodemcuv2.menu.non32xfer.fast.build.non32xferflags= nodemcuv2.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -4907,40 +6153,32 @@ nodemcuv2.build.flash_flags=-DFLASHMODE_DIO nodemcuv2.build.flash_freq=40 nodemcuv2.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) nodemcuv2.menu.eesz.4M2M.build.flash_size=4M -nodemcuv2.menu.eesz.4M2M.build.flash_size_bytes=0x400000 nodemcuv2.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld nodemcuv2.menu.eesz.4M2M.build.spiffs_pagesize=256 -nodemcuv2.menu.eesz.4M2M.upload.maximum_size=1044464 nodemcuv2.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 nodemcuv2.menu.eesz.4M2M.build.spiffs_start=0x200000 nodemcuv2.menu.eesz.4M2M.build.spiffs_end=0x3FA000 nodemcuv2.menu.eesz.4M2M.build.spiffs_blocksize=8192 nodemcuv2.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) nodemcuv2.menu.eesz.4M3M.build.flash_size=4M -nodemcuv2.menu.eesz.4M3M.build.flash_size_bytes=0x400000 nodemcuv2.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld nodemcuv2.menu.eesz.4M3M.build.spiffs_pagesize=256 -nodemcuv2.menu.eesz.4M3M.upload.maximum_size=1044464 nodemcuv2.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 nodemcuv2.menu.eesz.4M3M.build.spiffs_start=0x100000 nodemcuv2.menu.eesz.4M3M.build.spiffs_end=0x3FA000 nodemcuv2.menu.eesz.4M3M.build.spiffs_blocksize=8192 nodemcuv2.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) nodemcuv2.menu.eesz.4M1M.build.flash_size=4M -nodemcuv2.menu.eesz.4M1M.build.flash_size_bytes=0x400000 nodemcuv2.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld nodemcuv2.menu.eesz.4M1M.build.spiffs_pagesize=256 -nodemcuv2.menu.eesz.4M1M.upload.maximum_size=1044464 nodemcuv2.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 nodemcuv2.menu.eesz.4M1M.build.spiffs_start=0x300000 nodemcuv2.menu.eesz.4M1M.build.spiffs_end=0x3FA000 nodemcuv2.menu.eesz.4M1M.build.spiffs_blocksize=8192 nodemcuv2.menu.eesz.4M=4MB (FS:none OTA:~1019KB) nodemcuv2.menu.eesz.4M.build.flash_size=4M -nodemcuv2.menu.eesz.4M.build.flash_size_bytes=0x400000 nodemcuv2.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld nodemcuv2.menu.eesz.4M.build.spiffs_pagesize=256 -nodemcuv2.menu.eesz.4M.upload.maximum_size=1044464 nodemcuv2.menu.eesz.4M.build.rfcal_addr=0x3FC000 nodemcuv2.menu.led.2=2 nodemcuv2.menu.led.2.build.led=-DLED_BUILTIN=2 @@ -4978,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 @@ -5022,10 +6266,22 @@ nodemcuv2.menu.lvl.OOM=OOM nodemcuv2.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM nodemcuv2.menu.lvl.MDNS=MDNS nodemcuv2.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +nodemcuv2.menu.lvl.HWDT=HWDT +nodemcuv2.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +nodemcuv2.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +nodemcuv2.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K nodemcuv2.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS nodemcuv2.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 +nodemcuv2.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +nodemcuv2.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 +nodemcuv2.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +nodemcuv2.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 nodemcuv2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS nodemcuv2.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 +nodemcuv2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +nodemcuv2.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 +nodemcuv2.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 +nodemcuv2.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 nodemcuv2.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG nodemcuv2.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG nodemcuv2.menu.wipe.none=Only Sketch @@ -5052,6 +6308,15 @@ nodemcuv2.menu.baud.921600=921600 nodemcuv2.menu.baud.921600.upload.speed=921600 nodemcuv2.menu.baud.3000000=3000000 nodemcuv2.menu.baud.3000000.upload.speed=3000000 +nodemcuv2.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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) @@ -5066,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 @@ -5092,10 +6358,6 @@ modwifi.menu.ssl.all=All SSL ciphers (most compatible) modwifi.menu.ssl.all.build.sslflags= modwifi.menu.ssl.basic=Basic SSL ciphers (lower ROM use) modwifi.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -modwifi.menu.waveform.pwm=Locked PWM -modwifi.menu.waveform.pwm.build.waveform= -modwifi.menu.waveform.phase=Locked Phase -modwifi.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE modwifi.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) modwifi.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 modwifi.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -5104,70 +6366,77 @@ modwifi.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) modwifi.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP modwifi.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) modwifi.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +modwifi.menu.mmu.ext128k=128K Heap External 23LC1024 +modwifi.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +modwifi.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +modwifi.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 modwifi.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM modwifi.menu.non32xfer.fast.build.non32xferflags= modwifi.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) modwifi.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER -modwifi.upload.resetmethod=--before no_reset --after soft_reset -modwifi.build.flash_mode=qio -modwifi.build.flash_flags=-DFLASHMODE_QIO +modwifi.menu.ResetMethod.nodemcu=dtr (aka nodemcu) +modwifi.menu.ResetMethod.nodemcu.upload.resetmethod=--before default_reset --after hard_reset +modwifi.menu.ResetMethod.ck=no dtr (aka ck) +modwifi.menu.ResetMethod.ck.upload.resetmethod=--before no_reset --after soft_reset +modwifi.menu.ResetMethod.nodtr_nosync=no dtr, no_sync +modwifi.menu.ResetMethod.nodtr_nosync.upload.resetmethod=--before no_reset_no_sync --after soft_reset +modwifi.menu.FlashMode.dout=DOUT (compatible) +modwifi.menu.FlashMode.dout.build.flash_mode=dout +modwifi.menu.FlashMode.dout.build.flash_flags=-DFLASHMODE_DOUT +modwifi.menu.FlashMode.dio=DIO +modwifi.menu.FlashMode.dio.build.flash_mode=dio +modwifi.menu.FlashMode.dio.build.flash_flags=-DFLASHMODE_DIO +modwifi.menu.FlashMode.qout=QOUT +modwifi.menu.FlashMode.qout.build.flash_mode=qout +modwifi.menu.FlashMode.qout.build.flash_flags=-DFLASHMODE_QOUT +modwifi.menu.FlashMode.qio=QIO (fast) +modwifi.menu.FlashMode.qio.build.flash_mode=qio +modwifi.menu.FlashMode.qio.build.flash_flags=-DFLASHMODE_QIO modwifi.build.flash_freq=40 modwifi.menu.eesz.2M64=2MB (FS:64KB OTA:~992KB) modwifi.menu.eesz.2M64.build.flash_size=2M -modwifi.menu.eesz.2M64.build.flash_size_bytes=0x200000 modwifi.menu.eesz.2M64.build.flash_ld=eagle.flash.2m64.ld modwifi.menu.eesz.2M64.build.spiffs_pagesize=256 -modwifi.menu.eesz.2M64.upload.maximum_size=1044464 modwifi.menu.eesz.2M64.build.rfcal_addr=0x1FC000 modwifi.menu.eesz.2M64.build.spiffs_start=0x1F0000 modwifi.menu.eesz.2M64.build.spiffs_end=0x1FB000 modwifi.menu.eesz.2M64.build.spiffs_blocksize=4096 modwifi.menu.eesz.2M128=2MB (FS:128KB OTA:~960KB) modwifi.menu.eesz.2M128.build.flash_size=2M -modwifi.menu.eesz.2M128.build.flash_size_bytes=0x200000 modwifi.menu.eesz.2M128.build.flash_ld=eagle.flash.2m128.ld modwifi.menu.eesz.2M128.build.spiffs_pagesize=256 -modwifi.menu.eesz.2M128.upload.maximum_size=1044464 modwifi.menu.eesz.2M128.build.rfcal_addr=0x1FC000 modwifi.menu.eesz.2M128.build.spiffs_start=0x1E0000 modwifi.menu.eesz.2M128.build.spiffs_end=0x1FB000 modwifi.menu.eesz.2M128.build.spiffs_blocksize=4096 modwifi.menu.eesz.2M256=2MB (FS:256KB OTA:~896KB) modwifi.menu.eesz.2M256.build.flash_size=2M -modwifi.menu.eesz.2M256.build.flash_size_bytes=0x200000 modwifi.menu.eesz.2M256.build.flash_ld=eagle.flash.2m256.ld modwifi.menu.eesz.2M256.build.spiffs_pagesize=256 -modwifi.menu.eesz.2M256.upload.maximum_size=1044464 modwifi.menu.eesz.2M256.build.rfcal_addr=0x1FC000 modwifi.menu.eesz.2M256.build.spiffs_start=0x1C0000 modwifi.menu.eesz.2M256.build.spiffs_end=0x1FB000 modwifi.menu.eesz.2M256.build.spiffs_blocksize=4096 modwifi.menu.eesz.2M512=2MB (FS:512KB OTA:~768KB) modwifi.menu.eesz.2M512.build.flash_size=2M -modwifi.menu.eesz.2M512.build.flash_size_bytes=0x200000 modwifi.menu.eesz.2M512.build.flash_ld=eagle.flash.2m512.ld modwifi.menu.eesz.2M512.build.spiffs_pagesize=256 -modwifi.menu.eesz.2M512.upload.maximum_size=1044464 modwifi.menu.eesz.2M512.build.rfcal_addr=0x1FC000 modwifi.menu.eesz.2M512.build.spiffs_start=0x180000 modwifi.menu.eesz.2M512.build.spiffs_end=0x1FA000 modwifi.menu.eesz.2M512.build.spiffs_blocksize=8192 modwifi.menu.eesz.2M1M=2MB (FS:1MB OTA:~512KB) modwifi.menu.eesz.2M1M.build.flash_size=2M -modwifi.menu.eesz.2M1M.build.flash_size_bytes=0x200000 modwifi.menu.eesz.2M1M.build.flash_ld=eagle.flash.2m1m.ld modwifi.menu.eesz.2M1M.build.spiffs_pagesize=256 -modwifi.menu.eesz.2M1M.upload.maximum_size=1044464 modwifi.menu.eesz.2M1M.build.rfcal_addr=0x1FC000 modwifi.menu.eesz.2M1M.build.spiffs_start=0x100000 modwifi.menu.eesz.2M1M.build.spiffs_end=0x1FA000 modwifi.menu.eesz.2M1M.build.spiffs_blocksize=8192 modwifi.menu.eesz.2M=2MB (FS:none OTA:~1019KB) modwifi.menu.eesz.2M.build.flash_size=2M -modwifi.menu.eesz.2M.build.flash_size_bytes=0x200000 modwifi.menu.eesz.2M.build.flash_ld=eagle.flash.2m.ld modwifi.menu.eesz.2M.build.spiffs_pagesize=256 -modwifi.menu.eesz.2M.upload.maximum_size=1044464 modwifi.menu.eesz.2M.build.rfcal_addr=0x1FC000 modwifi.menu.ip.lm2f=v2 Lower Memory modwifi.menu.ip.lm2f.build.lwip_include=lwip2/include @@ -5201,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 @@ -5245,10 +6520,22 @@ modwifi.menu.lvl.OOM=OOM modwifi.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM modwifi.menu.lvl.MDNS=MDNS modwifi.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +modwifi.menu.lvl.HWDT=HWDT +modwifi.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +modwifi.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +modwifi.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K modwifi.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS modwifi.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 +modwifi.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +modwifi.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 +modwifi.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +modwifi.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 modwifi.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS modwifi.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 +modwifi.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +modwifi.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 +modwifi.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 +modwifi.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 modwifi.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG modwifi.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG modwifi.menu.wipe.none=Only Sketch @@ -5275,6 +6562,15 @@ modwifi.menu.baud.921600=921600 modwifi.menu.baud.921600.upload.speed=921600 modwifi.menu.baud.3000000=3000000 modwifi.menu.baud.3000000.upload.speed=3000000 +modwifi.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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 @@ -5289,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 @@ -5315,10 +6612,6 @@ phoenix_v1.menu.ssl.all=All SSL ciphers (most compatible) phoenix_v1.menu.ssl.all.build.sslflags= phoenix_v1.menu.ssl.basic=Basic SSL ciphers (lower ROM use) phoenix_v1.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -phoenix_v1.menu.waveform.pwm=Locked PWM -phoenix_v1.menu.waveform.pwm.build.waveform= -phoenix_v1.menu.waveform.phase=Locked Phase -phoenix_v1.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE phoenix_v1.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) phoenix_v1.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 phoenix_v1.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -5327,6 +6620,10 @@ phoenix_v1.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) phoenix_v1.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP phoenix_v1.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) phoenix_v1.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +phoenix_v1.menu.mmu.ext128k=128K Heap External 23LC1024 +phoenix_v1.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +phoenix_v1.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +phoenix_v1.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 phoenix_v1.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM phoenix_v1.menu.non32xfer.fast.build.non32xferflags= phoenix_v1.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -5336,40 +6633,32 @@ phoenix_v1.build.flash_flags=-DFLASHMODE_DIO phoenix_v1.build.flash_freq=40 phoenix_v1.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) phoenix_v1.menu.eesz.4M2M.build.flash_size=4M -phoenix_v1.menu.eesz.4M2M.build.flash_size_bytes=0x400000 phoenix_v1.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld phoenix_v1.menu.eesz.4M2M.build.spiffs_pagesize=256 -phoenix_v1.menu.eesz.4M2M.upload.maximum_size=1044464 phoenix_v1.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 phoenix_v1.menu.eesz.4M2M.build.spiffs_start=0x200000 phoenix_v1.menu.eesz.4M2M.build.spiffs_end=0x3FA000 phoenix_v1.menu.eesz.4M2M.build.spiffs_blocksize=8192 phoenix_v1.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) phoenix_v1.menu.eesz.4M3M.build.flash_size=4M -phoenix_v1.menu.eesz.4M3M.build.flash_size_bytes=0x400000 phoenix_v1.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld phoenix_v1.menu.eesz.4M3M.build.spiffs_pagesize=256 -phoenix_v1.menu.eesz.4M3M.upload.maximum_size=1044464 phoenix_v1.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 phoenix_v1.menu.eesz.4M3M.build.spiffs_start=0x100000 phoenix_v1.menu.eesz.4M3M.build.spiffs_end=0x3FA000 phoenix_v1.menu.eesz.4M3M.build.spiffs_blocksize=8192 phoenix_v1.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) phoenix_v1.menu.eesz.4M1M.build.flash_size=4M -phoenix_v1.menu.eesz.4M1M.build.flash_size_bytes=0x400000 phoenix_v1.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld phoenix_v1.menu.eesz.4M1M.build.spiffs_pagesize=256 -phoenix_v1.menu.eesz.4M1M.upload.maximum_size=1044464 phoenix_v1.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 phoenix_v1.menu.eesz.4M1M.build.spiffs_start=0x300000 phoenix_v1.menu.eesz.4M1M.build.spiffs_end=0x3FA000 phoenix_v1.menu.eesz.4M1M.build.spiffs_blocksize=8192 phoenix_v1.menu.eesz.4M=4MB (FS:none OTA:~1019KB) phoenix_v1.menu.eesz.4M.build.flash_size=4M -phoenix_v1.menu.eesz.4M.build.flash_size_bytes=0x400000 phoenix_v1.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld phoenix_v1.menu.eesz.4M.build.spiffs_pagesize=256 -phoenix_v1.menu.eesz.4M.upload.maximum_size=1044464 phoenix_v1.menu.eesz.4M.build.rfcal_addr=0x3FC000 phoenix_v1.menu.ResetMethod.nodemcu=dtr (aka nodemcu) phoenix_v1.menu.ResetMethod.nodemcu.upload.resetmethod=--before default_reset --after hard_reset @@ -5407,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 @@ -5451,10 +6746,22 @@ phoenix_v1.menu.lvl.OOM=OOM phoenix_v1.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM phoenix_v1.menu.lvl.MDNS=MDNS phoenix_v1.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +phoenix_v1.menu.lvl.HWDT=HWDT +phoenix_v1.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +phoenix_v1.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +phoenix_v1.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K phoenix_v1.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS phoenix_v1.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 +phoenix_v1.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +phoenix_v1.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 +phoenix_v1.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +phoenix_v1.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 phoenix_v1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS phoenix_v1.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 +phoenix_v1.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +phoenix_v1.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 +phoenix_v1.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 +phoenix_v1.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 phoenix_v1.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG phoenix_v1.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG phoenix_v1.menu.wipe.none=Only Sketch @@ -5481,6 +6788,15 @@ phoenix_v1.menu.baud.921600=921600 phoenix_v1.menu.baud.921600.upload.speed=921600 phoenix_v1.menu.baud.3000000=3000000 phoenix_v1.menu.baud.3000000.upload.speed=3000000 +phoenix_v1.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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 @@ -5495,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 @@ -5521,10 +6838,6 @@ phoenix_v2.menu.ssl.all=All SSL ciphers (most compatible) phoenix_v2.menu.ssl.all.build.sslflags= phoenix_v2.menu.ssl.basic=Basic SSL ciphers (lower ROM use) phoenix_v2.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -phoenix_v2.menu.waveform.pwm=Locked PWM -phoenix_v2.menu.waveform.pwm.build.waveform= -phoenix_v2.menu.waveform.phase=Locked Phase -phoenix_v2.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE phoenix_v2.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) phoenix_v2.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 phoenix_v2.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -5533,6 +6846,10 @@ phoenix_v2.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) phoenix_v2.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP phoenix_v2.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) phoenix_v2.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +phoenix_v2.menu.mmu.ext128k=128K Heap External 23LC1024 +phoenix_v2.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +phoenix_v2.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +phoenix_v2.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 phoenix_v2.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM phoenix_v2.menu.non32xfer.fast.build.non32xferflags= phoenix_v2.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -5542,40 +6859,32 @@ phoenix_v2.build.flash_flags=-DFLASHMODE_DIO phoenix_v2.build.flash_freq=40 phoenix_v2.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) phoenix_v2.menu.eesz.4M2M.build.flash_size=4M -phoenix_v2.menu.eesz.4M2M.build.flash_size_bytes=0x400000 phoenix_v2.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld phoenix_v2.menu.eesz.4M2M.build.spiffs_pagesize=256 -phoenix_v2.menu.eesz.4M2M.upload.maximum_size=1044464 phoenix_v2.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 phoenix_v2.menu.eesz.4M2M.build.spiffs_start=0x200000 phoenix_v2.menu.eesz.4M2M.build.spiffs_end=0x3FA000 phoenix_v2.menu.eesz.4M2M.build.spiffs_blocksize=8192 phoenix_v2.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) phoenix_v2.menu.eesz.4M3M.build.flash_size=4M -phoenix_v2.menu.eesz.4M3M.build.flash_size_bytes=0x400000 phoenix_v2.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld phoenix_v2.menu.eesz.4M3M.build.spiffs_pagesize=256 -phoenix_v2.menu.eesz.4M3M.upload.maximum_size=1044464 phoenix_v2.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 phoenix_v2.menu.eesz.4M3M.build.spiffs_start=0x100000 phoenix_v2.menu.eesz.4M3M.build.spiffs_end=0x3FA000 phoenix_v2.menu.eesz.4M3M.build.spiffs_blocksize=8192 phoenix_v2.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) phoenix_v2.menu.eesz.4M1M.build.flash_size=4M -phoenix_v2.menu.eesz.4M1M.build.flash_size_bytes=0x400000 phoenix_v2.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld phoenix_v2.menu.eesz.4M1M.build.spiffs_pagesize=256 -phoenix_v2.menu.eesz.4M1M.upload.maximum_size=1044464 phoenix_v2.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 phoenix_v2.menu.eesz.4M1M.build.spiffs_start=0x300000 phoenix_v2.menu.eesz.4M1M.build.spiffs_end=0x3FA000 phoenix_v2.menu.eesz.4M1M.build.spiffs_blocksize=8192 phoenix_v2.menu.eesz.4M=4MB (FS:none OTA:~1019KB) phoenix_v2.menu.eesz.4M.build.flash_size=4M -phoenix_v2.menu.eesz.4M.build.flash_size_bytes=0x400000 phoenix_v2.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld phoenix_v2.menu.eesz.4M.build.spiffs_pagesize=256 -phoenix_v2.menu.eesz.4M.upload.maximum_size=1044464 phoenix_v2.menu.eesz.4M.build.rfcal_addr=0x3FC000 phoenix_v2.menu.ResetMethod.nodemcu=dtr (aka nodemcu) phoenix_v2.menu.ResetMethod.nodemcu.upload.resetmethod=--before default_reset --after hard_reset @@ -5613,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 @@ -5657,10 +6972,22 @@ phoenix_v2.menu.lvl.OOM=OOM phoenix_v2.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM phoenix_v2.menu.lvl.MDNS=MDNS phoenix_v2.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +phoenix_v2.menu.lvl.HWDT=HWDT +phoenix_v2.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +phoenix_v2.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +phoenix_v2.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K phoenix_v2.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS phoenix_v2.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 +phoenix_v2.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +phoenix_v2.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 +phoenix_v2.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +phoenix_v2.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 phoenix_v2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS phoenix_v2.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 +phoenix_v2.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +phoenix_v2.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 +phoenix_v2.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 +phoenix_v2.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 phoenix_v2.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG phoenix_v2.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG phoenix_v2.menu.wipe.none=Only Sketch @@ -5687,6 +7014,15 @@ phoenix_v2.menu.baud.921600=921600 phoenix_v2.menu.baud.921600.upload.speed=921600 phoenix_v2.menu.baud.3000000=3000000 phoenix_v2.menu.baud.3000000.upload.speed=3000000 +phoenix_v2.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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 @@ -5701,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 @@ -5727,10 +7064,6 @@ eduinowifi.menu.ssl.all=All SSL ciphers (most compatible) eduinowifi.menu.ssl.all.build.sslflags= eduinowifi.menu.ssl.basic=Basic SSL ciphers (lower ROM use) eduinowifi.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -eduinowifi.menu.waveform.pwm=Locked PWM -eduinowifi.menu.waveform.pwm.build.waveform= -eduinowifi.menu.waveform.phase=Locked Phase -eduinowifi.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE eduinowifi.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) eduinowifi.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 eduinowifi.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -5739,6 +7072,10 @@ eduinowifi.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) eduinowifi.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP eduinowifi.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) eduinowifi.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +eduinowifi.menu.mmu.ext128k=128K Heap External 23LC1024 +eduinowifi.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +eduinowifi.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +eduinowifi.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 eduinowifi.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM eduinowifi.menu.non32xfer.fast.build.non32xferflags= eduinowifi.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -5749,40 +7086,32 @@ eduinowifi.build.flash_flags=-DFLASHMODE_DIO eduinowifi.build.flash_freq=40 eduinowifi.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) eduinowifi.menu.eesz.4M2M.build.flash_size=4M -eduinowifi.menu.eesz.4M2M.build.flash_size_bytes=0x400000 eduinowifi.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld eduinowifi.menu.eesz.4M2M.build.spiffs_pagesize=256 -eduinowifi.menu.eesz.4M2M.upload.maximum_size=1044464 eduinowifi.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 eduinowifi.menu.eesz.4M2M.build.spiffs_start=0x200000 eduinowifi.menu.eesz.4M2M.build.spiffs_end=0x3FA000 eduinowifi.menu.eesz.4M2M.build.spiffs_blocksize=8192 eduinowifi.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) eduinowifi.menu.eesz.4M3M.build.flash_size=4M -eduinowifi.menu.eesz.4M3M.build.flash_size_bytes=0x400000 eduinowifi.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld eduinowifi.menu.eesz.4M3M.build.spiffs_pagesize=256 -eduinowifi.menu.eesz.4M3M.upload.maximum_size=1044464 eduinowifi.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 eduinowifi.menu.eesz.4M3M.build.spiffs_start=0x100000 eduinowifi.menu.eesz.4M3M.build.spiffs_end=0x3FA000 eduinowifi.menu.eesz.4M3M.build.spiffs_blocksize=8192 eduinowifi.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) eduinowifi.menu.eesz.4M1M.build.flash_size=4M -eduinowifi.menu.eesz.4M1M.build.flash_size_bytes=0x400000 eduinowifi.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld eduinowifi.menu.eesz.4M1M.build.spiffs_pagesize=256 -eduinowifi.menu.eesz.4M1M.upload.maximum_size=1044464 eduinowifi.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 eduinowifi.menu.eesz.4M1M.build.spiffs_start=0x300000 eduinowifi.menu.eesz.4M1M.build.spiffs_end=0x3FA000 eduinowifi.menu.eesz.4M1M.build.spiffs_blocksize=8192 eduinowifi.menu.eesz.4M=4MB (FS:none OTA:~1019KB) eduinowifi.menu.eesz.4M.build.flash_size=4M -eduinowifi.menu.eesz.4M.build.flash_size_bytes=0x400000 eduinowifi.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld eduinowifi.menu.eesz.4M.build.spiffs_pagesize=256 -eduinowifi.menu.eesz.4M.upload.maximum_size=1044464 eduinowifi.menu.eesz.4M.build.rfcal_addr=0x3FC000 eduinowifi.menu.ip.lm2f=v2 Lower Memory eduinowifi.menu.ip.lm2f.build.lwip_include=lwip2/include @@ -5816,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 @@ -5860,10 +7195,22 @@ eduinowifi.menu.lvl.OOM=OOM eduinowifi.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM eduinowifi.menu.lvl.MDNS=MDNS eduinowifi.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +eduinowifi.menu.lvl.HWDT=HWDT +eduinowifi.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +eduinowifi.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +eduinowifi.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K eduinowifi.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS eduinowifi.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 +eduinowifi.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +eduinowifi.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 +eduinowifi.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +eduinowifi.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 eduinowifi.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS eduinowifi.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 +eduinowifi.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +eduinowifi.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 +eduinowifi.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 +eduinowifi.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 eduinowifi.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG eduinowifi.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG eduinowifi.menu.wipe.none=Only Sketch @@ -5890,6 +7237,15 @@ eduinowifi.menu.baud.921600=921600 eduinowifi.menu.baud.921600.upload.speed=921600 eduinowifi.menu.baud.3000000=3000000 eduinowifi.menu.baud.3000000.upload.speed=3000000 +eduinowifi.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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 @@ -5904,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 @@ -5930,10 +7287,6 @@ wiolink.menu.ssl.all=All SSL ciphers (most compatible) wiolink.menu.ssl.all.build.sslflags= wiolink.menu.ssl.basic=Basic SSL ciphers (lower ROM use) wiolink.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -wiolink.menu.waveform.pwm=Locked PWM -wiolink.menu.waveform.pwm.build.waveform= -wiolink.menu.waveform.phase=Locked Phase -wiolink.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE wiolink.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) wiolink.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 wiolink.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -5942,6 +7295,10 @@ wiolink.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) wiolink.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP wiolink.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) wiolink.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +wiolink.menu.mmu.ext128k=128K Heap External 23LC1024 +wiolink.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +wiolink.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +wiolink.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 wiolink.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM wiolink.menu.non32xfer.fast.build.non32xferflags= wiolink.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -5952,40 +7309,32 @@ wiolink.build.flash_flags=-DFLASHMODE_QIO wiolink.build.flash_freq=40 wiolink.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) wiolink.menu.eesz.4M2M.build.flash_size=4M -wiolink.menu.eesz.4M2M.build.flash_size_bytes=0x400000 wiolink.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld wiolink.menu.eesz.4M2M.build.spiffs_pagesize=256 -wiolink.menu.eesz.4M2M.upload.maximum_size=1044464 wiolink.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 wiolink.menu.eesz.4M2M.build.spiffs_start=0x200000 wiolink.menu.eesz.4M2M.build.spiffs_end=0x3FA000 wiolink.menu.eesz.4M2M.build.spiffs_blocksize=8192 wiolink.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) wiolink.menu.eesz.4M3M.build.flash_size=4M -wiolink.menu.eesz.4M3M.build.flash_size_bytes=0x400000 wiolink.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld wiolink.menu.eesz.4M3M.build.spiffs_pagesize=256 -wiolink.menu.eesz.4M3M.upload.maximum_size=1044464 wiolink.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 wiolink.menu.eesz.4M3M.build.spiffs_start=0x100000 wiolink.menu.eesz.4M3M.build.spiffs_end=0x3FA000 wiolink.menu.eesz.4M3M.build.spiffs_blocksize=8192 wiolink.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) wiolink.menu.eesz.4M1M.build.flash_size=4M -wiolink.menu.eesz.4M1M.build.flash_size_bytes=0x400000 wiolink.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld wiolink.menu.eesz.4M1M.build.spiffs_pagesize=256 -wiolink.menu.eesz.4M1M.upload.maximum_size=1044464 wiolink.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 wiolink.menu.eesz.4M1M.build.spiffs_start=0x300000 wiolink.menu.eesz.4M1M.build.spiffs_end=0x3FA000 wiolink.menu.eesz.4M1M.build.spiffs_blocksize=8192 wiolink.menu.eesz.4M=4MB (FS:none OTA:~1019KB) wiolink.menu.eesz.4M.build.flash_size=4M -wiolink.menu.eesz.4M.build.flash_size_bytes=0x400000 wiolink.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld wiolink.menu.eesz.4M.build.spiffs_pagesize=256 -wiolink.menu.eesz.4M.upload.maximum_size=1044464 wiolink.menu.eesz.4M.build.rfcal_addr=0x3FC000 wiolink.menu.ip.lm2f=v2 Lower Memory wiolink.menu.ip.lm2f.build.lwip_include=lwip2/include @@ -6019,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 @@ -6063,10 +7418,22 @@ wiolink.menu.lvl.OOM=OOM wiolink.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM wiolink.menu.lvl.MDNS=MDNS wiolink.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +wiolink.menu.lvl.HWDT=HWDT +wiolink.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +wiolink.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +wiolink.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K wiolink.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS wiolink.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 +wiolink.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +wiolink.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 +wiolink.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +wiolink.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 wiolink.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS wiolink.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 +wiolink.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +wiolink.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 +wiolink.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 +wiolink.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 wiolink.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG wiolink.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG wiolink.menu.wipe.none=Only Sketch @@ -6093,6 +7460,15 @@ wiolink.menu.baud.921600=921600 wiolink.menu.baud.921600.upload.speed=921600 wiolink.menu.baud.3000000=3000000 wiolink.menu.baud.3000000.upload.speed=3000000 +wiolink.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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 @@ -6107,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 @@ -6133,10 +7510,6 @@ blynk.menu.ssl.all=All SSL ciphers (most compatible) blynk.menu.ssl.all.build.sslflags= blynk.menu.ssl.basic=Basic SSL ciphers (lower ROM use) blynk.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -blynk.menu.waveform.pwm=Locked PWM -blynk.menu.waveform.pwm.build.waveform= -blynk.menu.waveform.phase=Locked Phase -blynk.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE blynk.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) blynk.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 blynk.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -6145,6 +7518,10 @@ blynk.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) blynk.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP blynk.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) blynk.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +blynk.menu.mmu.ext128k=128K Heap External 23LC1024 +blynk.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +blynk.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +blynk.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 blynk.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM blynk.menu.non32xfer.fast.build.non32xferflags= blynk.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -6155,40 +7532,32 @@ blynk.build.flash_flags=-DFLASHMODE_QIO blynk.build.flash_freq=40 blynk.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) blynk.menu.eesz.4M2M.build.flash_size=4M -blynk.menu.eesz.4M2M.build.flash_size_bytes=0x400000 blynk.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld blynk.menu.eesz.4M2M.build.spiffs_pagesize=256 -blynk.menu.eesz.4M2M.upload.maximum_size=1044464 blynk.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 blynk.menu.eesz.4M2M.build.spiffs_start=0x200000 blynk.menu.eesz.4M2M.build.spiffs_end=0x3FA000 blynk.menu.eesz.4M2M.build.spiffs_blocksize=8192 blynk.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) blynk.menu.eesz.4M3M.build.flash_size=4M -blynk.menu.eesz.4M3M.build.flash_size_bytes=0x400000 blynk.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld blynk.menu.eesz.4M3M.build.spiffs_pagesize=256 -blynk.menu.eesz.4M3M.upload.maximum_size=1044464 blynk.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 blynk.menu.eesz.4M3M.build.spiffs_start=0x100000 blynk.menu.eesz.4M3M.build.spiffs_end=0x3FA000 blynk.menu.eesz.4M3M.build.spiffs_blocksize=8192 blynk.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) blynk.menu.eesz.4M1M.build.flash_size=4M -blynk.menu.eesz.4M1M.build.flash_size_bytes=0x400000 blynk.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld blynk.menu.eesz.4M1M.build.spiffs_pagesize=256 -blynk.menu.eesz.4M1M.upload.maximum_size=1044464 blynk.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 blynk.menu.eesz.4M1M.build.spiffs_start=0x300000 blynk.menu.eesz.4M1M.build.spiffs_end=0x3FA000 blynk.menu.eesz.4M1M.build.spiffs_blocksize=8192 blynk.menu.eesz.4M=4MB (FS:none OTA:~1019KB) blynk.menu.eesz.4M.build.flash_size=4M -blynk.menu.eesz.4M.build.flash_size_bytes=0x400000 blynk.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld blynk.menu.eesz.4M.build.spiffs_pagesize=256 -blynk.menu.eesz.4M.upload.maximum_size=1044464 blynk.menu.eesz.4M.build.rfcal_addr=0x3FC000 blynk.menu.ip.lm2f=v2 Lower Memory blynk.menu.ip.lm2f.build.lwip_include=lwip2/include @@ -6222,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 @@ -6266,10 +7641,22 @@ blynk.menu.lvl.OOM=OOM blynk.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM blynk.menu.lvl.MDNS=MDNS blynk.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +blynk.menu.lvl.HWDT=HWDT +blynk.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +blynk.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +blynk.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K blynk.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS blynk.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 +blynk.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +blynk.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 +blynk.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +blynk.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 blynk.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS blynk.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 +blynk.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +blynk.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 +blynk.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 +blynk.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 blynk.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG blynk.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG blynk.menu.wipe.none=Only Sketch @@ -6296,6 +7683,15 @@ blynk.menu.baud.921600=921600 blynk.menu.baud.921600.upload.speed=921600 blynk.menu.baud.3000000=3000000 blynk.menu.baud.3000000.upload.speed=3000000 +blynk.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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 @@ -6310,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 @@ -6336,10 +7733,6 @@ thing.menu.ssl.all=All SSL ciphers (most compatible) thing.menu.ssl.all.build.sslflags= thing.menu.ssl.basic=Basic SSL ciphers (lower ROM use) thing.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -thing.menu.waveform.pwm=Locked PWM -thing.menu.waveform.pwm.build.waveform= -thing.menu.waveform.phase=Locked Phase -thing.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE thing.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) thing.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 thing.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -6348,6 +7741,10 @@ thing.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) thing.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP thing.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) thing.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +thing.menu.mmu.ext128k=128K Heap External 23LC1024 +thing.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +thing.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +thing.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 thing.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM thing.menu.non32xfer.fast.build.non32xferflags= thing.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -6358,40 +7755,32 @@ thing.build.flash_flags=-DFLASHMODE_QIO thing.build.flash_freq=40 thing.menu.eesz.512K32=512KB (FS:32KB OTA:~230KB) thing.menu.eesz.512K32.build.flash_size=512K -thing.menu.eesz.512K32.build.flash_size_bytes=0x80000 thing.menu.eesz.512K32.build.flash_ld=eagle.flash.512k32.ld thing.menu.eesz.512K32.build.spiffs_pagesize=256 -thing.menu.eesz.512K32.upload.maximum_size=466928 thing.menu.eesz.512K32.build.rfcal_addr=0x7C000 thing.menu.eesz.512K32.build.spiffs_start=0x73000 thing.menu.eesz.512K32.build.spiffs_end=0x7B000 thing.menu.eesz.512K32.build.spiffs_blocksize=4096 thing.menu.eesz.512K64=512KB (FS:64KB OTA:~214KB) thing.menu.eesz.512K64.build.flash_size=512K -thing.menu.eesz.512K64.build.flash_size_bytes=0x80000 thing.menu.eesz.512K64.build.flash_ld=eagle.flash.512k64.ld thing.menu.eesz.512K64.build.spiffs_pagesize=256 -thing.menu.eesz.512K64.upload.maximum_size=434160 thing.menu.eesz.512K64.build.rfcal_addr=0x7C000 thing.menu.eesz.512K64.build.spiffs_start=0x6B000 thing.menu.eesz.512K64.build.spiffs_end=0x7B000 thing.menu.eesz.512K64.build.spiffs_blocksize=4096 thing.menu.eesz.512K128=512KB (FS:128KB OTA:~182KB) thing.menu.eesz.512K128.build.flash_size=512K -thing.menu.eesz.512K128.build.flash_size_bytes=0x80000 thing.menu.eesz.512K128.build.flash_ld=eagle.flash.512k128.ld thing.menu.eesz.512K128.build.spiffs_pagesize=256 -thing.menu.eesz.512K128.upload.maximum_size=368624 thing.menu.eesz.512K128.build.rfcal_addr=0x7C000 thing.menu.eesz.512K128.build.spiffs_start=0x5B000 thing.menu.eesz.512K128.build.spiffs_end=0x7B000 thing.menu.eesz.512K128.build.spiffs_blocksize=4096 thing.menu.eesz.512K=512KB (FS:none OTA:~246KB) thing.menu.eesz.512K.build.flash_size=512K -thing.menu.eesz.512K.build.flash_size_bytes=0x80000 thing.menu.eesz.512K.build.flash_ld=eagle.flash.512k.ld thing.menu.eesz.512K.build.spiffs_pagesize=256 -thing.menu.eesz.512K.upload.maximum_size=499696 thing.menu.eesz.512K.build.rfcal_addr=0x7C000 thing.menu.ip.lm2f=v2 Lower Memory thing.menu.ip.lm2f.build.lwip_include=lwip2/include @@ -6425,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 @@ -6469,10 +7864,22 @@ thing.menu.lvl.OOM=OOM thing.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM thing.menu.lvl.MDNS=MDNS thing.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +thing.menu.lvl.HWDT=HWDT +thing.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +thing.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +thing.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K thing.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS thing.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 +thing.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +thing.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 +thing.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +thing.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 thing.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS thing.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 +thing.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +thing.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 +thing.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 +thing.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 thing.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG thing.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG thing.menu.wipe.none=Only Sketch @@ -6499,6 +7906,15 @@ thing.menu.baud.921600=921600 thing.menu.baud.921600.upload.speed=921600 thing.menu.baud.3000000=3000000 thing.menu.baud.3000000.upload.speed=3000000 +thing.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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 @@ -6513,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 @@ -6539,10 +7956,6 @@ thingdev.menu.ssl.all=All SSL ciphers (most compatible) thingdev.menu.ssl.all.build.sslflags= thingdev.menu.ssl.basic=Basic SSL ciphers (lower ROM use) thingdev.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -thingdev.menu.waveform.pwm=Locked PWM -thingdev.menu.waveform.pwm.build.waveform= -thingdev.menu.waveform.phase=Locked Phase -thingdev.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE thingdev.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) thingdev.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 thingdev.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -6551,6 +7964,10 @@ thingdev.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) thingdev.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP thingdev.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) thingdev.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +thingdev.menu.mmu.ext128k=128K Heap External 23LC1024 +thingdev.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +thingdev.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +thingdev.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 thingdev.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM thingdev.menu.non32xfer.fast.build.non32xferflags= thingdev.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -6561,40 +7978,32 @@ thingdev.build.flash_flags=-DFLASHMODE_DIO thingdev.build.flash_freq=40 thingdev.menu.eesz.512K32=512KB (FS:32KB OTA:~230KB) thingdev.menu.eesz.512K32.build.flash_size=512K -thingdev.menu.eesz.512K32.build.flash_size_bytes=0x80000 thingdev.menu.eesz.512K32.build.flash_ld=eagle.flash.512k32.ld thingdev.menu.eesz.512K32.build.spiffs_pagesize=256 -thingdev.menu.eesz.512K32.upload.maximum_size=466928 thingdev.menu.eesz.512K32.build.rfcal_addr=0x7C000 thingdev.menu.eesz.512K32.build.spiffs_start=0x73000 thingdev.menu.eesz.512K32.build.spiffs_end=0x7B000 thingdev.menu.eesz.512K32.build.spiffs_blocksize=4096 thingdev.menu.eesz.512K64=512KB (FS:64KB OTA:~214KB) thingdev.menu.eesz.512K64.build.flash_size=512K -thingdev.menu.eesz.512K64.build.flash_size_bytes=0x80000 thingdev.menu.eesz.512K64.build.flash_ld=eagle.flash.512k64.ld thingdev.menu.eesz.512K64.build.spiffs_pagesize=256 -thingdev.menu.eesz.512K64.upload.maximum_size=434160 thingdev.menu.eesz.512K64.build.rfcal_addr=0x7C000 thingdev.menu.eesz.512K64.build.spiffs_start=0x6B000 thingdev.menu.eesz.512K64.build.spiffs_end=0x7B000 thingdev.menu.eesz.512K64.build.spiffs_blocksize=4096 thingdev.menu.eesz.512K128=512KB (FS:128KB OTA:~182KB) thingdev.menu.eesz.512K128.build.flash_size=512K -thingdev.menu.eesz.512K128.build.flash_size_bytes=0x80000 thingdev.menu.eesz.512K128.build.flash_ld=eagle.flash.512k128.ld thingdev.menu.eesz.512K128.build.spiffs_pagesize=256 -thingdev.menu.eesz.512K128.upload.maximum_size=368624 thingdev.menu.eesz.512K128.build.rfcal_addr=0x7C000 thingdev.menu.eesz.512K128.build.spiffs_start=0x5B000 thingdev.menu.eesz.512K128.build.spiffs_end=0x7B000 thingdev.menu.eesz.512K128.build.spiffs_blocksize=4096 thingdev.menu.eesz.512K=512KB (FS:none OTA:~246KB) thingdev.menu.eesz.512K.build.flash_size=512K -thingdev.menu.eesz.512K.build.flash_size_bytes=0x80000 thingdev.menu.eesz.512K.build.flash_ld=eagle.flash.512k.ld thingdev.menu.eesz.512K.build.spiffs_pagesize=256 -thingdev.menu.eesz.512K.upload.maximum_size=499696 thingdev.menu.eesz.512K.build.rfcal_addr=0x7C000 thingdev.menu.ip.lm2f=v2 Lower Memory thingdev.menu.ip.lm2f.build.lwip_include=lwip2/include @@ -6628,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 @@ -6672,10 +8087,22 @@ thingdev.menu.lvl.OOM=OOM thingdev.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM thingdev.menu.lvl.MDNS=MDNS thingdev.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +thingdev.menu.lvl.HWDT=HWDT +thingdev.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +thingdev.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +thingdev.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K thingdev.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS thingdev.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 +thingdev.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +thingdev.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 +thingdev.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +thingdev.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 thingdev.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS thingdev.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 +thingdev.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +thingdev.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 +thingdev.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 +thingdev.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 thingdev.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG thingdev.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG thingdev.menu.wipe.none=Only Sketch @@ -6702,6 +8129,15 @@ thingdev.menu.baud.921600=921600 thingdev.menu.baud.921600.upload.speed=921600 thingdev.menu.baud.3000000=3000000 thingdev.menu.baud.3000000.upload.speed=3000000 +thingdev.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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 @@ -6716,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 @@ -6742,10 +8179,6 @@ esp210.menu.ssl.all=All SSL ciphers (most compatible) esp210.menu.ssl.all.build.sslflags= esp210.menu.ssl.basic=Basic SSL ciphers (lower ROM use) esp210.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -esp210.menu.waveform.pwm=Locked PWM -esp210.menu.waveform.pwm.build.waveform= -esp210.menu.waveform.phase=Locked Phase -esp210.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE esp210.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) esp210.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 esp210.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -6754,6 +8187,10 @@ esp210.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) esp210.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP esp210.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) esp210.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +esp210.menu.mmu.ext128k=128K Heap External 23LC1024 +esp210.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +esp210.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +esp210.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 esp210.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM esp210.menu.non32xfer.fast.build.non32xferflags= esp210.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -6764,40 +8201,32 @@ esp210.build.flash_flags=-DFLASHMODE_QIO esp210.build.flash_freq=40 esp210.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) esp210.menu.eesz.4M2M.build.flash_size=4M -esp210.menu.eesz.4M2M.build.flash_size_bytes=0x400000 esp210.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld esp210.menu.eesz.4M2M.build.spiffs_pagesize=256 -esp210.menu.eesz.4M2M.upload.maximum_size=1044464 esp210.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 esp210.menu.eesz.4M2M.build.spiffs_start=0x200000 esp210.menu.eesz.4M2M.build.spiffs_end=0x3FA000 esp210.menu.eesz.4M2M.build.spiffs_blocksize=8192 esp210.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) esp210.menu.eesz.4M3M.build.flash_size=4M -esp210.menu.eesz.4M3M.build.flash_size_bytes=0x400000 esp210.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld esp210.menu.eesz.4M3M.build.spiffs_pagesize=256 -esp210.menu.eesz.4M3M.upload.maximum_size=1044464 esp210.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 esp210.menu.eesz.4M3M.build.spiffs_start=0x100000 esp210.menu.eesz.4M3M.build.spiffs_end=0x3FA000 esp210.menu.eesz.4M3M.build.spiffs_blocksize=8192 esp210.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) esp210.menu.eesz.4M1M.build.flash_size=4M -esp210.menu.eesz.4M1M.build.flash_size_bytes=0x400000 esp210.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld esp210.menu.eesz.4M1M.build.spiffs_pagesize=256 -esp210.menu.eesz.4M1M.upload.maximum_size=1044464 esp210.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 esp210.menu.eesz.4M1M.build.spiffs_start=0x300000 esp210.menu.eesz.4M1M.build.spiffs_end=0x3FA000 esp210.menu.eesz.4M1M.build.spiffs_blocksize=8192 esp210.menu.eesz.4M=4MB (FS:none OTA:~1019KB) esp210.menu.eesz.4M.build.flash_size=4M -esp210.menu.eesz.4M.build.flash_size_bytes=0x400000 esp210.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld esp210.menu.eesz.4M.build.spiffs_pagesize=256 -esp210.menu.eesz.4M.upload.maximum_size=1044464 esp210.menu.eesz.4M.build.rfcal_addr=0x3FC000 esp210.menu.ip.lm2f=v2 Lower Memory esp210.menu.ip.lm2f.build.lwip_include=lwip2/include @@ -6831,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 @@ -6875,10 +8310,22 @@ esp210.menu.lvl.OOM=OOM esp210.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM esp210.menu.lvl.MDNS=MDNS esp210.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +esp210.menu.lvl.HWDT=HWDT +esp210.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +esp210.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +esp210.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K esp210.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS esp210.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 +esp210.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +esp210.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 +esp210.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +esp210.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 esp210.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS esp210.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 +esp210.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +esp210.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 +esp210.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 +esp210.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 esp210.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG esp210.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG esp210.menu.wipe.none=Only Sketch @@ -6905,10 +8352,19 @@ esp210.menu.baud.921600=921600 esp210.menu.baud.921600.upload.speed=921600 esp210.menu.baud.3000000=3000000 esp210.menu.baud.3000000.upload.speed=3000000 +esp210.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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 -espinotee.build.board=ESP8266_ESP13 +espinotee.build.board=ESP8266_ESPINO_ESP13 espinotee.build.variant=espinotee espinotee.upload.tool=esptool espinotee.upload.maximum_data_size=81920 @@ -6919,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 @@ -6945,10 +8402,6 @@ espinotee.menu.ssl.all=All SSL ciphers (most compatible) espinotee.menu.ssl.all.build.sslflags= espinotee.menu.ssl.basic=Basic SSL ciphers (lower ROM use) espinotee.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -espinotee.menu.waveform.pwm=Locked PWM -espinotee.menu.waveform.pwm.build.waveform= -espinotee.menu.waveform.phase=Locked Phase -espinotee.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE espinotee.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) espinotee.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 espinotee.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -6957,6 +8410,10 @@ espinotee.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) espinotee.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP espinotee.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) espinotee.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +espinotee.menu.mmu.ext128k=128K Heap External 23LC1024 +espinotee.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +espinotee.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +espinotee.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 espinotee.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM espinotee.menu.non32xfer.fast.build.non32xferflags= espinotee.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -6967,40 +8424,32 @@ espinotee.build.flash_flags=-DFLASHMODE_QIO espinotee.build.flash_freq=40 espinotee.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) espinotee.menu.eesz.4M2M.build.flash_size=4M -espinotee.menu.eesz.4M2M.build.flash_size_bytes=0x400000 espinotee.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld espinotee.menu.eesz.4M2M.build.spiffs_pagesize=256 -espinotee.menu.eesz.4M2M.upload.maximum_size=1044464 espinotee.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 espinotee.menu.eesz.4M2M.build.spiffs_start=0x200000 espinotee.menu.eesz.4M2M.build.spiffs_end=0x3FA000 espinotee.menu.eesz.4M2M.build.spiffs_blocksize=8192 espinotee.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) espinotee.menu.eesz.4M3M.build.flash_size=4M -espinotee.menu.eesz.4M3M.build.flash_size_bytes=0x400000 espinotee.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld espinotee.menu.eesz.4M3M.build.spiffs_pagesize=256 -espinotee.menu.eesz.4M3M.upload.maximum_size=1044464 espinotee.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 espinotee.menu.eesz.4M3M.build.spiffs_start=0x100000 espinotee.menu.eesz.4M3M.build.spiffs_end=0x3FA000 espinotee.menu.eesz.4M3M.build.spiffs_blocksize=8192 espinotee.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) espinotee.menu.eesz.4M1M.build.flash_size=4M -espinotee.menu.eesz.4M1M.build.flash_size_bytes=0x400000 espinotee.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld espinotee.menu.eesz.4M1M.build.spiffs_pagesize=256 -espinotee.menu.eesz.4M1M.upload.maximum_size=1044464 espinotee.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 espinotee.menu.eesz.4M1M.build.spiffs_start=0x300000 espinotee.menu.eesz.4M1M.build.spiffs_end=0x3FA000 espinotee.menu.eesz.4M1M.build.spiffs_blocksize=8192 espinotee.menu.eesz.4M=4MB (FS:none OTA:~1019KB) espinotee.menu.eesz.4M.build.flash_size=4M -espinotee.menu.eesz.4M.build.flash_size_bytes=0x400000 espinotee.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld espinotee.menu.eesz.4M.build.spiffs_pagesize=256 -espinotee.menu.eesz.4M.upload.maximum_size=1044464 espinotee.menu.eesz.4M.build.rfcal_addr=0x3FC000 espinotee.menu.ip.lm2f=v2 Lower Memory espinotee.menu.ip.lm2f.build.lwip_include=lwip2/include @@ -7034,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 @@ -7078,10 +8533,22 @@ espinotee.menu.lvl.OOM=OOM espinotee.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM espinotee.menu.lvl.MDNS=MDNS espinotee.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +espinotee.menu.lvl.HWDT=HWDT +espinotee.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +espinotee.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +espinotee.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K espinotee.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS espinotee.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 +espinotee.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +espinotee.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 +espinotee.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +espinotee.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 espinotee.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS espinotee.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 +espinotee.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +espinotee.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 +espinotee.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 +espinotee.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 espinotee.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG espinotee.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG espinotee.menu.wipe.none=Only Sketch @@ -7108,6 +8575,238 @@ espinotee.menu.baud.921600=921600 espinotee.menu.baud.921600.upload.speed=921600 espinotee.menu.baud.3000000=3000000 espinotee.menu.baud.3000000.upload.speed=3000000 +espinotee.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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 +wifi_kit_8.build.board=wifi_kit_8 +wifi_kit_8.build.variant=wifi_kit_8 +wifi_kit_8.upload.tool=esptool +wifi_kit_8.upload.maximum_data_size=81920 +wifi_kit_8.upload.wait_for_upload_port=true +wifi_kit_8.upload.erase_cmd= +wifi_kit_8.serial.disableDTR=true +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 +wifi_kit_8.menu.xtal.80.build.f_cpu=80000000L +wifi_kit_8.menu.xtal.160=160 MHz +wifi_kit_8.menu.xtal.160.build.f_cpu=160000000L +wifi_kit_8.menu.vt.flash=Flash +wifi_kit_8.menu.vt.flash.build.vtable_flags=-DVTABLES_IN_FLASH +wifi_kit_8.menu.vt.heap=Heap +wifi_kit_8.menu.vt.heap.build.vtable_flags=-DVTABLES_IN_DRAM +wifi_kit_8.menu.vt.iram=IRAM +wifi_kit_8.menu.vt.iram.build.vtable_flags=-DVTABLES_IN_IRAM +wifi_kit_8.menu.exception.disabled=Disabled (new aborts on oom) +wifi_kit_8.menu.exception.disabled.build.exception_flags=-fno-exceptions +wifi_kit_8.menu.exception.disabled.build.stdcpp_lib=-lstdc++ +wifi_kit_8.menu.exception.enabled=Enabled +wifi_kit_8.menu.exception.enabled.build.exception_flags=-fexceptions +wifi_kit_8.menu.exception.enabled.build.stdcpp_lib=-lstdc++-exc +wifi_kit_8.menu.stacksmash.disabled=Disabled +wifi_kit_8.menu.stacksmash.disabled.build.stacksmash_flags= +wifi_kit_8.menu.stacksmash.enabled=Enabled +wifi_kit_8.menu.stacksmash.enabled.build.stacksmash_flags=-fstack-protector +wifi_kit_8.menu.ssl.all=All SSL ciphers (most compatible) +wifi_kit_8.menu.ssl.all.build.sslflags= +wifi_kit_8.menu.ssl.basic=Basic SSL ciphers (lower ROM use) +wifi_kit_8.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC +wifi_kit_8.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) +wifi_kit_8.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +wifi_kit_8.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) +wifi_kit_8.menu.mmu.4816.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 +wifi_kit_8.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) +wifi_kit_8.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP +wifi_kit_8.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) +wifi_kit_8.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +wifi_kit_8.menu.mmu.ext128k=128K Heap External 23LC1024 +wifi_kit_8.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +wifi_kit_8.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +wifi_kit_8.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +wifi_kit_8.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM +wifi_kit_8.menu.non32xfer.fast.build.non32xferflags= +wifi_kit_8.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) +wifi_kit_8.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER +wifi_kit_8.upload.resetmethod=--before default_reset --after hard_reset +wifi_kit_8.build.flash_mode=dio +wifi_kit_8.build.flash_flags=-DFLASHMODE_DIO +wifi_kit_8.build.flash_freq=40 +wifi_kit_8.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) +wifi_kit_8.menu.eesz.4M2M.build.flash_size=4M +wifi_kit_8.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld +wifi_kit_8.menu.eesz.4M2M.build.spiffs_pagesize=256 +wifi_kit_8.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 +wifi_kit_8.menu.eesz.4M2M.build.spiffs_start=0x200000 +wifi_kit_8.menu.eesz.4M2M.build.spiffs_end=0x3FA000 +wifi_kit_8.menu.eesz.4M2M.build.spiffs_blocksize=8192 +wifi_kit_8.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) +wifi_kit_8.menu.eesz.4M3M.build.flash_size=4M +wifi_kit_8.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld +wifi_kit_8.menu.eesz.4M3M.build.spiffs_pagesize=256 +wifi_kit_8.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 +wifi_kit_8.menu.eesz.4M3M.build.spiffs_start=0x100000 +wifi_kit_8.menu.eesz.4M3M.build.spiffs_end=0x3FA000 +wifi_kit_8.menu.eesz.4M3M.build.spiffs_blocksize=8192 +wifi_kit_8.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) +wifi_kit_8.menu.eesz.4M1M.build.flash_size=4M +wifi_kit_8.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld +wifi_kit_8.menu.eesz.4M1M.build.spiffs_pagesize=256 +wifi_kit_8.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 +wifi_kit_8.menu.eesz.4M1M.build.spiffs_start=0x300000 +wifi_kit_8.menu.eesz.4M1M.build.spiffs_end=0x3FA000 +wifi_kit_8.menu.eesz.4M1M.build.spiffs_blocksize=8192 +wifi_kit_8.menu.eesz.4M=4MB (FS:none OTA:~1019KB) +wifi_kit_8.menu.eesz.4M.build.flash_size=4M +wifi_kit_8.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld +wifi_kit_8.menu.eesz.4M.build.spiffs_pagesize=256 +wifi_kit_8.menu.eesz.4M.build.rfcal_addr=0x3FC000 +wifi_kit_8.menu.ip.lm2f=v2 Lower Memory +wifi_kit_8.menu.ip.lm2f.build.lwip_include=lwip2/include +wifi_kit_8.menu.ip.lm2f.build.lwip_lib=-llwip2-536-feat +wifi_kit_8.menu.ip.lm2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +wifi_kit_8.menu.ip.hb2f=v2 Higher Bandwidth +wifi_kit_8.menu.ip.hb2f.build.lwip_include=lwip2/include +wifi_kit_8.menu.ip.hb2f.build.lwip_lib=-llwip2-1460-feat +wifi_kit_8.menu.ip.hb2f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 +wifi_kit_8.menu.ip.lm2n=v2 Lower Memory (no features) +wifi_kit_8.menu.ip.lm2n.build.lwip_include=lwip2/include +wifi_kit_8.menu.ip.lm2n.build.lwip_lib=-llwip2-536 +wifi_kit_8.menu.ip.lm2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +wifi_kit_8.menu.ip.hb2n=v2 Higher Bandwidth (no features) +wifi_kit_8.menu.ip.hb2n.build.lwip_include=lwip2/include +wifi_kit_8.menu.ip.hb2n.build.lwip_lib=-llwip2-1460 +wifi_kit_8.menu.ip.hb2n.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=0 -DLWIP_IPV6=0 +wifi_kit_8.menu.ip.lm6f=v2 IPv6 Lower Memory +wifi_kit_8.menu.ip.lm6f.build.lwip_include=lwip2/include +wifi_kit_8.menu.ip.lm6f.build.lwip_lib=-llwip6-536-feat +wifi_kit_8.menu.ip.lm6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +wifi_kit_8.menu.ip.hb6f=v2 IPv6 Higher Bandwidth +wifi_kit_8.menu.ip.hb6f.build.lwip_include=lwip2/include +wifi_kit_8.menu.ip.hb6f.build.lwip_lib=-llwip6-1460-feat +wifi_kit_8.menu.ip.hb6f.build.lwip_flags=-DLWIP_OPEN_SRC -DTCP_MSS=1460 -DLWIP_FEATURES=1 -DLWIP_IPV6=1 +wifi_kit_8.menu.dbg.Disabled=Disabled +wifi_kit_8.menu.dbg.Disabled.build.debug_port= +wifi_kit_8.menu.dbg.Serial=Serial +wifi_kit_8.menu.dbg.Serial.build.debug_port=-DDEBUG_ESP_PORT=Serial +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 +wifi_kit_8.menu.lvl.TLS_MEM.build.debug_level= -DDEBUG_ESP_TLS_MEM +wifi_kit_8.menu.lvl.HTTP_CLIENT=HTTP_CLIENT +wifi_kit_8.menu.lvl.HTTP_CLIENT.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT +wifi_kit_8.menu.lvl.HTTP_SERVER=HTTP_SERVER +wifi_kit_8.menu.lvl.HTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_SERVER +wifi_kit_8.menu.lvl.SSLTLS_MEM=SSL+TLS_MEM +wifi_kit_8.menu.lvl.SSLTLS_MEM.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM +wifi_kit_8.menu.lvl.SSLHTTP_CLIENT=SSL+HTTP_CLIENT +wifi_kit_8.menu.lvl.SSLHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT +wifi_kit_8.menu.lvl.SSLHTTP_SERVER=SSL+HTTP_SERVER +wifi_kit_8.menu.lvl.SSLHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_SERVER +wifi_kit_8.menu.lvl.TLS_MEMHTTP_CLIENT=TLS_MEM+HTTP_CLIENT +wifi_kit_8.menu.lvl.TLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +wifi_kit_8.menu.lvl.TLS_MEMHTTP_SERVER=TLS_MEM+HTTP_SERVER +wifi_kit_8.menu.lvl.TLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +wifi_kit_8.menu.lvl.HTTP_CLIENTHTTP_SERVER=HTTP_CLIENT+HTTP_SERVER +wifi_kit_8.menu.lvl.HTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wifi_kit_8.menu.lvl.SSLTLS_MEMHTTP_CLIENT=SSL+TLS_MEM+HTTP_CLIENT +wifi_kit_8.menu.lvl.SSLTLS_MEMHTTP_CLIENT.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT +wifi_kit_8.menu.lvl.SSLTLS_MEMHTTP_SERVER=SSL+TLS_MEM+HTTP_SERVER +wifi_kit_8.menu.lvl.SSLTLS_MEMHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_SERVER +wifi_kit_8.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER=SSL+HTTP_CLIENT+HTTP_SERVER +wifi_kit_8.menu.lvl.SSLHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wifi_kit_8.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER=TLS_MEM+HTTP_CLIENT+HTTP_SERVER +wifi_kit_8.menu.lvl.TLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wifi_kit_8.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER +wifi_kit_8.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVER.build.debug_level= -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER +wifi_kit_8.menu.lvl.CORE=CORE +wifi_kit_8.menu.lvl.CORE.build.debug_level= -DDEBUG_ESP_CORE +wifi_kit_8.menu.lvl.WIFI=WIFI +wifi_kit_8.menu.lvl.WIFI.build.debug_level= -DDEBUG_ESP_WIFI +wifi_kit_8.menu.lvl.HTTP_UPDATE=HTTP_UPDATE +wifi_kit_8.menu.lvl.HTTP_UPDATE.build.debug_level= -DDEBUG_ESP_HTTP_UPDATE +wifi_kit_8.menu.lvl.UPDATER=UPDATER +wifi_kit_8.menu.lvl.UPDATER.build.debug_level= -DDEBUG_ESP_UPDATER +wifi_kit_8.menu.lvl.OTA=OTA +wifi_kit_8.menu.lvl.OTA.build.debug_level= -DDEBUG_ESP_OTA +wifi_kit_8.menu.lvl.OOM=OOM +wifi_kit_8.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM +wifi_kit_8.menu.lvl.MDNS=MDNS +wifi_kit_8.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +wifi_kit_8.menu.lvl.HWDT=HWDT +wifi_kit_8.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +wifi_kit_8.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +wifi_kit_8.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K +wifi_kit_8.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +wifi_kit_8.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 +wifi_kit_8.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +wifi_kit_8.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 +wifi_kit_8.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +wifi_kit_8.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 +wifi_kit_8.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS +wifi_kit_8.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 +wifi_kit_8.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +wifi_kit_8.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 +wifi_kit_8.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 +wifi_kit_8.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 +wifi_kit_8.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG +wifi_kit_8.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG +wifi_kit_8.menu.wipe.none=Only Sketch +wifi_kit_8.menu.wipe.none.upload.erase_cmd= +wifi_kit_8.menu.wipe.sdk=Sketch + WiFi Settings +wifi_kit_8.menu.wipe.sdk.upload.erase_cmd=erase_region "{build.rfcal_addr}" 0x4000 +wifi_kit_8.menu.wipe.all=All Flash Contents +wifi_kit_8.menu.wipe.all.upload.erase_cmd=erase_flash +wifi_kit_8.menu.baud.115200=115200 +wifi_kit_8.menu.baud.115200.upload.speed=115200 +wifi_kit_8.menu.baud.57600=57600 +wifi_kit_8.menu.baud.57600.upload.speed=57600 +wifi_kit_8.menu.baud.230400.linux=230400 +wifi_kit_8.menu.baud.230400.macosx=230400 +wifi_kit_8.menu.baud.230400.upload.speed=230400 +wifi_kit_8.menu.baud.256000.windows=256000 +wifi_kit_8.menu.baud.256000.upload.speed=256000 +wifi_kit_8.menu.baud.460800.linux=460800 +wifi_kit_8.menu.baud.460800.macosx=460800 +wifi_kit_8.menu.baud.460800.upload.speed=460800 +wifi_kit_8.menu.baud.512000.windows=512000 +wifi_kit_8.menu.baud.512000.upload.speed=512000 +wifi_kit_8.menu.baud.921600=921600 +wifi_kit_8.menu.baud.921600.upload.speed=921600 +wifi_kit_8.menu.baud.3000000=3000000 +wifi_kit_8.menu.baud.3000000.upload.speed=3000000 +wifi_kit_8.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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 @@ -7122,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 @@ -7148,10 +8848,6 @@ wifiduino.menu.ssl.all=All SSL ciphers (most compatible) wifiduino.menu.ssl.all.build.sslflags= wifiduino.menu.ssl.basic=Basic SSL ciphers (lower ROM use) wifiduino.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -wifiduino.menu.waveform.pwm=Locked PWM -wifiduino.menu.waveform.pwm.build.waveform= -wifiduino.menu.waveform.phase=Locked Phase -wifiduino.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE wifiduino.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) wifiduino.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 wifiduino.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -7160,6 +8856,10 @@ wifiduino.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) wifiduino.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP wifiduino.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) wifiduino.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +wifiduino.menu.mmu.ext128k=128K Heap External 23LC1024 +wifiduino.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +wifiduino.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +wifiduino.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 wifiduino.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM wifiduino.menu.non32xfer.fast.build.non32xferflags= wifiduino.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -7170,40 +8870,32 @@ wifiduino.build.flash_flags=-DFLASHMODE_DIO wifiduino.build.flash_freq=40 wifiduino.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) wifiduino.menu.eesz.4M2M.build.flash_size=4M -wifiduino.menu.eesz.4M2M.build.flash_size_bytes=0x400000 wifiduino.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld wifiduino.menu.eesz.4M2M.build.spiffs_pagesize=256 -wifiduino.menu.eesz.4M2M.upload.maximum_size=1044464 wifiduino.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 wifiduino.menu.eesz.4M2M.build.spiffs_start=0x200000 wifiduino.menu.eesz.4M2M.build.spiffs_end=0x3FA000 wifiduino.menu.eesz.4M2M.build.spiffs_blocksize=8192 wifiduino.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) wifiduino.menu.eesz.4M3M.build.flash_size=4M -wifiduino.menu.eesz.4M3M.build.flash_size_bytes=0x400000 wifiduino.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld wifiduino.menu.eesz.4M3M.build.spiffs_pagesize=256 -wifiduino.menu.eesz.4M3M.upload.maximum_size=1044464 wifiduino.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 wifiduino.menu.eesz.4M3M.build.spiffs_start=0x100000 wifiduino.menu.eesz.4M3M.build.spiffs_end=0x3FA000 wifiduino.menu.eesz.4M3M.build.spiffs_blocksize=8192 wifiduino.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) wifiduino.menu.eesz.4M1M.build.flash_size=4M -wifiduino.menu.eesz.4M1M.build.flash_size_bytes=0x400000 wifiduino.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld wifiduino.menu.eesz.4M1M.build.spiffs_pagesize=256 -wifiduino.menu.eesz.4M1M.upload.maximum_size=1044464 wifiduino.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 wifiduino.menu.eesz.4M1M.build.spiffs_start=0x300000 wifiduino.menu.eesz.4M1M.build.spiffs_end=0x3FA000 wifiduino.menu.eesz.4M1M.build.spiffs_blocksize=8192 wifiduino.menu.eesz.4M=4MB (FS:none OTA:~1019KB) wifiduino.menu.eesz.4M.build.flash_size=4M -wifiduino.menu.eesz.4M.build.flash_size_bytes=0x400000 wifiduino.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld wifiduino.menu.eesz.4M.build.spiffs_pagesize=256 -wifiduino.menu.eesz.4M.upload.maximum_size=1044464 wifiduino.menu.eesz.4M.build.rfcal_addr=0x3FC000 wifiduino.menu.ip.lm2f=v2 Lower Memory wifiduino.menu.ip.lm2f.build.lwip_include=lwip2/include @@ -7237,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 @@ -7281,10 +8979,22 @@ wifiduino.menu.lvl.OOM=OOM wifiduino.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM wifiduino.menu.lvl.MDNS=MDNS wifiduino.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +wifiduino.menu.lvl.HWDT=HWDT +wifiduino.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +wifiduino.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +wifiduino.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K wifiduino.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS wifiduino.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 +wifiduino.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +wifiduino.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 +wifiduino.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +wifiduino.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 wifiduino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS wifiduino.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 +wifiduino.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +wifiduino.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 +wifiduino.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 +wifiduino.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 wifiduino.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG wifiduino.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG wifiduino.menu.wipe.none=Only Sketch @@ -7311,6 +9021,15 @@ wifiduino.menu.baud.512000.windows=512000 wifiduino.menu.baud.512000.upload.speed=512000 wifiduino.menu.baud.3000000=3000000 wifiduino.menu.baud.3000000.upload.speed=3000000 +wifiduino.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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 @@ -7342,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 @@ -7368,10 +9088,6 @@ wifinfo.menu.ssl.all=All SSL ciphers (most compatible) wifinfo.menu.ssl.all.build.sslflags= wifinfo.menu.ssl.basic=Basic SSL ciphers (lower ROM use) wifinfo.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -wifinfo.menu.waveform.pwm=Locked PWM -wifinfo.menu.waveform.pwm.build.waveform= -wifinfo.menu.waveform.phase=Locked Phase -wifinfo.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE wifinfo.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) wifinfo.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 wifinfo.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -7380,6 +9096,10 @@ wifinfo.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) wifinfo.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP wifinfo.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) wifinfo.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +wifinfo.menu.mmu.ext128k=128K Heap External 23LC1024 +wifinfo.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +wifinfo.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +wifinfo.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 wifinfo.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM wifinfo.menu.non32xfer.fast.build.non32xferflags= wifinfo.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -7397,80 +9117,64 @@ wifinfo.menu.FlashFreq.26=26MHz wifinfo.menu.FlashFreq.26.build.flash_freq=26 wifinfo.menu.eesz.1M64=1MB (FS:64KB OTA:~470KB) wifinfo.menu.eesz.1M64.build.flash_size=1M -wifinfo.menu.eesz.1M64.build.flash_size_bytes=0x100000 wifinfo.menu.eesz.1M64.build.flash_ld=eagle.flash.1m64.ld wifinfo.menu.eesz.1M64.build.spiffs_pagesize=256 -wifinfo.menu.eesz.1M64.upload.maximum_size=958448 wifinfo.menu.eesz.1M64.build.rfcal_addr=0xFC000 wifinfo.menu.eesz.1M64.build.spiffs_start=0xEB000 wifinfo.menu.eesz.1M64.build.spiffs_end=0xFB000 wifinfo.menu.eesz.1M64.build.spiffs_blocksize=4096 wifinfo.menu.eesz.1M128=1MB (FS:128KB OTA:~438KB) wifinfo.menu.eesz.1M128.build.flash_size=1M -wifinfo.menu.eesz.1M128.build.flash_size_bytes=0x100000 wifinfo.menu.eesz.1M128.build.flash_ld=eagle.flash.1m128.ld wifinfo.menu.eesz.1M128.build.spiffs_pagesize=256 -wifinfo.menu.eesz.1M128.upload.maximum_size=892912 wifinfo.menu.eesz.1M128.build.rfcal_addr=0xFC000 wifinfo.menu.eesz.1M128.build.spiffs_start=0xDB000 wifinfo.menu.eesz.1M128.build.spiffs_end=0xFB000 wifinfo.menu.eesz.1M128.build.spiffs_blocksize=4096 wifinfo.menu.eesz.1M144=1MB (FS:144KB OTA:~430KB) wifinfo.menu.eesz.1M144.build.flash_size=1M -wifinfo.menu.eesz.1M144.build.flash_size_bytes=0x100000 wifinfo.menu.eesz.1M144.build.flash_ld=eagle.flash.1m144.ld wifinfo.menu.eesz.1M144.build.spiffs_pagesize=256 -wifinfo.menu.eesz.1M144.upload.maximum_size=876528 wifinfo.menu.eesz.1M144.build.rfcal_addr=0xFC000 wifinfo.menu.eesz.1M144.build.spiffs_start=0xD7000 wifinfo.menu.eesz.1M144.build.spiffs_end=0xFB000 wifinfo.menu.eesz.1M144.build.spiffs_blocksize=4096 wifinfo.menu.eesz.1M160=1MB (FS:160KB OTA:~422KB) wifinfo.menu.eesz.1M160.build.flash_size=1M -wifinfo.menu.eesz.1M160.build.flash_size_bytes=0x100000 wifinfo.menu.eesz.1M160.build.flash_ld=eagle.flash.1m160.ld wifinfo.menu.eesz.1M160.build.spiffs_pagesize=256 -wifinfo.menu.eesz.1M160.upload.maximum_size=860144 wifinfo.menu.eesz.1M160.build.rfcal_addr=0xFC000 wifinfo.menu.eesz.1M160.build.spiffs_start=0xD3000 wifinfo.menu.eesz.1M160.build.spiffs_end=0xFB000 wifinfo.menu.eesz.1M160.build.spiffs_blocksize=4096 wifinfo.menu.eesz.1M192=1MB (FS:192KB OTA:~406KB) wifinfo.menu.eesz.1M192.build.flash_size=1M -wifinfo.menu.eesz.1M192.build.flash_size_bytes=0x100000 wifinfo.menu.eesz.1M192.build.flash_ld=eagle.flash.1m192.ld wifinfo.menu.eesz.1M192.build.spiffs_pagesize=256 -wifinfo.menu.eesz.1M192.upload.maximum_size=827376 wifinfo.menu.eesz.1M192.build.rfcal_addr=0xFC000 wifinfo.menu.eesz.1M192.build.spiffs_start=0xCB000 wifinfo.menu.eesz.1M192.build.spiffs_end=0xFB000 wifinfo.menu.eesz.1M192.build.spiffs_blocksize=4096 wifinfo.menu.eesz.1M256=1MB (FS:256KB OTA:~374KB) wifinfo.menu.eesz.1M256.build.flash_size=1M -wifinfo.menu.eesz.1M256.build.flash_size_bytes=0x100000 wifinfo.menu.eesz.1M256.build.flash_ld=eagle.flash.1m256.ld wifinfo.menu.eesz.1M256.build.spiffs_pagesize=256 -wifinfo.menu.eesz.1M256.upload.maximum_size=761840 wifinfo.menu.eesz.1M256.build.rfcal_addr=0xFC000 wifinfo.menu.eesz.1M256.build.spiffs_start=0xBB000 wifinfo.menu.eesz.1M256.build.spiffs_end=0xFB000 wifinfo.menu.eesz.1M256.build.spiffs_blocksize=4096 wifinfo.menu.eesz.1M512=1MB (FS:512KB OTA:~246KB) wifinfo.menu.eesz.1M512.build.flash_size=1M -wifinfo.menu.eesz.1M512.build.flash_size_bytes=0x100000 wifinfo.menu.eesz.1M512.build.flash_ld=eagle.flash.1m512.ld wifinfo.menu.eesz.1M512.build.spiffs_pagesize=256 -wifinfo.menu.eesz.1M512.upload.maximum_size=499696 wifinfo.menu.eesz.1M512.build.rfcal_addr=0xFC000 wifinfo.menu.eesz.1M512.build.spiffs_start=0x7B000 wifinfo.menu.eesz.1M512.build.spiffs_end=0xFB000 wifinfo.menu.eesz.1M512.build.spiffs_blocksize=8192 wifinfo.menu.eesz.1M=1MB (FS:none OTA:~502KB) wifinfo.menu.eesz.1M.build.flash_size=1M -wifinfo.menu.eesz.1M.build.flash_size_bytes=0x100000 wifinfo.menu.eesz.1M.build.flash_ld=eagle.flash.1m.ld wifinfo.menu.eesz.1M.build.spiffs_pagesize=256 -wifinfo.menu.eesz.1M.upload.maximum_size=1023984 wifinfo.menu.eesz.1M.build.rfcal_addr=0xFC000 wifinfo.menu.ip.lm2f=v2 Lower Memory wifinfo.menu.ip.lm2f.build.lwip_include=lwip2/include @@ -7504,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 @@ -7548,10 +9258,22 @@ wifinfo.menu.lvl.OOM=OOM wifinfo.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM wifinfo.menu.lvl.MDNS=MDNS wifinfo.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +wifinfo.menu.lvl.HWDT=HWDT +wifinfo.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +wifinfo.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +wifinfo.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K wifinfo.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS wifinfo.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 +wifinfo.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +wifinfo.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 +wifinfo.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +wifinfo.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 wifinfo.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS wifinfo.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 +wifinfo.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +wifinfo.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 +wifinfo.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 +wifinfo.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 wifinfo.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG wifinfo.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG wifinfo.menu.wipe.none=Only Sketch @@ -7578,10 +9300,19 @@ wifinfo.menu.baud.921600=921600 wifinfo.menu.baud.921600.upload.speed=921600 wifinfo.menu.baud.3000000=3000000 wifinfo.menu.baud.3000000.upload.speed=3000000 +wifinfo.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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 -cw01.build.board=ESP8266_GENERIC +cw01.build.board=ESP8266_XINABOX_CW01 cw01.build.variant=xinabox cw01.upload.tool=esptool cw01.upload.maximum_data_size=81920 @@ -7592,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 @@ -7618,10 +9350,6 @@ cw01.menu.ssl.all=All SSL ciphers (most compatible) cw01.menu.ssl.all.build.sslflags= cw01.menu.ssl.basic=Basic SSL ciphers (lower ROM use) cw01.menu.ssl.basic.build.sslflags=-DBEARSSL_SSL_BASIC -cw01.menu.waveform.pwm=Locked PWM -cw01.menu.waveform.pwm.build.waveform= -cw01.menu.waveform.phase=Locked Phase -cw01.menu.waveform.phase.build.waveform=-DWAVEFORM_LOCKED_PHASE cw01.menu.mmu.3232=32KB cache + 32KB IRAM (balanced) cw01.menu.mmu.3232.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 cw01.menu.mmu.4816=16KB cache + 48KB IRAM (IRAM) @@ -7630,6 +9358,10 @@ cw01.menu.mmu.4816H=16KB cache + 48KB IRAM and 2nd Heap (shared) cw01.menu.mmu.4816H.build.mmuflags=-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP cw01.menu.mmu.3216=16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared) cw01.menu.mmu.3216.build.mmuflags=-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000 +cw01.menu.mmu.ext128k=128K Heap External 23LC1024 +cw01.menu.mmu.ext128k.build.mmuflags=-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 +cw01.menu.mmu.ext8192k=8M w/256K Heap External 64 MBit PSRAM +cw01.menu.mmu.ext8192k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000 cw01.menu.non32xfer.fast=Use pgm_read macros for IRAM/PROGMEM cw01.menu.non32xfer.fast.build.non32xferflags= cw01.menu.non32xfer.safe=Byte/Word access to IRAM/PROGMEM (very slow) @@ -7637,46 +9369,38 @@ cw01.menu.non32xfer.safe.build.non32xferflags=-DNON32XFER_HANDLER cw01.upload.resetmethod=--before default_reset --after hard_reset cw01.menu.CrystalFreq.26=26 MHz cw01.menu.CrystalFreq.40=40 MHz -cw01.menu.CrystalFreq.40.build.extra_flags=-DF_CRYSTAL=40000000 -DESP8266 +cw01.menu.CrystalFreq.40.build.extra_flags=-DF_CRYSTAL=40000000 cw01.build.flash_mode=dio cw01.build.flash_flags=-DFLASHMODE_DIO cw01.build.flash_freq=40 cw01.menu.eesz.4M2M=4MB (FS:2MB OTA:~1019KB) cw01.menu.eesz.4M2M.build.flash_size=4M -cw01.menu.eesz.4M2M.build.flash_size_bytes=0x400000 cw01.menu.eesz.4M2M.build.flash_ld=eagle.flash.4m2m.ld cw01.menu.eesz.4M2M.build.spiffs_pagesize=256 -cw01.menu.eesz.4M2M.upload.maximum_size=1044464 cw01.menu.eesz.4M2M.build.rfcal_addr=0x3FC000 cw01.menu.eesz.4M2M.build.spiffs_start=0x200000 cw01.menu.eesz.4M2M.build.spiffs_end=0x3FA000 cw01.menu.eesz.4M2M.build.spiffs_blocksize=8192 cw01.menu.eesz.4M3M=4MB (FS:3MB OTA:~512KB) cw01.menu.eesz.4M3M.build.flash_size=4M -cw01.menu.eesz.4M3M.build.flash_size_bytes=0x400000 cw01.menu.eesz.4M3M.build.flash_ld=eagle.flash.4m3m.ld cw01.menu.eesz.4M3M.build.spiffs_pagesize=256 -cw01.menu.eesz.4M3M.upload.maximum_size=1044464 cw01.menu.eesz.4M3M.build.rfcal_addr=0x3FC000 cw01.menu.eesz.4M3M.build.spiffs_start=0x100000 cw01.menu.eesz.4M3M.build.spiffs_end=0x3FA000 cw01.menu.eesz.4M3M.build.spiffs_blocksize=8192 cw01.menu.eesz.4M1M=4MB (FS:1MB OTA:~1019KB) cw01.menu.eesz.4M1M.build.flash_size=4M -cw01.menu.eesz.4M1M.build.flash_size_bytes=0x400000 cw01.menu.eesz.4M1M.build.flash_ld=eagle.flash.4m1m.ld cw01.menu.eesz.4M1M.build.spiffs_pagesize=256 -cw01.menu.eesz.4M1M.upload.maximum_size=1044464 cw01.menu.eesz.4M1M.build.rfcal_addr=0x3FC000 cw01.menu.eesz.4M1M.build.spiffs_start=0x300000 cw01.menu.eesz.4M1M.build.spiffs_end=0x3FA000 cw01.menu.eesz.4M1M.build.spiffs_blocksize=8192 cw01.menu.eesz.4M=4MB (FS:none OTA:~1019KB) cw01.menu.eesz.4M.build.flash_size=4M -cw01.menu.eesz.4M.build.flash_size_bytes=0x400000 cw01.menu.eesz.4M.build.flash_ld=eagle.flash.4m.ld cw01.menu.eesz.4M.build.spiffs_pagesize=256 -cw01.menu.eesz.4M.upload.maximum_size=1044464 cw01.menu.eesz.4M.build.rfcal_addr=0x3FC000 cw01.menu.ip.lm2f=v2 Lower Memory cw01.menu.ip.lm2f.build.lwip_include=lwip2/include @@ -7710,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 @@ -7754,10 +9484,22 @@ cw01.menu.lvl.OOM=OOM cw01.menu.lvl.OOM.build.debug_level= -DDEBUG_ESP_OOM cw01.menu.lvl.MDNS=MDNS cw01.menu.lvl.MDNS.build.debug_level= -DDEBUG_ESP_MDNS +cw01.menu.lvl.HWDT=HWDT +cw01.menu.lvl.HWDT.build.debug_level= -DDEBUG_ESP_HWDT +cw01.menu.lvl.HWDT_NOEXTRA4K=HWDT_NOEXTRA4K +cw01.menu.lvl.HWDT_NOEXTRA4K.build.debug_level= -DDEBUG_ESP_HWDT_NOEXTRA4K cw01.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS cw01.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 +cw01.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +cw01.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 +cw01.menu.lvl.COREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT_NOEXTRA4K=CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT_NOEXTRA4K +cw01.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 cw01.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNS=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS cw01.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 +cw01.menu.lvl.SSLTLS_MEMHTTP_CLIENTHTTP_SERVERCOREWIFIHTTP_UPDATEUPDATEROTAOOMMDNSHWDT=SSL+TLS_MEM+HTTP_CLIENT+HTTP_SERVER+CORE+WIFI+HTTP_UPDATE+UPDATER+OTA+OOM+MDNS+HWDT +cw01.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 +cw01.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 +cw01.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 cw01.menu.lvl.NoAssert-NDEBUG=NoAssert-NDEBUG cw01.menu.lvl.NoAssert-NDEBUG.build.debug_level= -DNDEBUG cw01.menu.wipe.none=Only Sketch @@ -7784,4 +9526,13 @@ cw01.menu.baud.921600=921600 cw01.menu.baud.921600.upload.speed=921600 cw01.menu.baud.3000000=3000000 cw01.menu.baud.3000000.upload.speed=3000000 +cw01.menu.eesz.autoflash=Mapping defined by Hardware and Sketch +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/Makefile b/bootloaders/eboot/Makefile index 7f035dd1e7..3eb9fc58f4 100644 --- a/bootloaders/eboot/Makefile +++ b/bootloaders/eboot/Makefile @@ -21,7 +21,7 @@ OBJDUMP := $(XTENSA_TOOLCHAIN)xtensa-lx106-elf-objdump INC += -I../../tools/sdk/include -I../../tools/sdk/uzlib/src -CFLAGS += -std=gnu99 +CFLAGS += -std=gnu17 CFLAGS += -Os -fcommon -g -Wall -Wpointer-arith -Wno-implicit-function-declaration -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mno-text-section-literals -ffunction-sections -fdata-sections -free -fipa-pta @@ -40,17 +40,17 @@ APP_FW := eboot.bin all: $(APP_OUT) -tinflate.o: $(UZLIB_PATH)/tinflate.c $(UZLIB_PATH)/uzlib.h $(UZLIB_PATH)/uzlib_conf.h +tinflate.o: $(UZLIB_PATH)/tinflate.c $(UZLIB_PATH)/uzlib.h $(UZLIB_PATH)/uzlib_conf.h Makefile $(CC) $(CFLAGS) -c -o tinflate.o $(UZLIB_PATH)/tinflate.c -tinfgzip.o: $(UZLIB_PATH)/tinfgzip.c $(UZLIB_PATH)/uzlib.h $(UZLIB_PATH)/uzlib_conf.h +tinfgzip.o: $(UZLIB_PATH)/tinfgzip.c $(UZLIB_PATH)/uzlib.h $(UZLIB_PATH)/uzlib_conf.h Makefile $(CC) $(CFLAGS) -c -o tinfgzip.o $(UZLIB_PATH)/tinfgzip.c -$(APP_AR): $(TARGET_OBJ_PATHS) tinflate.o tinfgzip.o +$(APP_AR): $(TARGET_OBJ_PATHS) tinflate.o tinfgzip.o Makefile $(AR) cru $@ $^ $(APP_OUT): $(APP_AR) eboot.ld | Makefile - $(LD) $(LD_SCRIPT) $(LDFLAGS) -Wl,--start-group -Wl,--whole-archive $(APP_AR) -Wl,--end-group -o $@ + $(LD) $(LD_SCRIPT) $(LDFLAGS) -Wl,--start-group -Wl,--sort-common $(APP_AR) -Wl,--end-group -o $@ clean: rm -f *.o diff --git a/bootloaders/eboot/eboot.c b/bootloaders/eboot/eboot.c index 6e188d806e..c3d0c278f7 100644 --- a/bootloaders/eboot/eboot.c +++ b/bootloaders/eboot/eboot.c @@ -14,15 +14,42 @@ #include "eboot_command.h" #include -extern unsigned char _gzip_dict; #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. -// Converts bit of a string into a uint32 -#define S(a,b,c,d) ( (((uint32_t)a) & 0xff) | (((uint32_t)b) << 8) | (((uint32_t)c) << 16) | (((uint32_t)d)<<24) ) + 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) { @@ -30,12 +57,7 @@ int print_version(const uint32_t flash_addr) if (SPIRead(flash_addr + APP_START_OFFSET + sizeof(image_header_t) + sizeof(section_header_t), &ver, sizeof(ver))) { return 1; } - // We don't have BSS and can't print from flash, so build up string - // 4 chars at a time. Smaller code than byte-wise assignment. - uint32_t fmt[2]; - fmt[0] = S('v', '%', '0', '8'); - fmt[1] = S('x', '\n', 0, 0); - ets_printf((const char*) fmt, ver); + ets_printf("v%08x\n", ver); return 0; } @@ -222,6 +244,16 @@ int main() bool clear_cmd = false; struct eboot_command cmd; +// BSS init commented out for now to save space. If any static variables set +// to 0 are used, need to uncomment it or else the BSS will not be cleared and +// the static vars will power on with random values. +#if 0 + // Clear BSS ourselves, we don't have handy C runtime + extern char _bss_start; + extern char _bss_end; + ets_bzero(&_bss_start, &_bss_end - &_bss_start); +#endif + print_version(0); if (eboot_command_read(&cmd) == 0) { @@ -236,33 +268,27 @@ int main() } if (cmd.action == ACTION_COPY_RAW) { - uint32_t cp = S('c', 'p', ':', 0); - ets_printf((const char *)&cp); + ets_printf("cp:"); 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. - cp = S('0' + res, '\n', 0, 0 ); - ets_printf((const char *)&cp); + 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 - uint32_t v[2]; - v[0] = S('c', 'm', 'p', ':'); - v[1] = 0; - ets_printf(const char *)v); + ets_printf("cmp:"); if (res == 0) { ets_wdt_disable(); res = copy_raw(cmd.args[0], cmd.args[1], cmd.args[2], true); ets_wdt_enable(); } - cp = S('0' + res, '\n', 0, 0 ); - ets_printf((const char *)&cp); -#endif + ets_printf("%d\n", res); +#endif if (res == 0) { cmd.action = ACTION_LOAD_APP; cmd.args[0] = cmd.args[1]; @@ -274,13 +300,10 @@ int main() } if (cmd.action == ACTION_LOAD_APP) { - ets_putc('l'); ets_putc('d'); ets_putc('\n'); + ets_printf("ld\n"); res = load_app_from_flash_raw(cmd.args[0]); // We will get to this only on load fail - uint32_t e[2]; - e[0] = S('e', ':', '0' + res, '\n' ); - e[1] = 0; - ets_printf((const char*)e); + ets_printf("e:%d\n", res); } if (res) { diff --git a/bootloaders/eboot/eboot.elf b/bootloaders/eboot/eboot.elf index 7155fd88e0..0e679ff46c 100755 Binary files a/bootloaders/eboot/eboot.elf and b/bootloaders/eboot/eboot.elf differ diff --git a/bootloaders/eboot/eboot.ld b/bootloaders/eboot/eboot.ld index c664daf660..3946412db2 100644 --- a/bootloaders/eboot/eboot.ld +++ b/bootloaders/eboot/eboot.ld @@ -42,53 +42,13 @@ PROVIDE(_memmap_cacheattr_reset = _memmap_cacheattr_wb_trapnull); SECTIONS { - .dport0.rodata : ALIGN(4) - { - _dport0_rodata_start = ABSOLUTE(.); - *(.dport0.rodata) - *(.dport.rodata) - _dport0_rodata_end = ABSOLUTE(.); - } >dport0_0_seg :dport0_0_phdr - - .dport0.literal : ALIGN(4) - { - _dport0_literal_start = ABSOLUTE(.); - *(.dport0.literal) - *(.dport.literal) - _dport0_literal_end = ABSOLUTE(.); - } >dport0_0_seg :dport0_0_phdr - - .dport0.data : ALIGN(4) - { - _dport0_data_start = ABSOLUTE(.); - *(.dport0.data) - *(.dport.data) - _dport0_data_end = ABSOLUTE(.); - } >dport0_0_seg :dport0_0_phdr - - .data : ALIGN(4) + .globals : ALIGN(4) { *(COMMON) /* Global vars */ - . = ALIGN(4); - _heap_start = ABSOLUTE(.); -/* _stack_sentry = ALIGN(0x8); */ } >dram0_0_seg :dram0_0_bss_phdr -/* __stack = 0x3ffc8000; */ - .text : ALIGN(4) + .data : ALIGN(4) { - _stext = .; - _text_start = ABSOLUTE(.); - *(.entry.text) - *(.init.literal) - *(.init) - *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) - *(.fini.literal) - *(.fini) - *(.gnu.version) - _text_end = ABSOLUTE(.); - _etext = .; - . = ALIGN (8); _data_start = ABSOLUTE(.); *(.data) *(.data.*) @@ -102,7 +62,10 @@ SECTIONS *(.gnu.linkonce.s2.*) *(.jcr) _data_end = ABSOLUTE(.); - . = ALIGN (8); + } >dram0_0_seg :dram0_0_bss_phdr + + .rodata : ALIGN(4) + { _rodata_start = ABSOLUTE(.); *(.rodata) *(.rodata.*) @@ -131,14 +94,11 @@ SECTIONS *(.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_bss_phdr - . = ALIGN (8); + .bss : ALIGN(4) + { _bss_start = ABSOLUTE(.); *(.dynsbss) *(.sbss) @@ -152,26 +112,24 @@ SECTIONS *(.bss) *(.bss.*) *(.gnu.linkonce.b.*) - . = ALIGN (8); _bss_end = ABSOLUTE(.); - _free_space = 4096 - 17 - (. - _stext); -/* -The boot loader checksum must be before the CRC, which is written by elf2bin.py. -This leaves 16 bytes after the checksum for the CRC placed at the end of the -4096-byte sector. */ - _cs_here = (ALIGN((. + 1), 16) == ALIGN(16)) ? (ALIGN(16) - 1) : (. + 0x0F); + } >dram0_0_seg :dram0_0_bss_phdr -/* -The filling (padding) and values for _crc_size and _crc_val are handled by -elf2bin.py. With this, we give values to the symbols without explicitly -assigning space. This avoids the linkers back *fill* operation that causes -trouble. -The CRC info is stored in last 8 bytes. */ - _crc_size = _stext + 4096 - 8; - _crc_val = _stext + 4096 - 4; - ASSERT((4096 > (17 + (. - _stext))), "Error: No space for CS and CRC in bootloader sector."); - ASSERT((_crc_size > _cs_here), "Error: CRC must be located after CS."); + .text : ALIGN(4) + { + _stext = .; + _text_start = ABSOLUTE(.); + *(.entry.text) + *(.init.literal) + *(.init) + *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.fini.literal) + *(.fini) + *(.gnu.version) + _text_end = ABSOLUTE(.); + _etext = .; + . = ALIGN (4); /* Ensure 32b alignment since this is written to IRAM */ } >iram1_0_seg :iram1_0_phdr .lit4 : ALIGN(4) diff --git a/cores/esp8266/Arduino.h b/cores/esp8266/Arduino.h index b484a3765a..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" @@ -171,6 +172,7 @@ int digitalRead(uint8_t pin); int analogRead(uint8_t pin); void analogReference(uint8_t mode); void analogWrite(uint8_t pin, int val); +void analogWriteMode(uint8_t pin, int val, bool openDrain); void analogWriteFreq(uint32_t freq); void analogWriteResolution(int res); void analogWriteRange(uint32_t range); @@ -185,6 +187,9 @@ void attachInterrupt(uint8_t pin, void (*)(void), int mode); void detachInterrupt(uint8_t pin); void attachInterruptArg(uint8_t pin, void (*)(void*), void* arg, int mode); +#if FLASH_MAP_SUPPORT +#include "flash_hal.h" +#endif void preinit(void); void setup(void); void loop(void); @@ -265,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 @@ -278,12 +286,20 @@ inline void configTzTime(const char* tz, const char* server1, configTime(tz, server1, server2, server3); } +bool getLocalTime(struct tm * info, uint32_t ms = 5000); + // Everything we expect to be implicitly loaded for the sketch #include #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" @@ -296,7 +312,8 @@ inline void configTzTime(const char* tz, const char* server1, #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/CallBackList.h b/cores/esp8266/CallBackList.h index 8d419cbb89..1df590e00e 100644 --- a/cores/esp8266/CallBackList.h +++ b/cores/esp8266/CallBackList.h @@ -1,9 +1,8 @@ #ifndef __CALLBACKLIST_H__ #define __CALLBACKLIST_H__ - /* - CallBackList, An implemention for handling callback execution + CallBackList, An implementation for handling callback execution Copyright (c) 2019 Herman Reintke. All rights reserved. This file is part of the esp8266 core for Arduino environment. diff --git a/cores/esp8266/Client.h b/cores/esp8266/Client.h index ed0f0f8026..7f8f810458 100644 --- a/cores/esp8266/Client.h +++ b/cores/esp8266/Client.h @@ -26,15 +26,15 @@ class Client: public Stream { public: - virtual int connect(IPAddress ip, uint16_t port) =0; - virtual int connect(const char *host, uint16_t port) =0; - virtual size_t write(uint8_t) =0; - virtual size_t write(const uint8_t *buf, size_t size) =0; - virtual int available() = 0; - virtual int read() = 0; - virtual int read(uint8_t *buf, size_t size) = 0; - virtual int peek() = 0; - virtual void flush() = 0; + virtual int connect(IPAddress ip, uint16_t port) = 0; + virtual int connect(const char *host, uint16_t port) = 0; + virtual size_t write(uint8_t) override = 0; + virtual size_t write(const uint8_t *buf, size_t size) override = 0; + virtual int available() override = 0; + virtual int read() override = 0; + virtual int read(uint8_t *buf, size_t size) override = 0; + virtual int peek() override = 0; + virtual void flush() override = 0; virtual void stop() = 0; virtual uint8_t connected() = 0; virtual operator bool() = 0; diff --git a/cores/esp8266/Esp-frag.cpp b/cores/esp8266/Esp-frag.cpp index 30332aca62..9e4e9af6f1 100644 --- a/cores/esp8266/Esp-frag.cpp +++ b/cores/esp8266/Esp-frag.cpp @@ -19,23 +19,22 @@ */ #include "umm_malloc/umm_malloc.h" -#include "umm_malloc/umm_malloc_cfg.h" #include "coredecls.h" #include "Esp.h" -void EspClass::getHeapStats(uint32_t* hfree, uint16_t* hmax, uint8_t* hfrag) +#if defined(UMM_INFO) +void EspClass::getHeapStats(uint32_t* hfree, uint32_t* hmax, uint8_t* hfrag) { - // L2 / Euclidian norm of free block sizes. + // L2 / Euclidean norm of free block sizes. // Having getFreeHeap()=sum(hole-size), fragmentation is given by // 100 * (1 - sqrt(sum(hole-size²)) / sum(hole-size)) - umm_info(NULL, false); uint32_t free_size = umm_free_heap_size_core(umm_get_current_heap()); if (hfree) *hfree = free_size; if (hmax) - *hmax = (uint16_t)umm_max_block_size_core(umm_get_current_heap()); + *hmax = umm_max_block_size_core(umm_get_current_heap()); if (hfrag) { if (free_size) { *hfrag = umm_fragmentation_metric_core(umm_get_current_heap()); @@ -45,7 +44,20 @@ void EspClass::getHeapStats(uint32_t* hfree, uint16_t* hmax, uint8_t* hfrag) } } +void EspClass::getHeapStats(uint32_t* hfree, uint16_t* hmax, uint8_t* hfrag) +{ + uint32_t hmax32; + getHeapStats(hfree, &hmax32, hfrag); + if (hmax) { + // With the MMU_EXTERNAL_HEAP option, hmax could overflow for heaps larger + // then 64KB. return UINT16_MAX (saturation) for those cases. + // Added deprecated attribute and message. + *hmax = (hmax32 > UINT16_MAX) ? UINT16_MAX : hmax32; + } +} + uint8_t EspClass::getHeapFragmentation() { return (uint8_t)umm_fragmentation_metric(); } +#endif diff --git a/cores/esp8266/Esp.cpp b/cores/esp8266/Esp.cpp index 51bdfe8e12..67719dcfe7 100644 --- a/cores/esp8266/Esp.cpp +++ b/cores/esp8266/Esp.cpp @@ -26,11 +26,12 @@ #include "MD5Builder.h" #include "umm_malloc/umm_malloc.h" #include "cont.h" - +#include "flash_hal.h" #include "coredecls.h" #include "umm_malloc/umm_malloc.h" -// #include "core_esp8266_vm.h" #include +#include "reboot_uart_dwnld.h" +#include "hardware_reset.h" extern "C" { #include "user_interface.h" @@ -115,20 +116,18 @@ void EspClass::wdtFeed(void) system_soft_wdt_feed(); } -extern "C" void esp_yield(); - void EspClass::deepSleep(uint64_t time_us, WakeMode mode) { system_deep_sleep_set_option(static_cast(mode)); system_deep_sleep(time_us); - esp_yield(); + esp_suspend(); } void EspClass::deepSleepInstant(uint64_t time_us, WakeMode mode) { system_deep_sleep_set_option(static_cast(mode)); system_deep_sleep_instant(time_us); - esp_yield(); + esp_suspend(); } //this calculation was taken verbatim from the SDK api reference for SDK 2.1.0. @@ -200,7 +199,16 @@ void EspClass::reset(void) void EspClass::restart(void) { system_restart(); - esp_yield(); + esp_suspend(); +} + +[[noreturn]] void EspClass::rebootIntoUartDownloadMode() +{ + wdtDisable(); + /* disable hardware watchdog */ + CLEAR_PERI_REG_MASK(PERIPHS_HW_WDT, 0x1); + + esp8266RebootIntoUartDownloadMode(); } uint16_t EspClass::getVcc(void) @@ -212,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(); } -uint16_t EspClass::getMaxFreeBlockSize(void) +#if defined(UMM_INFO) +uint32_t EspClass::getMaxFreeBlockSize(void) { return umm_max_block_size(); } +#endif uint32_t EspClass::getFreeContStack() { @@ -284,6 +294,9 @@ uint32_t EspClass::getFlashChipRealSize(void) uint32_t EspClass::getFlashChipSize(void) { +#if FLASH_MAP_SUPPORT + return getFlashChipRealSize(); +#else uint32_t data; uint8_t * bytes = (uint8_t *) &data; // read first 4 byte (magic byte + flash config) @@ -291,6 +304,7 @@ uint32_t EspClass::getFlashChipSize(void) return magicFlashChipSize((bytes[3] & 0xf0) >> 4); } return 0; +#endif } uint32_t EspClass::getFlashChipSpeed(void) @@ -316,6 +330,7 @@ FlashMode_t EspClass::getFlashChipMode(void) return mode; } +#if !FLASH_MAP_SUPPORT uint32_t EspClass::magicFlashChipSize(uint8_t byte) { switch(byte & 0x0F) { case 0x0: // 4 Mbit (512KB) @@ -336,6 +351,7 @@ uint32_t EspClass::magicFlashChipSize(uint8_t byte) { return 0; } } +#endif uint32_t EspClass::magicFlashChipSpeed(uint8_t byte) { switch(byte & 0x0F) { @@ -444,22 +460,24 @@ bool EspClass::checkFlashConfig(bool needsEquals) { return false; } +// These are defined in the linker script, and filled in by the elf2bin.py util +extern "C" uint32_t __crc_len; +extern "C" uint32_t __crc_val; + bool EspClass::checkFlashCRC() { - // The CRC and total length are placed in extra space at the end of the 4K chunk - // of flash occupied by the bootloader. If the bootloader grows to >4K-8 bytes, - // we'll need to adjust this. - uint32_t flashsize = *((uint32_t*)(0x40200000 + 4088)); // Start of PROGMEM plus 4K-8 - uint32_t flashcrc = *((uint32_t*)(0x40200000 + 4092)); // Start of PROGMEM plus 4K-4 + // Dummy CRC fill uint32_t z[2]; z[0] = z[1] = 0; + 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, 4096-8, 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 - crc = crc32((const void*)0x40201000, flashsize-4096, crc); - return crc == flashcrc; + crc = crc32((const void*)(0x40200000 + firstPart + 8), __crc_len - (firstPart + 8), crc); + return crc == __crc_val; } @@ -502,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) { @@ -514,7 +532,18 @@ bool EspClass::eraseConfig(void) { return true; } -uint8_t *EspClass::random(uint8_t *resultArray, const size_t outputSizeBytes) const +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) { /** * The ESP32 Technical Reference Manual v4.1 chapter 24 has the following to say about random number generation (no information found for ESP8266): @@ -564,7 +593,7 @@ uint8_t *EspClass::random(uint8_t *resultArray, const size_t outputSizeBytes) co return resultArray; } -uint32_t EspClass::random() const +uint32_t EspClass::random() { union { uint32_t b32; uint8_t b8[4]; } result; random(result.b8, 4); @@ -603,14 +632,12 @@ uint32_t EspClass::getSketchSize() { return result; } -extern "C" uint32_t _FS_start; - uint32_t EspClass::getFreeSketchSpace() { uint32_t usedSize = getSketchSize(); // round one sector up uint32_t freeSpaceStart = (usedSize + FLASH_SECTOR_SIZE - 1) & (~(FLASH_SECTOR_SIZE - 1)); - uint32_t freeSpaceEnd = (uint32_t)&_FS_start - 0x40200000; + uint32_t freeSpaceEnd = (uint32_t)FS_start - 0x40200000; #ifdef DEBUG_SERIAL DEBUG_SERIAL.printf("usedSize=%u freeSpaceStart=%u freeSpaceEnd=%u\r\n", usedSize, freeSpaceStart, freeSpaceEnd); @@ -660,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; @@ -707,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]; + + size_t written = 0; - if (pageBreak) { - size_t byteCount = 4 - (address % 4); + if (!isAlignedAddress(address)) { + auto before_address = alignBefore(address); + auto offset = address - before_address; + auto wlen = std::min(Alignment - offset, size); - if (!flashReplaceBlock(address, data, byteCount)) { + 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) { @@ -903,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; @@ -927,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; @@ -960,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); @@ -972,22 +973,11 @@ String EspClass::getSketchMD5() return result; } -void EspClass::enableVM() -{ -#ifdef UMM_HEAP_EXTERNAL - if (!vmEnabled) - install_vm_exception_handler(); - vmEnabled = true; -#endif -} - void EspClass::setExternalHeap() { #ifdef UMM_HEAP_EXTERNAL - if (vmEnabled) { - if (!umm_push_heap(UMM_HEAP_EXTERNAL)) { - panic(); - } + if (!umm_push_heap(UMM_HEAP_EXTERNAL)) { + panic(); } #endif } @@ -1004,10 +994,8 @@ void EspClass::setIramHeap() void EspClass::setDramHeap() { #if defined(UMM_HEAP_EXTERNAL) && !defined(UMM_HEAP_IRAM) - if (vmEnabled) { - if (!umm_push_heap(UMM_HEAP_DRAM)) { - panic(); - } + if (!umm_push_heap(UMM_HEAP_DRAM)) { + panic(); } #elif defined(UMM_HEAP_IRAM) if (!umm_push_heap(UMM_HEAP_DRAM)) { @@ -1019,10 +1007,8 @@ void EspClass::setDramHeap() void EspClass::resetHeap() { #if defined(UMM_HEAP_EXTERNAL) && !defined(UMM_HEAP_IRAM) - if (vmEnabled) { - if (!umm_pop_heap()) { - panic(); - } + if (!umm_pop_heap()) { + panic(); } #elif defined(UMM_HEAP_IRAM) if (!umm_pop_heap()) { diff --git a/cores/esp8266/Esp.h b/cores/esp8266/Esp.h index 6d990e876b..9cb4141292 100644 --- a/cores/esp8266/Esp.h +++ b/cores/esp8266/Esp.h @@ -26,7 +26,7 @@ #include "spi_vendors.h" /** - * AVR macros for WDT managment + * AVR macros for WDT management */ typedef enum { WDTO_0MS = 0, //!< WDTO_0MS @@ -87,69 +87,77 @@ typedef enum { class EspClass { public: // TODO: figure out how to set WDT timeout - void wdtEnable(uint32_t timeout_ms = 0); + static void wdtEnable(uint32_t timeout_ms = 0); // note: setting the timeout value is not implemented at the moment - void wdtEnable(WDTO_t timeout_ms = WDTO_0MS); + static void wdtEnable(WDTO_t timeout_ms = WDTO_0MS); - void wdtDisable(); - void wdtFeed(); + static void wdtDisable(); + static void wdtFeed(); - void deepSleep(uint64_t time_us, RFMode mode = RF_DEFAULT); - void deepSleepInstant(uint64_t time_us, RFMode mode = RF_DEFAULT); - uint64_t deepSleepMax(); + static void deepSleep(uint64_t time_us, RFMode mode = RF_DEFAULT); + static void deepSleepInstant(uint64_t time_us, RFMode mode = RF_DEFAULT); + static uint64_t deepSleepMax(); - bool rtcUserMemoryRead(uint32_t offset, uint32_t *data, size_t size); - bool rtcUserMemoryWrite(uint32_t offset, uint32_t *data, size_t size); + static bool rtcUserMemoryRead(uint32_t offset, uint32_t *data, size_t size); + static bool rtcUserMemoryWrite(uint32_t offset, uint32_t *data, size_t size); - void reset(); - void restart(); - - uint16_t getVcc(); - uint32_t getChipId(); + 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. + */ + [[noreturn]] static void rebootIntoUartDownloadMode(); - uint32_t getFreeHeap(); - uint16_t getMaxFreeBlockSize(); - uint8_t getHeapFragmentation(); // in % - void getHeapStats(uint32_t* free = nullptr, uint16_t* max = nullptr, uint8_t* frag = nullptr); + static uint16_t getVcc(); + static uint32_t getChipId(); - uint32_t getFreeContStack(); - void resetFreeContStack(); + 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(); - const char * getSdkVersion(); - String getCoreVersion(); - String getFullVersion(); + static const char * getSdkVersion(); + static String getCoreVersion(); + static String getFullVersion(); - uint8_t getBootVersion(); - uint8_t getBootMode(); + static uint8_t getBootVersion(); + static uint8_t getBootMode(); #if defined(F_CPU) || defined(CORE_MOCK) constexpr #endif - inline uint8_t getCpuFreqMHz() const __attribute__((always_inline)) + static inline uint8_t getCpuFreqMHz() __attribute__((always_inline)) { return esp_get_cpu_freq_mhz(); } - uint32_t getFlashChipId(); - uint8_t getFlashChipVendorId(); + static uint32_t getFlashChipId(); + static uint8_t getFlashChipVendorId(); //gets the actual chip size based on the flash id - uint32_t getFlashChipRealSize(); + static uint32_t getFlashChipRealSize(); //gets the size of the flash as set by the compiler - uint32_t getFlashChipSize(); - uint32_t getFlashChipSpeed(); - FlashMode_t getFlashChipMode(); - uint32_t getFlashChipSizeByChipId(); + static uint32_t getFlashChipSize(); + static uint32_t getFlashChipSpeed(); + static FlashMode_t getFlashChipMode(); + static uint32_t getFlashChipSizeByChipId(); - uint32_t magicFlashChipSize(uint8_t byte); - uint32_t magicFlashChipSpeed(uint8_t byte); - FlashMode_t magicFlashChipMode(uint8_t byte); + static uint32_t magicFlashChipSize(uint8_t byte); + static uint32_t magicFlashChipSpeed(uint8_t byte); + static FlashMode_t magicFlashChipMode(uint8_t byte); - bool checkFlashConfig(bool needsEquals = false); + static bool checkFlashConfig(bool needsEquals = false); - bool checkFlashCRC(); + static bool checkFlashCRC(); - bool flashEraseSector(uint32_t sector); + static bool flashEraseSector(uint32_t sector); /** * @brief Write @a size bytes from @a data to flash at @a address * This overload requires @a data and @a size to be always 4 byte aligned and @@ -162,7 +170,7 @@ class EspClass { * @retval true success * @retval false failure to write to flash or incorrect alignment of params */ - bool flashWrite(uint32_t address, const uint32_t *data, size_t size); + static bool flashWrite(uint32_t address, const uint32_t *data, size_t size); /** * @brief Write @a size bytes from @a data to flash at @a address * This overload handles all misalignment cases @@ -171,7 +179,7 @@ class EspClass { * @param size amount of data, passing not multiple of 4 will cause additional reads and writes * @return bool result of operation */ - bool flashWrite(uint32_t address, const uint8_t *data, size_t size); + static bool flashWrite(uint32_t address, const uint8_t *data, size_t size); /** * @brief Read @a size bytes to @a data to flash at @a address * This overload requires @a data and @a size to be 4 byte aligned @@ -182,7 +190,7 @@ class EspClass { * @retval true success * @retval false failure to read from flash or incorrect alignment of params */ - bool flashRead(uint32_t address, uint32_t *data, size_t size); + static bool flashRead(uint32_t address, uint32_t *data, size_t size); /** * @brief Read @a size bytes to @a data to flash at @a address * This overload handles all misalignment cases @@ -191,58 +199,67 @@ class EspClass { * @param size amount of data, passing not multiple of 4 will cause additional read * @return bool result of operation */ - bool flashRead(uint32_t address, uint8_t *data, size_t size); + static bool flashRead(uint32_t address, uint8_t *data, size_t size); + + static uint32_t getSketchSize(); + static String getSketchMD5(); + static uint32_t getFreeSketchSpace(); + static bool updateSketch(Stream& in, uint32_t size, bool restartOnFail = false, bool restartOnSuccess = true); - uint32_t getSketchSize(); - String getSketchMD5(); - uint32_t getFreeSketchSpace(); - bool updateSketch(Stream& in, uint32_t size, bool restartOnFail = false, bool restartOnSuccess = true); + static String getResetReason(); + static String getResetInfo(); + static struct rst_info * getResetInfoPtr(); - String getResetReason(); - String getResetInfo(); - struct rst_info * getResetInfoPtr(); + static bool eraseConfig(); - 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(); - uint8_t *random(uint8_t *resultArray, const size_t outputSizeBytes) const; - uint32_t random() const; + static uint8_t *random(uint8_t *resultArray, const size_t outputSizeBytes); + static uint32_t random(); #if !defined(CORE_MOCK) - inline uint32_t getCycleCount() __attribute__((always_inline)) + static inline uint32_t getCycleCount() __attribute__((always_inline)) { return esp_get_cycle_count(); } #else - uint32_t getCycleCount(); + static uint32_t getCycleCount(); #endif // !defined(CORE_MOCK) - /** - * @brief Installs VM exception handler to support External memory (Experimental) - * - * @param none - * @return none - */ - void enableVM(); /** * @brief Push current Heap selection and set Heap selection to DRAM. * * @param none * @return none */ - void setDramHeap(); + static void setDramHeap(); /** * @brief Push current Heap selection and set Heap selection to IRAM. * * @param none * @return none */ - void setIramHeap(); + static void setIramHeap(); /** * @brief Push current Heap selection and set Heap selection to External. (Experimental) * * @param none * @return none */ - void setExternalHeap(); + static void setExternalHeap(); /** * @brief Restores Heap selection back to value present when * setDramHeap, setIramHeap, or setExternalHeap was called. @@ -250,44 +267,20 @@ class EspClass { * @param none * @return none */ - void resetHeap(); + static void resetHeap(); private: -#ifdef UMM_HEAP_EXTERNAL - bool vmEnabled = false; -#endif - /** - * @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 - */ - 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 acces 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 */ - 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 - */ - bool flashWritePageBreak(uint32_t address, const uint8_t *data, size_t size); + static size_t flashWriteUnalignedMemory(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 f897a52d1d..edfc1f8b49 100644 --- a/cores/esp8266/FS.cpp +++ b/cores/esp8266/FS.cpp @@ -66,9 +66,9 @@ int File::read() { return result; } -size_t File::read(uint8_t* buf, size_t size) { +int File::read(uint8_t* buf, size_t size) { if (!_p) - return -1; + return 0; return _p->read(buf, size); } @@ -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; } @@ -441,6 +438,13 @@ bool FS::rename(const String& pathFrom, const String& pathTo) { return rename(pathFrom.c_str(), pathTo.c_str()); } +time_t FS::getCreationTime() { + if (!_impl) { + return 0; + } + return _impl->getCreationTime(); +} + void FS::setTimeCallback(time_t (*cb)(void)) { if (!_impl) return; diff --git a/cores/esp8266/FS.h b/cores/esp8266/FS.h index 99c71cdcea..55305e9688 100644 --- a/cores/esp8266/FS.h +++ b/cores/esp8266/FS.h @@ -67,13 +67,14 @@ class File : public Stream size_t readBytes(char *buffer, size_t length) override { return read((uint8_t*)buffer, length); } - size_t read(uint8_t* buf, size_t size); + int read(uint8_t* buf, size_t size) override; bool seek(uint32_t pos, SeekMode mode); bool seek(uint32_t pos) { return seek(pos, SeekSet); } size_t position() const; size_t size() const; + virtual ssize_t streamRemaining() override { return (ssize_t)size() - (ssize_t)position(); } void close(); operator bool() const; const char* name() const; @@ -84,13 +85,13 @@ class File : public Stream bool isDirectory() const; // Arduino "class SD" methods for compatibility + //TODO use stream::send / check read(buf,size) result template size_t write(T &src){ 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; @@ -116,6 +117,18 @@ class File : public Stream time_t getCreationTime(); void setTimeCallback(time_t (*cb)(void)); + // Stream::send configuration + + bool inputCanTimeout () override { + // unavailable data can't become later available + return false; + } + + bool outputCanTimeout () override { + // free space for write can't increase later + return false; + } + protected: FileImplPtr _p; time_t (*_timeCallback)(void) = nullptr; @@ -235,6 +248,8 @@ class FS bool gc(); bool check(); + time_t getCreationTime(); + void setTimeCallback(time_t (*cb)(void)); friend class ::SDClass; // More of a frenemy, but SD needs internal implementation to get private FAT bits diff --git a/cores/esp8266/FSImpl.h b/cores/esp8266/FSImpl.h index 801a8d9c0d..22a058f7b3 100644 --- a/cores/esp8266/FSImpl.h +++ b/cores/esp8266/FSImpl.h @@ -30,7 +30,7 @@ class FileImpl { public: virtual ~FileImpl() { } virtual size_t write(const uint8_t *buf, size_t size) = 0; - virtual size_t read(uint8_t* buf, size_t size) = 0; + virtual int read(uint8_t* buf, size_t size) = 0; virtual void flush() = 0; virtual bool seek(uint32_t pos, SeekMode mode) = 0; virtual size_t position() const = 0; @@ -115,6 +115,7 @@ class FSImpl { virtual bool rmdir(const char* path) = 0; virtual bool gc() { return true; } // May not be implemented in all file systems. virtual bool check() { return true; } // May not be implemented in all file systems. + virtual time_t getCreationTime() { return 0; } // May not be implemented in all file systems. // Filesystems *may* support a timestamp per-file, so allow the user to override with // their own callback for all files on this FS. The default implementation simply diff --git a/cores/esp8266/FSnoop.cpp b/cores/esp8266/FSnoop.cpp index 6cead006f2..b5840ff16e 100644 --- a/cores/esp8266/FSnoop.cpp +++ b/cores/esp8266/FSnoop.cpp @@ -15,7 +15,7 @@ void close_all_fs(void) } // default weak definitions -// they are overriden in their respective real implementation +// they are overridden in their respective real implementation // hint: https://github.com/esp8266/Arduino/pull/6699#issuecomment-549085382 void littlefs_request_end(void) __attribute__((weak)); diff --git a/cores/esp8266/FlashMap.h b/cores/esp8266/FlashMap.h new file mode 100644 index 0000000000..996228fdc2 --- /dev/null +++ b/cores/esp8266/FlashMap.h @@ -0,0 +1,59 @@ + +// - do not edit - autogenerated by boards.txt.py + +#ifndef __FLASH_MAP_H +#define __FLASH_MAP_H + +#include +#include + +typedef struct +{ + uint32_t eeprom_start; + uint32_t fs_start; + uint32_t fs_end; + uint32_t fs_block_size; + uint32_t fs_page_size; + uint32_t flash_size_kb; +} flash_map_s; + +/* + Following definitions map the above structure, one per line. + FLASH_MAP_* is a user choice in sketch: + `FLASH_MAP_SETUP_CONFIG(FLASH_MAP_OTA_FS)` + Configuration is made at boot with detected flash chip size (last argument 512..16384) + Other values are defined from `tools/boards.txt.py`. +*/ + +#define FLASH_MAP_OTA_FS \ + { \ + { .eeprom_start = 0x402fb000, .fs_start = 0x402eb000, .fs_end = 0x402fb000, .fs_block_size = 0x1000, .fs_page_size = 0x100, .flash_size_kb = 1024 }, \ + { .eeprom_start = 0x403fb000, .fs_start = 0x403c0000, .fs_end = 0x403fb000, .fs_block_size = 0x1000, .fs_page_size = 0x100, .flash_size_kb = 2048 }, \ + { .eeprom_start = 0x405fb000, .fs_start = 0x40400000, .fs_end = 0x405fa000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 4096 }, \ + { .eeprom_start = 0x409fb000, .fs_start = 0x40400000, .fs_end = 0x409fa000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 8192 }, \ + { .eeprom_start = 0x411fb000, .fs_start = 0x40400000, .fs_end = 0x411fa000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 16384 }, \ + { .eeprom_start = 0x4027b000, .fs_start = 0x40273000, .fs_end = 0x4027b000, .fs_block_size = 0x1000, .fs_page_size = 0x100, .flash_size_kb = 512 }, \ + } + +#define FLASH_MAP_MAX_FS \ + { \ + { .eeprom_start = 0x402fb000, .fs_start = 0x4027b000, .fs_end = 0x402fb000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 1024 }, \ + { .eeprom_start = 0x403fb000, .fs_start = 0x40300000, .fs_end = 0x403fa000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 2048 }, \ + { .eeprom_start = 0x405fb000, .fs_start = 0x40300000, .fs_end = 0x405fa000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 4096 }, \ + { .eeprom_start = 0x409fb000, .fs_start = 0x40300000, .fs_end = 0x409fa000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 8192 }, \ + { .eeprom_start = 0x411fb000, .fs_start = 0x40300000, .fs_end = 0x411fa000, .fs_block_size = 0x2000, .fs_page_size = 0x100, .flash_size_kb = 16384 }, \ + { .eeprom_start = 0x4027b000, .fs_start = 0x4025b000, .fs_end = 0x4027b000, .fs_block_size = 0x1000, .fs_page_size = 0x100, .flash_size_kb = 512 }, \ + } + +#define FLASH_MAP_NO_FS \ + { \ + { .eeprom_start = 0x402fb000, .fs_start = 0x402fb000, .fs_end = 0x402fb000, .fs_block_size = 0x0, .fs_page_size = 0x0, .flash_size_kb = 1024 }, \ + { .eeprom_start = 0x403fb000, .fs_start = 0x403fb000, .fs_end = 0x403fb000, .fs_block_size = 0x0, .fs_page_size = 0x0, .flash_size_kb = 2048 }, \ + { .eeprom_start = 0x405fb000, .fs_start = 0x405fb000, .fs_end = 0x405fb000, .fs_block_size = 0x0, .fs_page_size = 0x0, .flash_size_kb = 4096 }, \ + { .eeprom_start = 0x409fb000, .fs_start = 0x409fb000, .fs_end = 0x409fb000, .fs_block_size = 0x0, .fs_page_size = 0x0, .flash_size_kb = 8192 }, \ + { .eeprom_start = 0x411fb000, .fs_start = 0x411fb000, .fs_end = 0x411fb000, .fs_block_size = 0x0, .fs_page_size = 0x0, .flash_size_kb = 16384 }, \ + { .eeprom_start = 0x4027b000, .fs_start = 0x4027b000, .fs_end = 0x4027b000, .fs_block_size = 0x0, .fs_page_size = 0x0, .flash_size_kb = 512 }, \ + } + +#endif // __FLASH_MAP_H + diff --git a/cores/esp8266/FunctionalInterrupt.cpp b/cores/esp8266/FunctionalInterrupt.cpp index 665d8043b3..f468595787 100644 --- a/cores/esp8266/FunctionalInterrupt.cpp +++ b/cores/esp8266/FunctionalInterrupt.cpp @@ -10,7 +10,7 @@ typedef void (*voidFuncPtrArg)(void*); extern "C" void __attachInterruptFunctionalArg(uint8_t pin, voidFuncPtr userFunc, void*fp, int mode, bool functional); -void ICACHE_RAM_ATTR interruptFunctional(void* arg) +void IRAM_ATTR interruptFunctional(void* arg) { ArgStructure* localArg = (ArgStructure*)arg; if (localArg->functionInfo->reqScheduledFunction) diff --git a/cores/esp8266/HardwareSerial.cpp b/cores/esp8266/HardwareSerial.cpp index 26383dd43e..4896e27758 100644 --- a/cores/esp8266/HardwareSerial.cpp +++ b/cores/esp8266/HardwareSerial.cpp @@ -35,9 +35,6 @@ // SerialEvent functions are weak, so when the user doesn't define them, // the linker just sets their address to 0 (which is checked below). -// The Serialx_available is just a wrapper around Serialx.available(), -// but we can refer to it weakly so we don't pull in the entire -// HardwareSerial instance if the user doesn't also refer to it. void serialEvent() __attribute__((weak)); HardwareSerial::HardwareSerial(int uart_nr) @@ -146,8 +143,7 @@ unsigned long HardwareSerial::detectBaudrate(time_t timeoutMillis) if ((detectedBaudrate = testBaudrate())) { break; } - yield(); - delay(100); + esp_delay(100); } return detectedBaudrate; } diff --git a/cores/esp8266/HardwareSerial.h b/cores/esp8266/HardwareSerial.h index 940bde173b..3ef45892e6 100644 --- a/cores/esp8266/HardwareSerial.h +++ b/cores/esp8266/HardwareSerial.h @@ -101,50 +101,79 @@ class HardwareSerial: public Stream return uart_get_rx_buffer_size(_uart); } - void swap() + bool swap() { - swap(1); + return swap(1); } - void swap(uint8_t tx_pin) //toggle between use of GPIO13/GPIO15 or GPIO3/GPIO(1/2) as RX and TX + bool swap(uint8_t tx_pin) //toggle between use of GPIO13/GPIO15 or GPIO3/GPIO(1/2) as RX and TX { - uart_swap(_uart, tx_pin); + return uart_swap(_uart, tx_pin); } /* * Toggle between use of GPIO1 and GPIO2 as TX on UART 0. * Note: UART 1 can't be used if GPIO2 is used with UART 0! */ - void set_tx(uint8_t tx_pin) + bool set_tx(uint8_t tx_pin) { - uart_set_tx(_uart, tx_pin); + return uart_set_tx(_uart, tx_pin); } /* * UART 0 possible options are (1, 3), (2, 3) or (15, 13) * UART 1 allows only TX on 2 if UART 0 is not (2, 3) */ - void pins(uint8_t tx, uint8_t rx) + bool pins(uint8_t tx, uint8_t rx) { - uart_set_pins(_uart, tx, rx); + return uart_set_pins(_uart, tx, rx); } int available(void) override; int peek(void) override { - // return -1 when data is unvailable (arduino api) + // return -1 when data is unavailable (arduino api) return uart_peek_char(_uart); } + + virtual bool hasPeekBufferAPI () const override + { + return true; + } + + // return a pointer to available data buffer (size = available()) + // semantic forbids any kind of read() before calling peekConsume() + const char* peekBuffer () override + { + return uart_peek_buffer(_uart); + } + + // return number of byte accessible by peekBuffer() + size_t peekAvailable () override + { + return uart_peek_available(_uart); + } + + // consume bytes after use (see peekBuffer) + void peekConsume (size_t consume) override + { + return uart_peek_consume(_uart, consume); + } + int read(void) override { - // return -1 when data is unvailable (arduino api) + // return -1 when data is unavailable (arduino api) return uart_read_char(_uart); } // ::read(buffer, size): same as readBytes without timeout - size_t read(char* buffer, size_t size) + int read(char* buffer, size_t size) { return uart_read(_uart, buffer, size); } + int read(uint8_t* buffer, size_t size) override + { + return uart_read(_uart, (char*)buffer, size); + } size_t readBytes(char* buffer, size_t size) override; size_t readBytes(uint8_t* buffer, size_t size) override { @@ -154,7 +183,7 @@ class HardwareSerial: public Stream { return static_cast(uart_tx_free(_uart)); } - void flush(void) override; + void flush(void) override; // wait for all outgoing characters to be sent, output buffer is empty after this call size_t write(uint8_t c) override { return uart_write_char(_uart, c); @@ -204,8 +233,12 @@ class HardwareSerial: public Stream size_t _rx_size; }; +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL) extern HardwareSerial Serial; +#endif +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL1) extern HardwareSerial Serial1; +#endif extern void serialEventRun(void) __attribute__((weak)); 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 0a0f0b77b1..a5324ec5d5 100644 --- a/cores/esp8266/LwipDhcpServer-NonOS.cpp +++ b/cores/esp8266/LwipDhcpServer-NonOS.cpp @@ -1,64 +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" { + // `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_start(struct ip_info *info, netif* apnetif) + void dhcps_stop() { - // apnetif is esp interface, replaced by lwip2's - // netif_git[SOFTAP_IF] interface in constructor - (void)apnetif; + auto& server = getNonOSDhcpServer(); + if (server.isRunning()) + { + server.end(); + } + } -#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 intialized with AP interface number above. - fw_has_started_softap_dhcps = 1; -#endif + // 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); } - void dhcps_stop() + 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) { - dhcpSoftAP.end(); + auto& server = getNonOSDhcpServer(); + return server.add_dhcps_lease(macaddr); } -} // extern "C" +} // 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 2fa0b12d84..0f5d2bcf32 100644 --- a/cores/esp8266/LwipDhcpServer.cpp +++ b/cores/esp8266/LwipDhcpServer.cpp @@ -34,9 +34,7 @@ // (better is enemy of [good = already working]) // ^^ this comment is supposed to be removed after the first commit -#include // LWIP_VERSION - -#define DHCPS_LEASE_TIME_DEF (120) +#include // LWIP_VERSION #define USE_DNS @@ -52,6 +50,9 @@ #include "user_interface.h" #include "mem.h" +#include +#include + typedef struct dhcps_state { sint16_t state; @@ -59,33 +60,27 @@ typedef struct dhcps_state typedef struct dhcps_msg { - uint8_t op, htype, hlen, hops; - uint8_t xid[4]; + 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]; + 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; + 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 @@ -103,50 +98,48 @@ typedef enum struct dhcps_pool { struct ipv4_addr ip; - uint8 mac[6]; - uint32 lease_timer; - dhcps_type_t type; - dhcps_state_t state; - + uint8 mac[6]; + uint32 lease_timer; + dhcps_type_t type; + dhcps_state_t state; }; -#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_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_HLEN_ETHERNET 6 +#define DHCP_MSG_LEN 236 + +#define DHCPS_SERVER_PORT 67 +#define DHCPS_CLIENT_PORT 68 + +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 +#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 DHCP_OPTION_REQ_LIST 55 +#define DHCP_OPTION_END 255 //#define USE_CLASS_B_NET 1 -#define DHCPS_DEBUG 0 -#define MAX_STATION_NUM 8 +#define DHCPS_DEBUG 0 +#define MAX_STATION_NUM 8 #define DHCPS_STATE_OFFER 1 #define DHCPS_STATE_DECLINE 2 @@ -155,87 +148,81 @@ 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 #if DHCPS_DEBUG -#define LWIP_IS_OK(what,err) ({ int ret = 1, errval = (err); if (errval != ERR_OK) { os_printf("DHCPS ERROR: %s (lwip:%d)\n", what, errval); ret = 0; } ret; }) +#define LWIP_IS_OK(what, err) \ + ({ \ + int ret = 1, errval = (err); \ + if (errval != ERR_OK) \ + { \ + os_printf("DHCPS ERROR: %s (lwip:%s(%d))\n", what, lwip_strerr(errval), errval); \ + ret = 0; \ + } \ + ret; \ + }) #else -#define LWIP_IS_OK(what,err) ((err) == ERR_OK) +#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 intial 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 Parameters : arg -- Additional argument to pass to the callback function Returns : none *******************************************************************************/ -void DhcpServer::node_insert_to_list(list_node **phead, list_node* pinsert) +void DhcpServer::node_insert_to_list(list_node** phead, list_node* pinsert) { - list_node *plist = nullptr; - struct dhcps_pool *pdhcps_pool = nullptr; - struct dhcps_pool *pdhcps_node = nullptr; + list_node* plist = nullptr; + struct dhcps_pool* pdhcps_pool = nullptr; + struct dhcps_pool* pdhcps_node = nullptr; if (*phead == nullptr) { *phead = pinsert; } else { - plist = *phead; + plist = *phead; pdhcps_node = (struct dhcps_pool*)pinsert->pnode; pdhcps_pool = (struct dhcps_pool*)plist->pnode; if (pdhcps_node->ip.addr < pdhcps_pool->ip.addr) { pinsert->pnext = plist; - *phead = pinsert; + *phead = pinsert; } else { @@ -245,7 +232,7 @@ void DhcpServer::node_insert_to_list(list_node **phead, list_node* pinsert) if (pdhcps_node->ip.addr < pdhcps_pool->ip.addr) { pinsert->pnext = plist->pnext; - plist->pnext = pinsert; + plist->pnext = pinsert; break; } plist = plist->pnext; @@ -266,9 +253,9 @@ void DhcpServer::node_insert_to_list(list_node **phead, list_node* pinsert) Parameters : arg -- Additional argument to pass to the callback function Returns : none *******************************************************************************/ -void DhcpServer::node_remove_from_list(list_node **phead, list_node* pdelete) +void DhcpServer::node_remove_from_list(list_node** phead, list_node* pdelete) { - list_node *plist = nullptr; + list_node* plist = nullptr; plist = *phead; if (plist == nullptr) @@ -279,7 +266,7 @@ void DhcpServer::node_remove_from_list(list_node **phead, list_node* pdelete) { if (plist == pdelete) { - *phead = plist->pnext; + *phead = plist->pnext; pdelete->pnext = nullptr; } else @@ -288,7 +275,7 @@ void DhcpServer::node_remove_from_list(list_node **phead, list_node* pdelete) { if (plist->pnext == pdelete) { - plist->pnext = pdelete->pnext; + plist->pnext = pdelete->pnext; pdelete->pnext = nullptr; } plist = plist->pnext; @@ -303,13 +290,13 @@ void DhcpServer::node_remove_from_list(list_node **phead, list_node* pdelete) Parameters : mac address Returns : true if ok and false if this mac already exist or if all ip are already reserved *******************************************************************************/ -bool DhcpServer::add_dhcps_lease(uint8 *macaddr) +bool DhcpServer::add_dhcps_lease(uint8* macaddr) { - struct dhcps_pool *pdhcps_pool = nullptr; - list_node *pback_node = nullptr; + 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) { @@ -335,15 +322,15 @@ bool DhcpServer::add_dhcps_lease(uint8 *macaddr) return false; } - pdhcps_pool = (struct dhcps_pool *)zalloc(sizeof(struct dhcps_pool)); + 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->type = DHCPS_TYPE_STATIC; - pdhcps_pool->state = DHCPS_STATE_ONLINE; - pback_node = (list_node *)zalloc(sizeof(list_node)); - pback_node->pnode = pdhcps_pool; - pback_node->pnext = nullptr; + 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)); + pback_node->pnode = pdhcps_pool; + pback_node->pnext = nullptr; node_insert_to_list(&plist, pback_node); return true; @@ -351,167 +338,72 @@ bool DhcpServer::add_dhcps_lease(uint8 *macaddr) /////////////////////////////////////////////////////////////////////////////////// /* - DHCP msg - - @param optptr -- DHCP msg - @param type -- option + Set DHCP msg offer options for the given server - @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) { + options.add(DHCP_OPTION_SUBNET_MASK, ip_2_ip4(&_netif->netmask)) + .add(DHCP_OPTION_SERVER_ID, ip_2_ip4(&_netif->ip_addr)); - *optptr++ = DHCP_OPTION_MSG_TYPE; - *optptr++ = 1; - *optptr++ = type; - return optptr; -} -/////////////////////////////////////////////////////////////////////////////////// -/* - DHCP msg offer - - @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 unititialized (??) - *optptr++ = 43; // vendor specific - *optptr++ = 6; - // unitialized ? -#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; client.addr = client_address.addr; - m->op = DHCP_REPLY; + m->op = DHCP_REPLY; m->htype = DHCP_HTYPE_ETHERNET; - m->hlen = 6; - m->hops = 0; - m->secs = 0; + m->hlen = 6; + m->hops = 0; + m->secs = 0; m->flags = htons(BOOTP_BROADCAST); - memcpy((char *) m->yiaddr, (char *) &client.addr, sizeof(m->yiaddr)); - memset((char *) m->ciaddr, 0, sizeof(m->ciaddr)); - memset((char *) m->siaddr, 0, sizeof(m->siaddr)); - memset((char *) m->giaddr, 0, sizeof(m->giaddr)); - 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->yiaddr, (char*)&client.addr, sizeof(m->yiaddr)); + memset((char*)m->ciaddr, 0, sizeof(m->ciaddr)); + memset((char*)m->siaddr, 0, sizeof(m->siaddr)); + memset((char*)m->giaddr, 0, sizeof(m->giaddr)); + 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, &MagicCookie, sizeof(MagicCookie)); + + return { &m->options[sizeof(magic_cookie)], std::end(m->options) }; } /////////////////////////////////////////////////////////////////////////////////// /* @@ -520,18 +412,20 @@ void DhcpServer::create_msg(struct dhcps_msg *m) @param -- m DHCP msg */ /////////////////////////////////////////////////////////////////////////////////// -void DhcpServer::send_offer(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; - end = add_msg_type(&m->options[4], DHCPOFFER); - end = add_offer_options(end); - end = add_end(end); + 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); + } + + options.add(DHCP_OPTION_END); p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcps_msg), PBUF_RAM); #if DHCPS_DEBUG @@ -539,27 +433,15 @@ void DhcpServer::send_offer(struct dhcps_msg *m) #endif if (p != nullptr) { - #if DHCPS_DEBUG os_printf("dhcps: send_offer>>pbuf_alloc succeed\n"); 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 { - #if DHCPS_DEBUG os_printf("dhcps: send_offer>>pbuf_alloc failed\n"); #endif @@ -586,18 +468,13 @@ void DhcpServer::send_offer(struct dhcps_msg *m) @param m DHCP msg */ /////////////////////////////////////////////////////////////////////////////////// -void DhcpServer::send_nak(struct dhcps_msg *m) +void DhcpServer::send_nak(struct dhcps_msg* m) { + struct pbuf* p; - u8_t *end; - struct pbuf *p, *q; - u8_t *data; - u16_t cnt = 0; - u16_t i; - create_msg(m); - - 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 @@ -605,27 +482,15 @@ void DhcpServer::send_nak(struct dhcps_msg *m) #endif if (p != nullptr) { - #if DHCPS_DEBUG os_printf("dhcps: send_nak>>pbuf_alloc succeed\n"); 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 { - #if DHCPS_DEBUG os_printf("dhcps: send_nak>>pbuf_alloc failed\n"); #endif @@ -647,19 +512,20 @@ void DhcpServer::send_nak(struct dhcps_msg *m) @param m DHCP msg */ /////////////////////////////////////////////////////////////////////////////////// -void DhcpServer::send_ack(struct dhcps_msg *m) +void DhcpServer::send_ack(struct dhcps_msg* m) { + struct pbuf* p; - u8_t *end; - struct pbuf *p, *q; - u8_t *data; - u16_t cnt = 0; - u16_t i; - create_msg(m); + auto options = create_msg(m); + options.add(DHCP_OPTION_MSG_TYPE, DHCPACK); - end = add_msg_type(&m->options[4], DHCPACK); - end = add_offer_options(end); - end = add_end(end); + add_offer_options(options); + if (custom_offer_options) + { + custom_offer_options(*this, options); + } + + options.add(DHCP_OPTION_END); p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcps_msg), PBUF_RAM); #if DHCPS_DEBUG @@ -667,33 +533,22 @@ void DhcpServer::send_ack(struct dhcps_msg *m) #endif if (p != nullptr) { - #if DHCPS_DEBUG os_printf("dhcps: send_ack>>pbuf_alloc succeed\n"); 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 { - #if DHCPS_DEBUG os_printf("dhcps: send_ack>>pbuf_alloc failed\n"); #endif return; } - if (!LWIP_IS_OK("dhcps send ack", udp_sendto(pcb_dhcps, p, &broadcast_dhcps, DHCPS_CLIENT_PORT))) + if (!LWIP_IS_OK("dhcps send ack", + udp_sendto(pcb_dhcps, p, &broadcast_dhcps, DHCPS_CLIENT_PORT))) { #if DHCPS_DEBUG os_printf("dhcps: send_ack>>udp_sendto\n"); @@ -718,15 +573,15 @@ void DhcpServer::send_ack(struct dhcps_msg *m) @return uint8_t* DHCP Server */ /////////////////////////////////////////////////////////////////////////////////// -uint8_t DhcpServer::parse_options(uint8_t *optptr, sint16_t len) +uint8_t DhcpServer::parse_options(uint8_t* optptr, sint16_t len) { - struct ipv4_addr client; - bool is_dhcp_parse_end = false; + struct ipv4_addr client; + bool is_dhcp_parse_end = false; struct dhcps_state s; client.addr = client_address.addr; - u8_t *end = optptr + len; + u8_t* end = optptr + len; u16_t type = 0; s.state = DHCPS_STATE_IDLE; @@ -736,16 +591,15 @@ uint8_t DhcpServer::parse_options(uint8_t *optptr, sint16_t len) #if DHCPS_DEBUG os_printf("dhcps: (sint16_t)*optptr = %d\n", (sint16_t)*optptr); #endif - switch ((sint16_t) *optptr) + switch ((sint16_t)*optptr) { - - case DHCP_OPTION_MSG_TYPE: //53 + case DHCP_OPTION_MSG_TYPE: // 53 type = *(optptr + 2); break; - case DHCP_OPTION_REQ_IPADDR://50 - //os_printf("dhcps:0x%08x,0x%08x\n",client.addr,*(uint32*)(optptr+2)); - if (memcmp((char *) &client.addr, (char *) optptr + 2, 4) == 0) + case DHCP_OPTION_REQ_IPADDR: // 50 + // os_printf("dhcps:0x%08x,0x%08x\n",client.addr,*(uint32*)(optptr+2)); + if (memcmp((char*)&client.addr, (char*)optptr + 2, 4) == 0) { #if DHCPS_DEBUG os_printf("dhcps: DHCP_OPTION_REQ_IPADDR = 0 ok\n"); @@ -777,14 +631,14 @@ uint8_t DhcpServer::parse_options(uint8_t *optptr, sint16_t len) switch (type) { - case DHCPDISCOVER://1 + case DHCPDISCOVER: // 1 s.state = DHCPS_STATE_OFFER; #if DHCPS_DEBUG os_printf("dhcps: DHCPD_STATE_OFFER\n"); #endif break; - case DHCPREQUEST://3 + case DHCPREQUEST: // 3 if (!(s.state == DHCPS_STATE_ACK || s.state == DHCPS_STATE_NAK)) { if (renew == true) @@ -801,14 +655,14 @@ uint8_t DhcpServer::parse_options(uint8_t *optptr, sint16_t len) } break; - case DHCPDECLINE://4 + case DHCPDECLINE: // 4 s.state = DHCPS_STATE_IDLE; #if DHCPS_DEBUG os_printf("dhcps: DHCPD_STATE_IDLE\n"); #endif break; - case DHCPRELEASE://7 + case DHCPRELEASE: // 7 s.state = DHCPS_STATE_RELEASE; #if DHCPS_DEBUG os_printf("dhcps: DHCPD_STATE_IDLE\n"); @@ -822,11 +676,9 @@ uint8_t DhcpServer::parse_options(uint8_t *optptr, sint16_t len) } /////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////// -sint16_t DhcpServer::parse_msg(struct dhcps_msg *m, u16_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)); @@ -836,7 +688,7 @@ sint16_t DhcpServer::parse_msg(struct dhcps_msg *m, u16_t len) if (ret == DHCPS_STATE_RELEASE) { - dhcps_client_leave(m->chaddr, &ip, true); // force to delete + dhcps_client_leave(m->chaddr, &ip, true); // force to delete client_address.addr = ip.addr; } @@ -857,32 +709,26 @@ sint16_t DhcpServer::parse_msg(struct dhcps_msg *m, u16_t len) */ /////////////////////////////////////////////////////////////////////////////////// -void DhcpServer::S_handle_dhcp(void *arg, - struct udp_pcb *pcb, - struct pbuf *p, - const ip_addr_t *addr, - uint16_t port) +void DhcpServer::S_handle_dhcp(void* arg, struct udp_pcb* pcb, struct pbuf* p, + const ip_addr_t* addr, uint16_t port) { DhcpServer* instance = reinterpret_cast(arg); instance->handle_dhcp(pcb, p, addr, port); } -void DhcpServer::handle_dhcp( - struct udp_pcb *pcb, - struct pbuf *p, - const ip_addr_t *addr, - uint16_t port) +void DhcpServer::handle_dhcp(struct udp_pcb* pcb, struct pbuf* p, const ip_addr_t* addr, + uint16_t port) { (void)pcb; (void)addr; (void)port; - struct dhcps_msg *pmsg_dhcps = nullptr; - sint16_t tlen = 0; - u16_t i = 0; - u16_t dhcps_msg_cnt = 0; - u8_t *p_dhcps_msg = nullptr; - u8_t *data = nullptr; + struct dhcps_msg* pmsg_dhcps = nullptr; + sint16_t tlen = 0; + u16_t i = 0; + u16_t dhcps_msg_cnt = 0; + u8_t* p_dhcps_msg = nullptr; + u8_t* data = nullptr; #if DHCPS_DEBUG os_printf("dhcps: handle_dhcp-> receive a packet\n"); @@ -892,15 +738,15 @@ void DhcpServer::handle_dhcp( return; } - pmsg_dhcps = (struct dhcps_msg *)zalloc(sizeof(struct dhcps_msg)); + pmsg_dhcps = (struct dhcps_msg*)zalloc(sizeof(struct dhcps_msg)); if (nullptr == pmsg_dhcps) { pbuf_free(p); return; } - p_dhcps_msg = (u8_t *)pmsg_dhcps; - tlen = p->tot_len; - data = (u8_t*)p->payload; + p_dhcps_msg = (u8_t*)pmsg_dhcps; + tlen = p->tot_len; + data = (u8_t*)p->payload; #if DHCPS_DEBUG os_printf("dhcps: handle_dhcp-> p->tot_len = %d\n", tlen); @@ -933,14 +779,13 @@ void DhcpServer::handle_dhcp( switch (parse_msg(pmsg_dhcps, tlen - 240)) { - - case DHCPS_STATE_OFFER://1 + case DHCPS_STATE_OFFER: // 1 #if DHCPS_DEBUG os_printf("dhcps: handle_dhcp-> DHCPD_STATE_OFFER\n"); #endif send_offer(pmsg_dhcps); break; - case DHCPS_STATE_ACK://3 + case DHCPS_STATE_ACK: // 3 #if DHCPS_DEBUG os_printf("dhcps: handle_dhcp-> DHCPD_STATE_ACK\n"); #endif @@ -950,13 +795,13 @@ void DhcpServer::handle_dhcp( wifi_softap_set_station_info(pmsg_dhcps->chaddr, &client_address); } break; - case DHCPS_STATE_NAK://4 + case DHCPS_STATE_NAK: // 4 #if DHCPS_DEBUG os_printf("dhcps: handle_dhcp-> DHCPD_STATE_NAK\n"); #endif send_nak(pmsg_dhcps); break; - default : + default: break; } #if DHCPS_DEBUG @@ -971,30 +816,30 @@ 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) + uint32 end_ip = 0; + 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 { /*config ip information must be in the same segment as the local ip*/ softap_ip >>= 8; if (((start_ip >> 8 != softap_ip) || (end_ip >> 8 != softap_ip)) - || (end_ip - start_ip > DHCPS_MAX_LEASE)) + || (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; @@ -1005,22 +850,22 @@ void DhcpServer::init_dhcps_lease(uint32 ip) } else { - local_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) { @@ -1028,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; } @@ -1040,23 +887,18 @@ bool DhcpServer::begin(struct ip_info *info) broadcast_dhcps = _netif->ip_addr; ip_2_ip4(&broadcast_dhcps)->addr &= ip_2_ip4(&_netif->netmask)->addr; ip_2_ip4(&broadcast_dhcps)->addr |= ~ip_2_ip4(&_netif->netmask)->addr; - //XXXFIXMEIPV6 broadcast address? + // 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); udp_recv(pcb_dhcps, S_handle_dhcp, this); #if DHCPS_DEBUG - os_printf("dhcps:dhcps_start->udp_recv function Set a receive callback handle_dhcp for UDP_PCB pcb_dhcps\n"); + os_printf("dhcps:dhcps_start->udp_recv function Set a receive callback handle_dhcp for UDP_PCB " + "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; } @@ -1076,21 +918,21 @@ void DhcpServer::end() udp_remove(pcb_dhcps); pcb_dhcps = nullptr; - //udp_remove(pcb_dhcps); - list_node *pnode = nullptr; - list_node *pback_node = nullptr; - struct dhcps_pool* dhcp_node = nullptr; - struct ipv4_addr ip_zero; + // udp_remove(pcb_dhcps); + list_node* pnode = nullptr; + list_node* pback_node = nullptr; + struct dhcps_pool* dhcp_node = nullptr; + struct ipv4_addr ip_zero; memset(&ip_zero, 0x0, sizeof(ip_zero)); pnode = plist; while (pnode != nullptr) { pback_node = pnode; - pnode = pback_node->pnext; + pnode = pback_node->pnext; node_remove_from_list(&plist, pback_node); dhcp_node = (struct dhcps_pool*)pback_node->pnode; - //dhcps_client_leave(dhcp_node->mac,&dhcp_node->ip,true); // force to delete + // dhcps_client_leave(dhcp_node->mac,&dhcp_node->ip,true); // force to delete if (_netif->num == SOFTAP_IF) { wifi_softap_set_station_info(dhcp_node->mac, &ip_zero); @@ -1102,12 +944,11 @@ void DhcpServer::end() } } -bool DhcpServer::isRunning() +bool DhcpServer::isRunning() const { - return !!_netif->state; + return pcb_dhcps != nullptr; } - /****************************************************************************** FunctionName : set_dhcps_lease Description : set the lease information of DHCP server @@ -1115,11 +956,11 @@ bool DhcpServer::isRunning() Little-Endian. Returns : true or false *******************************************************************************/ -bool DhcpServer::set_dhcps_lease(struct dhcps_lease *please) +bool DhcpServer::set_dhcps_lease(struct dhcps_lease* please) { uint32 softap_ip = 0; - uint32 start_ip = 0; - uint32 end_ip = 0; + uint32 start_ip = 0; + uint32 end_ip = 0; if (_netif->num == SOFTAP_IF || _netif->num == STATION_IF) { @@ -1140,16 +981,9 @@ bool DhcpServer::set_dhcps_lease(struct dhcps_lease *please) // logic below is subject for improvement // - is wrong // - limited to /24 address plans -#if 1 - softap_ip = ip_2_ip4(&_netif->ip_addr)->addr; -#else - struct ip_info info; - bzero(&info, sizeof(struct ip_info)); - wifi_get_ip_info(SOFTAP_IF, &info); - softap_ip = htonl(info.ip.addr); - start_ip = htonl(please->start_ip.addr); - end_ip = htonl(please->end_ip.addr); -#endif + softap_ip = htonl(ip_2_ip4(&_netif->ip_addr)->addr); + start_ip = htonl(please->start_ip.addr); + end_ip = htonl(please->end_ip.addr); /*config ip information can't contain local ip*/ if ((start_ip <= softap_ip) && (softap_ip <= end_ip)) { @@ -1158,8 +992,7 @@ bool DhcpServer::set_dhcps_lease(struct dhcps_lease *please) /*config ip information must be in the same segment as the local ip*/ softap_ip >>= 8; - if ((start_ip >> 8 != softap_ip) - || (end_ip >> 8 != softap_ip)) + if ((start_ip >> 8 != softap_ip) || (end_ip >> 8 != softap_ip)) { return false; } @@ -1169,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; } @@ -1187,7 +1020,7 @@ bool DhcpServer::set_dhcps_lease(struct dhcps_lease *please) Little-Endian. Returns : true or false *******************************************************************************/ -bool DhcpServer::get_dhcps_lease(struct dhcps_lease *please) +bool DhcpServer::get_dhcps_lease(struct dhcps_lease* please) { if (_netif->num == SOFTAP_IF) { @@ -1204,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()) { @@ -1213,45 +1046,46 @@ 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; } void DhcpServer::kill_oldest_dhcps_pool(void) { - list_node *pre = nullptr, *p = nullptr; - list_node *minpre = nullptr, *minp = nullptr; + list_node * pre = nullptr, *p = nullptr; + list_node * minpre = nullptr, *minp = nullptr; struct dhcps_pool *pdhcps_pool = nullptr, *pmin_pool = nullptr; - pre = plist; - p = pre->pnext; + pre = plist; + p = pre->pnext; minpre = pre; - minp = p; + minp = p; while (p != nullptr) { pdhcps_pool = (struct dhcps_pool*)p->pnode; - pmin_pool = (struct dhcps_pool*)minp->pnode; + pmin_pool = (struct dhcps_pool*)minp->pnode; if (pdhcps_pool->lease_timer < pmin_pool->lease_timer) { - minp = p; + minp = p; minpre = pre; } pre = p; - p = p->pnext; + p = p->pnext; } - minpre->pnext = minp->pnext; pdhcps_pool->state = DHCPS_STATE_OFFLINE; + minpre->pnext = minp->pnext; + pdhcps_pool->state = DHCPS_STATE_OFFLINE; free(minp->pnode); minp->pnode = nullptr; free(minp); @@ -1260,22 +1094,22 @@ void DhcpServer::kill_oldest_dhcps_pool(void) void DhcpServer::dhcps_coarse_tmr(void) { - uint8 num_dhcps_pool = 0; - list_node *pback_node = nullptr; - list_node *pnode = nullptr; - struct dhcps_pool *pdhcps_pool = nullptr; - pnode = plist; + uint8 num_dhcps_pool = 0; + list_node* pback_node = nullptr; + list_node* pnode = nullptr; + struct dhcps_pool* pdhcps_pool = nullptr; + pnode = plist; while (pnode != nullptr) { pdhcps_pool = (struct dhcps_pool*)pnode->pnode; if (pdhcps_pool->type == DHCPS_TYPE_DYNAMIC) { - pdhcps_pool->lease_timer --; + pdhcps_pool->lease_timer--; } if (pdhcps_pool->lease_timer == 0) { pback_node = pnode; - pnode = pback_node->pnext; + pnode = pback_node->pnext; node_remove_from_list(&plist, pback_node); free(pback_node->pnode); pback_node->pnode = nullptr; @@ -1284,8 +1118,8 @@ void DhcpServer::dhcps_coarse_tmr(void) } else { - pnode = pnode ->pnext; - num_dhcps_pool ++; + pnode = pnode->pnext; + num_dhcps_pool++; } } @@ -1295,85 +1129,10 @@ void DhcpServer::dhcps_coarse_tmr(void) } } -bool DhcpServer::set_dhcps_offer_option(uint8 level, void* optarg) +void DhcpServer::dhcps_client_leave(u8* bssid, struct ipv4_addr* ip, bool force) { - 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; - list_node *pback_node = nullptr; + struct dhcps_pool* pdhcps_pool = nullptr; + list_node* pback_node = nullptr; if ((bssid == nullptr) || (ip == nullptr)) { @@ -1419,16 +1178,16 @@ void DhcpServer::dhcps_client_leave(u8 *bssid, struct ipv4_addr *ip, bool force) } } -uint32 DhcpServer::dhcps_client_update(u8 *bssid, struct ipv4_addr *ip) +uint32 DhcpServer::dhcps_client_update(u8* bssid, struct ipv4_addr* ip) { - struct dhcps_pool *pdhcps_pool = nullptr; - list_node *pback_node = nullptr; - 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; - dhcps_type_t type = DHCPS_TYPE_DYNAMIC; + struct dhcps_pool* pdhcps_pool = nullptr; + list_node* pback_node = nullptr; + list_node* pmac_node = nullptr; + list_node* pip_node = nullptr; + bool flag = false; + uint32 start_ip = lease.start_ip.addr; + uint32 end_ip = lease.end_ip.addr; + dhcps_type_t type = DHCPS_TYPE_DYNAMIC; if (bssid == nullptr) { return IPADDR_ANY; @@ -1454,7 +1213,7 @@ uint32 DhcpServer::dhcps_client_update(u8 *bssid, struct ipv4_addr *ip) for (pback_node = plist; pback_node != nullptr; pback_node = pback_node->pnext) { pdhcps_pool = (struct dhcps_pool*)pback_node->pnode; - //os_printf("mac:"MACSTR"bssid:"MACSTR"\r\n",MAC2STR(pdhcps_pool->mac),MAC2STR(bssid)); + // os_printf("mac:"MACSTR"bssid:"MACSTR"\r\n",MAC2STR(pdhcps_pool->mac),MAC2STR(bssid)); if (memcmp(pdhcps_pool->mac, bssid, sizeof(pdhcps_pool->mac)) == 0) { pmac_node = pback_node; @@ -1503,12 +1262,12 @@ uint32 DhcpServer::dhcps_client_update(u8 *bssid, struct ipv4_addr *ip) { return IPADDR_ANY; } - //start_ip = htonl((ntohl(start_ip) + 1)); + // start_ip = htonl((ntohl(start_ip) + 1)); flag = true; } } - if (pmac_node != nullptr) // update new ip + if (pmac_node != nullptr) // update new ip { if (pip_node != nullptr) { @@ -1532,13 +1291,12 @@ uint32 DhcpServer::dhcps_client_update(u8 *bssid, struct ipv4_addr *ip) else { renew = true; - type = DHCPS_TYPE_DYNAMIC; + type = DHCPS_TYPE_DYNAMIC; } - pdhcps_pool->lease_timer = DHCPS_LEASE_TIMER; - pdhcps_pool->type = type; - pdhcps_pool->state = DHCPS_STATE_ONLINE; - + pdhcps_pool->lease_timer = this->lease_time; + pdhcps_pool->type = type; + pdhcps_pool->state = DHCPS_STATE_ONLINE; } else { @@ -1551,21 +1309,21 @@ uint32 DhcpServer::dhcps_client_update(u8 *bssid, struct ipv4_addr *ip) { pdhcps_pool->ip.addr = start_ip; } - else // no ip to distribute + else // no ip to distribute { return IPADDR_ANY; } node_remove_from_list(&plist, pmac_node); - pdhcps_pool->lease_timer = DHCPS_LEASE_TIMER; - pdhcps_pool->type = type; - pdhcps_pool->state = DHCPS_STATE_ONLINE; + pdhcps_pool->lease_timer = this->lease_time; + pdhcps_pool->type = type; + pdhcps_pool->state = DHCPS_STATE_ONLINE; node_insert_to_list(&plist, pmac_node); } } - else // new station + else // new station { - if (pip_node != nullptr) // maybe ip has used + if (pip_node != nullptr) // maybe ip has used { pdhcps_pool = (struct dhcps_pool*)pip_node->pnode; if (pdhcps_pool->state != DHCPS_STATE_OFFLINE) @@ -1573,13 +1331,13 @@ 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->type = type; - pdhcps_pool->state = DHCPS_STATE_ONLINE; + pdhcps_pool->lease_timer = this->lease_time; + pdhcps_pool->type = type; + pdhcps_pool->state = DHCPS_STATE_ONLINE; } else { - pdhcps_pool = (struct dhcps_pool *)zalloc(sizeof(struct dhcps_pool)); + pdhcps_pool = (struct dhcps_pool*)zalloc(sizeof(struct dhcps_pool)); if (ip != nullptr) { pdhcps_pool->ip.addr = ip->addr; @@ -1588,7 +1346,7 @@ uint32 DhcpServer::dhcps_client_update(u8 *bssid, struct ipv4_addr *ip) { pdhcps_pool->ip.addr = start_ip; } - else // no ip to distribute + else // no ip to distribute { free(pdhcps_pool); return IPADDR_ANY; @@ -1599,12 +1357,12 @@ 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->type = type; - pdhcps_pool->state = DHCPS_STATE_ONLINE; - pback_node = (list_node *)zalloc(sizeof(list_node)); - pback_node->pnode = pdhcps_pool; - pback_node->pnext = nullptr; + 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)); + pback_node->pnode = pdhcps_pool; + pback_node->pnext = nullptr; node_insert_to_list(&plist, pback_node); } } diff --git a/cores/esp8266/LwipDhcpServer.h b/cores/esp8266/LwipDhcpServer.h index e93166981d..152e54c9e6 100644 --- a/cores/esp8266/LwipDhcpServer.h +++ b/cores/esp8266/LwipDhcpServer.h @@ -28,23 +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; + } - bool begin(ip_info* info); + 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(); 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) @@ -54,71 +178,59 @@ 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); + 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 { - void *pnode; - struct _list_node *pnext; + void* pnode; + 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 send_offer(struct dhcps_msg *m); - void send_nak(struct dhcps_msg *m); - void send_ack(struct dhcps_msg *m); - uint8_t parse_options(uint8_t *optptr, sint16_t len); - sint16_t parse_msg(struct dhcps_msg *m, u16_t len); - static void S_handle_dhcp(void *arg, - struct udp_pcb *pcb, - struct pbuf *p, - const ip_addr_t *addr, + 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); + uint8_t parse_options(uint8_t* optptr, sint16_t len); + sint16_t parse_msg(struct dhcps_msg* m, u16_t len); + static void S_handle_dhcp(void* arg, struct udp_pcb* pcb, struct pbuf* p, const ip_addr_t* addr, uint16_t port); - void handle_dhcp( - struct udp_pcb *pcb, - struct pbuf *p, - const ip_addr_t *addr, - uint16_t port); - void kill_oldest_dhcps_pool(void); - void dhcps_coarse_tmr(void); // CURRENTLY NOT CALLED - void dhcps_client_leave(u8 *bssid, struct ipv4_addr *ip, bool force); - uint32 dhcps_client_update(u8 *bssid, struct ipv4_addr *ip); - - netif* _netif; - - 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 dhcps_lease dhcps_lease; - list_node *plist; - uint8 offer; - bool renew; + void handle_dhcp(struct udp_pcb* pcb, struct pbuf* p, const ip_addr_t* addr, uint16_t port); + void kill_oldest_dhcps_pool(void); + void dhcps_coarse_tmr(void); // CURRENTLY NOT CALLED + void dhcps_client_leave(u8* bssid, struct ipv4_addr* ip, bool force); + uint32 dhcps_client_update(u8* bssid, struct ipv4_addr* ip); - static const uint32 magic_cookie; -}; + netif* _netif = nullptr; -// SoftAP DHCP server always exists and is started on boot -extern DhcpServer dhcpSoftAP; -extern "C" int fw_has_started_softap_dhcps; + struct udp_pcb* pcb_dhcps = nullptr; + ip_addr_t broadcast_dhcps {}; + ip4_addr_t server_address {}; + ip4_addr_t client_address {}; -#endif // __DHCPS_H__ + uint32_t lease_time = DefaultLeaseTime; + + bool offer_router = true; + ip4_addr_t dns_address {}; + + dhcps_lease lease {}; + + list_node* plist = nullptr; + bool renew = false; + + OptionsBufferHandler custom_offer_options = nullptr; + + static const uint32 magic_cookie; +}; diff --git a/cores/esp8266/LwipIntf.cpp b/cores/esp8266/LwipIntf.cpp index d2746745cc..675063cd62 100644 --- a/cores/esp8266/LwipIntf.cpp +++ b/cores/esp8266/LwipIntf.cpp @@ -1,12 +1,35 @@ +/* + LwipIntf.cpp -extern "C" { + 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" +{ #include "lwip/err.h" #include "lwip/ip_addr.h" #include "lwip/dns.h" #include "lwip/dhcp.h" -#include "lwip/init.h" // LWIP_VERSION_ +#include "lwip/init.h" // LWIP_VERSION_ #if LWIP_IPV6 -#include "lwip/netif.h" // struct netif +#include "lwip/netif.h" // struct netif #endif #include @@ -15,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 @@ -24,19 +58,23 @@ extern "C" { // // result stored into gateway/netmask/dns1 -bool LwipIntf::ipAddressReorder(const IPAddress& local_ip, const IPAddress& arg1, const IPAddress& arg2, const IPAddress& arg3, - IPAddress& gateway, IPAddress& netmask, IPAddress& dns1) +bool LwipIntf::ipAddressReorder(const IPAddress& local_ip, const IPAddress& arg1, + const IPAddress& arg2, const IPAddress& arg3, IPAddress& gateway, + IPAddress& netmask, IPAddress& dns1) { - //To allow compatibility, check first octet of 3rd arg. If 255, interpret as ESP order, otherwise Arduino order. + // To allow compatibility, check first octet of 3rd arg. If 255, interpret as ESP order, + // otherwise Arduino order. gateway = arg1; netmask = arg2; - dns1 = arg3; + dns1 = arg3; if (netmask[0] != 255) { - //octet is not 255 => interpret as Arduino order + // octet is not 255 => interpret as Arduino order gateway = arg2; - netmask = arg3[0] == 0 ? IPAddress(255, 255, 255, 0) : arg3; //arg order is arduino and 4th arg not given => assign it arduino default + netmask = arg3[0] == 0 ? IPAddress(255, 255, 255, 0) + : arg3; // arg order is arduino and 4th arg not given => assign it + // arduino default dns1 = arg1; } @@ -46,7 +84,7 @@ bool LwipIntf::ipAddressReorder(const IPAddress& local_ip, const IPAddress& arg1 return false; } - //ip and gateway must be in the same netmask + // ip and gateway must be in the same netmask if (gateway.isSet() && (local_ip.v4() & netmask.v4()) != (gateway.v4() & netmask.v4())) { return false; @@ -61,7 +99,16 @@ bool LwipIntf::ipAddressReorder(const IPAddress& local_ip, const IPAddress& arg1 */ String LwipIntf::hostname(void) { - return wifi_station_get_hostname(); + return wifi_station_hostname; +} + +/** + Get ESP8266 station DHCP hostname + @return hostname +*/ +const char* LwipIntf::getHostname(void) +{ + return wifi_station_hostname; } /** @@ -122,21 +169,17 @@ 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 // harmless for AP, also compatible with ethernet adapters (to come) 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) { @@ -153,4 +196,3 @@ bool LwipIntf::hostname(const char* aHostname) return ret && compliant; } - diff --git a/cores/esp8266/LwipIntf.h b/cores/esp8266/LwipIntf.h index 972a7c8008..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 @@ -10,34 +32,44 @@ class LwipIntf { public: - - using CBType = std::function ; - - static bool stateUpCB(LwipIntf::CBType&& cb); + using CBType = std::function; // 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 - static - bool ipAddressReorder(const IPAddress& local_ip, const IPAddress& arg1, const IPAddress& arg2, const IPAddress& arg3, - IPAddress& gateway, IPAddress& netmask, IPAddress& dns1); + static bool ipAddressReorder(const IPAddress& local_ip, const IPAddress& arg1, + const IPAddress& arg2, const IPAddress& arg3, IPAddress& gateway, + IPAddress& netmask, IPAddress& dns1); String hostname(); + bool hostname(const String& aHostname) { return hostname(aHostname.c_str()); } + bool hostname(const char* aHostname); -protected: + // ESP32 API compatibility + bool setHostname(const char* aHostName) + { + return hostname(aHostName); + } + + // ESP32 API compatibility + const char* getHostname(); + + // whenever netif status callback is called + static bool statusChangeCB(LwipIntf::CBType); - static bool stateChangeSysCB(LwipIntf::CBType&& cb); + static bool stateUpCB(LwipIntf::CBType); + static bool stateDownCB(LwipIntf::CBType); }; -#endif // _LWIPINTF_H +#endif // _LWIPINTF_H diff --git a/cores/esp8266/LwipIntfCB.cpp b/cores/esp8266/LwipIntfCB.cpp index 1e495c5fd3..945d7d43ac 100644 --- a/cores/esp8266/LwipIntfCB.cpp +++ b/cores/esp8266/LwipIntfCB.cpp @@ -1,44 +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) +bool LwipIntf::stateUpCB(LwipIntf::CBType cb) { - return stateChangeSysCB([cb](netif * nif) - { - if (netif_is_up(nif)) - schedule_function([cb, nif]() + return statusChangeCB( + [cb](netif* interface) + { + if (netif_is_up(interface)) + { + cb(interface); + } + }); +} + +bool LwipIntf::stateDownCB(LwipIntf::CBType cb) +{ + return statusChangeCB( + [cb](netif* interface) { - cb(nif); + if (!netif_is_up(interface)) + { + cb(interface); + } }); - }); } diff --git a/cores/esp8266/LwipIntfDev.h b/cores/esp8266/LwipIntfDev.h index dded934992..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 @@ -11,9 +32,10 @@ #include #include #include +#include #include -#include // wifi_get_macaddr() +#include // wifi_get_macaddr() #include "SPI.h" #include "Schedule.h" @@ -24,63 +46,114 @@ #define DEFAULT_MTU 1500 #endif -template -class LwipIntfDev: public LwipIntf, public RawDev +enum EthernetLinkStatus { + Unknown, + LinkON, + LinkOFF +}; +template +class LwipIntfDev: public LwipIntf, public RawDev +{ public: - - LwipIntfDev(int8_t cs = SS, SPIClass& spi = SPI, int8_t intr = -1): - RawDev(cs, spi, intr), - _mtu(DEFAULT_MTU), - _intrPin(intr), - _started(false), - _default(false) + LwipIntfDev(int8_t cs = SS, SPIClass& spi = SPI, int8_t intr = -1) : + RawDev(cs, spi, intr), _mtu(DEFAULT_MTU), _intrPin(intr), _started(false), _default(false) { memset(&_netif, 0, sizeof(_netif)); } - boolean config(const IPAddress& local_ip, const IPAddress& arg1, const IPAddress& arg2, const IPAddress& arg3, const IPAddress& dns2); + //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); + boolean begin(const uint8_t* macAddress = nullptr, const uint16_t mtu = DEFAULT_MTU); + void end(); const netif* getNetIf() const { return &_netif; } - IPAddress localIP() const + 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))); } - IPAddress subnetMask() const + IPAddress subnetMask() const { return IPAddress(ip4_addr_get_u32(ip_2_ip4(&_netif.netmask))); } - IPAddress gatewayIP() const + IPAddress gatewayIP() const { 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(); -protected: + // 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); - static err_t linkoutput_s(netif *netif, struct pbuf *p); + static err_t linkoutput_s(netif* netif, struct pbuf* p); static void netif_status_callback_s(netif* netif); // called on a regular basis or on interrupt @@ -88,18 +161,20 @@ class LwipIntfDev: public LwipIntf, public RawDev // members - netif _netif; - - uint16_t _mtu; - int8_t _intrPin; - uint8_t _macAddress[6]; - bool _started; - bool _default; + netif _netif; + uint16_t _mtu; + int8_t _intrPin; + uint8_t _macAddress[6]; + bool _started; + bool _scheduled; + bool _default; }; -template -boolean LwipIntfDev::config(const IPAddress& localIP, const IPAddress& gateway, const IPAddress& netmask, const IPAddress& dns1, const IPAddress& dns2) +template +boolean LwipIntfDev::config(const IPAddress& localIP, const IPAddress& gateway, + const IPAddress& netmask, const IPAddress& dns1, + const IPAddress& dns2) { if (_started) { @@ -116,10 +191,39 @@ boolean LwipIntfDev::config(const IPAddress& localIP, const IPAddress& g ip4_addr_set_u32(ip_2_ip4(&_netif.gw), realGateway.v4()); ip4_addr_set_u32(ip_2_ip4(&_netif.netmask), realNetmask.v4()); + if (realDns1.isSet()) + { + // Set DNS1-Server + dns_setserver(0, realDns1); + } + + if (dns2.isSet()) + { + // Set DNS2-Server + dns_setserver(1, dns2); + } return true; } -template +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) { if (mtu) @@ -149,9 +253,9 @@ boolean LwipIntfDev::begin(const uint8_t* macAddress, const uint16_t mtu memset(_macAddress, 0, 6); _macAddress[0] = 0xEE; #endif - _macAddress[3] += _netif.num; // alter base mac address - _macAddress[0] &= 0xfe; // set as locally administered, unicast, per - _macAddress[0] |= 0x02; // https://en.wikipedia.org/wiki/MAC_address#Universal_vs._local + _macAddress[3] += _netif.num; // alter base mac address + _macAddress[0] &= 0xfe; // set as locally administered, unicast, per + _macAddress[0] |= 0x02; // https://en.wikipedia.org/wiki/MAC_address#Universal_vs._local } if (!RawDev::begin(_macAddress)) @@ -170,28 +274,37 @@ boolean LwipIntfDev::begin(const uint8_t* macAddress, const uint16_t mtu ip_addr_copy(netmask, _netif.netmask); ip_addr_copy(gw, _netif.gw); - if (!netif_add(&_netif, ip_2_ip4(&ip_addr), ip_2_ip4(&netmask), ip_2_ip4(&gw), this, netif_init_s, ethernet_input)) + 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; @@ -199,36 +312,65 @@ boolean LwipIntfDev::begin(const uint8_t* macAddress, const uint16_t mtu { if (RawDev::interruptIsPossible()) { - //attachInterrupt(_intrPin, [&]() { this->handlePackets(); }, FALLING); + // attachInterrupt(_intrPin, [&]() { this->handlePackets(); }, FALLING); } else { - ::printf((PGM_P)F("lwIP_Intf: Interrupt not implemented yet, enabling transparent polling\r\n")); + ::printf((PGM_P)F( + "lwIP_Intf: Interrupt not implemented yet, enabling transparent polling\r\n")); _intrPin = -1; } } - if (_intrPin < 0 && !schedule_recurrent_function_us([&]() -{ - this->handlePackets(); - return true; - }, 100)) + if (_intrPin < 0 && !_scheduled) { - netif_remove(&_netif); - return false; + _scheduled = schedule_recurrent_function_us( + [&]() + { + if (!_started) + { + _scheduled = false; + return false; + } + this->handlePackets(); + return true; + }, + 100); + if (!_scheduled) + { + end(); + return false; + } } return true; } -template +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 -err_t LwipIntfDev::linkoutput_s(netif *netif, struct pbuf *pbuf) +template +EthernetLinkStatus LwipIntfDev::linkStatus() +{ + return RawDev::isLinkDetectable() ? _started && RawDev::isLinked() ? LinkON : LinkOFF : Unknown; +} + +template +err_t LwipIntfDev::linkoutput_s(netif* netif, struct pbuf* pbuf) { LwipIntfDev* ths = (LwipIntfDev*)netif->state; @@ -242,37 +384,34 @@ err_t LwipIntfDev::linkoutput_s(netif *netif, struct pbuf *pbuf) #if PHY_HAS_CAPTURE if (phy_capture) { - phy_capture(ths->_netif.num, (const char*)pbuf->payload, pbuf->len, /*out*/1, /*success*/len == pbuf->len); + phy_capture(ths->_netif.num, (const char*)pbuf->payload, pbuf->len, /*out*/ 1, + /*success*/ len == pbuf->len); } #endif return len == pbuf->len ? ERR_OK : ERR_MEM; } -template +template err_t LwipIntfDev::netif_init_s(struct netif* netif) { return ((LwipIntfDev*)netif->state)->netif_init(); } -template +template void LwipIntfDev::netif_status_callback_s(struct netif* netif) { ((LwipIntfDev*)netif->state)->netif_status_callback(); } -template +template err_t LwipIntfDev::netif_init() { - _netif.name[0] = 'e'; - _netif.name[1] = '0' + _netif.num; - _netif.mtu = _mtu; + _netif.name[0] = 'e'; + _netif.name[1] = '0' + _netif.num; + _netif.mtu = _mtu; _netif.chksum_flags = NETIF_CHECKSUM_ENABLE_ALL; - _netif.flags = - NETIF_FLAG_ETHARP - | NETIF_FLAG_IGMP - | NETIF_FLAG_BROADCAST - | NETIF_FLAG_LINK_UP; + _netif.flags = NETIF_FLAG_ETHARP | NETIF_FLAG_IGMP | NETIF_FLAG_BROADCAST | NETIF_FLAG_LINK_UP; // lwIP's doc: This function typically first resolves the hardware // address, then sends the packet. For ethernet physical layer, this is @@ -288,17 +427,28 @@ err_t LwipIntfDev::netif_init() return ERR_OK; } -template +template void LwipIntfDev::netif_status_callback() { + check_route(); if (connected()) { - if (_default) + 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 our gateway is valid netif_set_default(&_netif); } - sntp_stop(); - sntp_init(); } else if (netif_default == &_netif) { @@ -306,14 +456,14 @@ void LwipIntfDev::netif_status_callback() } } -template +template err_t LwipIntfDev::handlePackets() { int pkt = 0; while (1) { if (++pkt == 10) - // prevent starvation + // prevent starvation { return ERR_OK; } @@ -359,7 +509,8 @@ err_t LwipIntfDev::handlePackets() #if PHY_HAS_CAPTURE if (phy_capture) { - phy_capture(_netif.num, (const char*)pbuf->payload, tot_len, /*out*/0, /*success*/err == ERR_OK); + phy_capture(_netif.num, (const char*)pbuf->payload, tot_len, /*out*/ 0, + /*success*/ err == ERR_OK); } #endif @@ -369,18 +520,14 @@ err_t LwipIntfDev::handlePackets() return err; } // (else) allocated pbuf is now lwIP's responsibility - } } -template -void LwipIntfDev::setDefault() +template +void LwipIntfDev::setDefault(bool deflt) { - _default = true; - if (connected()) - { - netif_set_default(&_netif); - } + _default = deflt; + check_route(); } -#endif // _LWIPINTFDEV_H +#endif // _LWIPINTFDEV_H diff --git a/cores/esp8266/PolledTimeout.h b/cores/esp8266/PolledTimeout.h index 7eec547e48..1252ce6c29 100644 --- a/cores/esp8266/PolledTimeout.h +++ b/cores/esp8266/PolledTimeout.h @@ -27,6 +27,7 @@ #include // std::numeric_limits #include // std::is_unsigned #include +#include namespace esp8266 { @@ -45,7 +46,7 @@ struct DoNothing struct YieldOrSkip { - static void execute() {delay(0);} + static void execute() {esp_yield();} }; template diff --git a/cores/esp8266/Print.cpp b/cores/esp8266/Print.cpp index 186d6e1cb1..c2075fa7d8 100644 --- a/cores/esp8266/Print.cpp +++ b/cores/esp8266/Print.cpp @@ -33,15 +33,7 @@ /* default implementation: may be overridden */ size_t Print::write(const uint8_t *buffer, size_t size) { - -#ifdef DEBUG_ESP_CORE - static char not_the_best_way [] PROGMEM STORE_ATTR = "Print::write(data,len) should be overridden for better efficiency\r\n"; - static bool once = false; - if (!once) { - once = true; - os_printf_plus(not_the_best_way); - } -#endif + IAMSLOW(); size_t n = 0; while (size--) { diff --git a/cores/esp8266/Print.h b/cores/esp8266/Print.h index 6b2faed6e6..67f3ba5502 100644 --- a/cores/esp8266/Print.h +++ b/cores/esp8266/Print.h @@ -76,7 +76,7 @@ class Print { inline size_t write(int8_t c) { return write((uint8_t) c); } // default to zero, meaning "a single write may block" - // should be overriden by subclasses with buffering + // should be overridden by subclasses with buffering virtual int availableForWrite() { return 0; } size_t printf(const char * format, ...) __attribute__ ((format (printf, 2, 3))); @@ -110,7 +110,14 @@ class Print { size_t println(const Printable&); size_t println(void); - virtual void flush() { /* Empty implementation for backward compatibility */ } + // flush(): + // Empty implementation by default in Print:: + // should wait for all outgoing characters to be sent, output buffer is empty after this call + virtual void flush() { } + + // by default write timeout is possible (outgoing data from network,serial..) + // (children can override to false (like String)) + virtual bool outputCanTimeout () { return true; } }; template<> size_t Print::printNumber(double number, uint8_t digits); diff --git a/cores/esp8266/Schedule.cpp b/cores/esp8266/Schedule.cpp index b1230ed675..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,13 +132,41 @@ bool schedule_recurrent_function_us(const std::function& fn, } rLast = item; + // grain needs to be recomputed + recurrent_max_grain_mS = 0; + return true; } -void run_scheduled_functions() +uint32_t compute_scheduled_recurrent_grain () { - esp8266::polledTimeout::periodicFastMs yieldNow(100); // yield every 100ms + 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 auto stop = sLast; bool done = false; @@ -161,13 +191,10 @@ void run_scheduled_functions() recycle_fn_unsafe(to_recycle); } - if (yieldNow) - { - // because scheduled functions might last too long for watchdog etc, - // this is yield() in cont stack: - esp_schedule(); - cont_yield(g_pcont); - } + // scheduled functions might last too long for watchdog etc. + // yield() is allowed in scheduled functions, therefore + // recursion into run_scheduled_recurrent_functions() is permitted + optimistic_yield(100000); } } @@ -231,6 +258,9 @@ void run_scheduled_recurrent_functions() } delete(to_ditch); + + // grain needs to be recomputed + recurrent_max_grain_mS = 0; } else { @@ -241,9 +271,10 @@ void run_scheduled_recurrent_functions() if (yieldNow) { // because scheduled functions might last too long for watchdog etc, - // this is yield() in cont stack: + // this is yield() in cont stack, but need to call cont_suspend directly + // to prevent recursion into run_scheduled_recurrent_functions() esp_schedule(); - cont_yield(g_pcont); + cont_suspend(g_pcont); } } while (current && !done); 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 c4d5fc87d3..b9b5b95f65 100644 --- a/cores/esp8266/Stream.cpp +++ b/cores/esp8266/Stream.cpp @@ -22,6 +22,7 @@ #include #include + #define PARSE_TIMEOUT 1000 // default number of milli-seconds to wait #define NO_SKIP_CHAR 1 // a magic char not found in a valid ASCII numeric field @@ -57,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 } } @@ -140,14 +141,14 @@ 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 do { if(c == skipChar) - ; // ignore this charactor + ; // ignore this character else if(c == '-') isNegative = true; else if(c >= '0' && c <= '9') // is c a digit? @@ -175,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 @@ -210,6 +211,8 @@ float Stream::parseFloat(char skipChar) { // the buffer is NOT null terminated. // size_t Stream::readBytes(char *buffer, size_t length) { + IAMSLOW(); + size_t count = 0; while(count < length) { int c = timedRead(); @@ -258,3 +261,46 @@ 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) +{ + IAMSLOW(); + + size_t nbread = 0; + while (nbread < maxLen && available()) + { + int c = read(); + if (c == -1) + break; + buffer[nbread++] = c; + } + return nbread; +} diff --git a/cores/esp8266/Stream.h b/cores/esp8266/Stream.h index 6dcb508d22..0706bec001 100644 --- a/cores/esp8266/Stream.h +++ b/cores/esp8266/Stream.h @@ -22,10 +22,13 @@ #ifndef Stream_h #define Stream_h +#include #include -#include "Print.h" +#include +#include +#include // ssize_t -// compatability macros for testing +// compatibility macros for testing /* #define getInt() parseInt() #define getInt(skipChar) parseInt(skipchar) @@ -35,13 +38,22 @@ readBytesBetween( pre_string, terminator, buffer, length) */ +// Arduino `Client: public Stream` class defines `virtual int read(uint8_t *buf, size_t size) = 0;` +// This function is now imported into `Stream::` for `Stream::send*()`. +// Other classes inheriting from `Stream::` and implementing `read(uint8_t *buf, size_t size)` +// must consequently use `int` as return type, namely Hardware/SoftwareSerial, FileSystems... +#define STREAM_READ_RETURNS_INT 1 + +// Stream::send API is present +#define STREAMSEND_API 1 + class Stream: public Print { protected: unsigned long _timeout = 1000; // number of milliseconds to wait for the next char before aborting timed read 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; @@ -49,10 +61,12 @@ class Stream: public Print { virtual int peek() = 0; Stream() {} + virtual ~Stream() {} // parsing methods void setTimeout(unsigned long timeout); // sets maximum milliseconds to wait for stream data, default is 1 second + unsigned long getTimeout () const { return _timeout; } bool find(const char *target); // reads data from the stream until the target string is found bool find(uint8_t *target) { @@ -101,13 +115,150 @@ 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); } + + //////////////////// extension: direct access to input buffer + // to provide when possible a pointer to available data for read + + // informs user and ::to*() on effective buffered peek API implementation + // by default: not available + virtual bool hasPeekBufferAPI () const { return false; } + + // returns number of byte accessible by peekBuffer() + virtual size_t peekAvailable () { return 0; } + + // returns a pointer to available data buffer (size = peekAvailable()) + // semantic forbids any kind of ::read() + // - after calling peekBuffer() + // - and before calling peekConsume() + virtual const char* peekBuffer () { return nullptr; } + + // consumes bytes after peekBuffer() use + // (then ::read() is allowed) + virtual void peekConsume (size_t consume) { (void)consume; } + + // by default read timeout is possible (incoming data from network,serial..) + // children can override to false (like String::) + virtual bool inputCanTimeout () { return true; } + + // (outputCanTimeout() is defined in Print::) + + //////////////////////// + //////////////////// extensions: Streaming streams to streams + // Stream::send*() + // + // Stream::send*() uses 1-copy transfers when peekBuffer API is + // available, or makes a regular transfer through a temporary buffer. + // + // - for efficiency, Stream classes should implement peekAPI when + // possible + // - for an efficient timeout management, Print/Stream classes + // should implement {output,input}CanTimeout() + + using oneShotMs = esp8266::polledTimeout::oneShotFastMs; + static constexpr int temporaryStackBufferSize = 64; + + // ::send*() methods: + // - always stop before timeout when "no-more-input-possible-data" + // or "no-more-output-possible-data" condition is met + // - always return number of transferred bytes + // 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 (Stream* to) { return sendGeneric(to, -1, -1, oneShotMs::alwaysExpired); } + size_t sendAvailable (Stream& to) { return sendAvailable(&to); } + size_t sendAvailable (Stream&& to) { return sendAvailable(&to); } + + // transfers data until timeout + // returns number of transferred bytes + size_t sendAll (Stream* to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, -1, -1, timeoutMs); } + size_t sendAll (Stream& to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendAll(&to, timeoutMs); } + size_t sendAll (Stream&& to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendAll(&to, timeoutMs); } + + // transfers data until a char is encountered (the char is swallowed but not transferred) with timeout + // returns number of transferred bytes + size_t sendUntil (Stream* to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, -1, readUntilChar, timeoutMs); } + size_t sendUntil (Stream& to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendUntil(&to, readUntilChar, timeoutMs); } + size_t sendUntil (Stream&& to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendUntil(&to, readUntilChar, timeoutMs); } + + // transfers data until requested size or timeout + // returns number of transferred bytes + size_t sendSize (Stream* to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, maxLen, -1, timeoutMs); } + size_t sendSize (Stream& to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendSize(&to, maxLen, timeoutMs); } + size_t sendSize (Stream&& to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendSize(&to, maxLen, timeoutMs); } + + // remaining size (-1 by default = unknown) + virtual ssize_t streamRemaining () { return -1; } + + enum class Report + { + Success = 0, + TimedOut, + ReadError, + WriteError, + ShortOperation, + }; + + 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); + + void setReport (Report report) { _sendReport = report; } + + private: + + Report _sendReport = Report::Success; + + //////////////////// end of extensions protected: - long parseInt(char skipChar); // as above but the given skipChar is ignored - // as above but the given skipChar is ignored + long parseInt(char skipChar); // as parseInt() but the given skipChar is ignored // this allows format characters (typically commas) in values to be ignored - float parseFloat(char skipChar); // as above but the given skipChar is ignored + float parseFloat(char skipChar); // as parseFloat() but the given skipChar is ignored }; #endif diff --git a/cores/esp8266/StreamDev.h b/cores/esp8266/StreamDev.h new file mode 100644 index 0000000000..2aeee4c87a --- /dev/null +++ b/cores/esp8266/StreamDev.h @@ -0,0 +1,251 @@ +/* + StreamDev.h - Stream helpers + Copyright (c) 2019 David Gauchard. All right reserved. + + 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 __STREAMDEV_H +#define __STREAMDEV_H + +#include +#include +#include + +/////////////////////////////////////////////// +// /dev/null +// - black hole as output, swallow everything, availableForWrite = infinite +// - black hole as input, nothing to read, available = 0 + +class StreamNull: public Stream +{ +public: + + // Print + virtual size_t write(uint8_t) override + { + return 1; + } + + virtual size_t write(const uint8_t* buffer, size_t size) override + { + (void)buffer; + (void)size; + return size; + } + + virtual int availableForWrite() override + { + return std::numeric_limits::max(); + } + + // Stream + virtual int available() override + { + return 0; + } + + virtual int read() override + { + return -1; + } + + virtual int peek() override + { + return -1; + } + + virtual size_t readBytes(char* buffer, size_t len) override + { + (void)buffer; + (void)len; + return 0; + } + + virtual int read(uint8_t* buffer, size_t len) override + { + (void)buffer; + (void)len; + return 0; + } + + virtual bool outputCanTimeout() override + { + return false; + } + + virtual bool inputCanTimeout() override + { + return false; + } + + virtual ssize_t streamRemaining() override + { + return 0; + } +}; + +/////////////////////////////////////////////// +// /dev/zero +// - black hole as output, swallow everything, availableForWrite = infinite +// - big bang as input, gives infinity to read, available = infinite + +class StreamZero: public StreamNull +{ +protected: + + char _zero; + +public: + + StreamZero(char zero = 0): _zero(zero) { } + + // Stream + virtual int available() override + { + return std::numeric_limits::max(); + } + + virtual int read() override + { + return _zero; + } + + virtual int peek() override + { + return _zero; + } + + virtual size_t readBytes(char* buffer, size_t len) override + { + memset(buffer, _zero, len); + return len; + } + + virtual int read(uint8_t* buffer, size_t len) override + { + memset((char*)buffer, _zero, len); + return len; + } + + virtual ssize_t streamRemaining() override + { + return std::numeric_limits::max(); + } +}; + +/////////////////////////////////////////////// +// static buffer (in flash or ram) +// - black hole as output, swallow everything, availableForWrite = infinite +// - Stream buffer out as input, resettable + +class StreamConstPtr: public StreamNull +{ +protected: + const char* _buffer; + size_t _size; + bool _byteAddressable; + size_t _peekPointer = 0; + +public: + StreamConstPtr(const String&& string) = delete; // prevents passing String temporary, use ctor(buffer,size) if you know what you are doing + StreamConstPtr(const String& string): _buffer(string.c_str()), _size(string.length()), _byteAddressable(true) { } + StreamConstPtr(const char* buffer, size_t size): _buffer(buffer), _size(size), _byteAddressable(__byteAddressable(buffer)) { } + StreamConstPtr(const uint8_t* buffer, size_t size): _buffer((const char*)buffer), _size(size), _byteAddressable(__byteAddressable(buffer)) { } + StreamConstPtr(const __FlashStringHelper* buffer, size_t size): _buffer(reinterpret_cast(buffer)), _size(size), _byteAddressable(false) { } + StreamConstPtr(const __FlashStringHelper* text): _buffer(reinterpret_cast(text)), _size(strlen_P((PGM_P)text)), _byteAddressable(false) { } + + void resetPointer(int pointer = 0) + { + _peekPointer = pointer; + } + + // Stream + virtual int available() override + { + return peekAvailable(); + } + + virtual int read() override + { + // valid with dram, iram and flash + return _peekPointer < _size ? pgm_read_byte(&_buffer[_peekPointer++]) : -1; + } + + virtual int peek() override + { + // valid with dram, iram and flash + return _peekPointer < _size ? pgm_read_byte(&_buffer[_peekPointer]) : -1; + } + + virtual size_t readBytes(char* buffer, size_t len) override + { + if (_peekPointer >= _size) + { + return 0; + } + size_t cpylen = std::min(_size - _peekPointer, len); + memcpy_P(buffer, _buffer + _peekPointer, cpylen); // whether byte adressible is true + _peekPointer += cpylen; + return cpylen; + } + + virtual int read(uint8_t* buffer, size_t len) override + { + return readBytes((char*)buffer, len); + } + + virtual ssize_t streamRemaining() override + { + return _size; + } + + // peekBuffer + virtual bool hasPeekBufferAPI() const override + { + return _byteAddressable; + } + + virtual size_t peekAvailable() override + { + return _peekPointer < _size ? _size - _peekPointer : 0; + } + + virtual const char* peekBuffer() override + { + return _peekPointer < _size ? _buffer + _peekPointer : nullptr; + } + + virtual void peekConsume(size_t consume) override + { + _peekPointer += consume; + } +}; + +/////////////////////////////////////////////// + +Stream& operator << (Stream& out, String& string); +Stream& operator << (Stream& out, Stream& stream); +Stream& operator << (Stream& out, StreamString& stream); +Stream& operator << (Stream& out, const char* text); +Stream& operator << (Stream& out, const __FlashStringHelper* text); + +/////////////////////////////////////////////// + +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_STREAMDEV) +extern StreamNull devnull; +#endif + +#endif // __STREAMDEV_H diff --git a/cores/esp8266/StreamSend.cpp b/cores/esp8266/StreamSend.cpp new file mode 100644 index 0000000000..b46d1c1560 --- /dev/null +++ b/cores/esp8266/StreamSend.cpp @@ -0,0 +1,402 @@ +/* + StreamDev.cpp - 1-copy transfer related methods + Copyright (c) 2019 David Gauchard. All right reserved. + + 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 + + parsing functions based on TextFinder library by Michael Margolis +*/ + +#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) + { + 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, inputTimeoutMs); + } + + if (readUntilChar >= 0) + { + return SendGenericRegularUntil(to, len, readUntilChar, inputTimeoutMs); + } + + return SendGenericRegular(to, len, inputTimeoutMs); +} + +size_t +Stream::SendGenericPeekBuffer(Print* to, const ssize_t len, const int readUntilChar, + const esp8266::polledTimeout::oneShotFastMs::timeType 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; + + while (!maxLen || written < maxLen) + { + size_t avpk = peekAvailable(); + if (avpk == 0 && !inputCanTimeout()) + { + // no more data to read, ever + break; + } + + size_t w = to->availableForWrite(); + if (w == 0 && !to->outputCanTimeout()) + { + // no more data can be written, ever + break; + } + + w = std::min(w, avpk); + if (maxLen) + { + w = std::min(w, maxLen - written); + } + if (w) + { + const char* directbuf = peekBuffer(); + bool foundChar = false; + if (readUntilChar >= 0) + { + const char* last = (const char*)memchr(directbuf, readUntilChar, w); + if (last) + { + w = std::min((size_t)(last - directbuf), w); + foundChar = true; + } + } + if (w && ((w = to->write(directbuf, w)))) + { + peekConsume(w); + written += w; + timedOut.reset(); // something has been written + } + if (foundChar) + { + peekConsume(1); + break; + } + } + + if (timedOut) + { + // either (maxLen>0) nothing has been transferred for too long + // or readUntilChar >= 0 but char is not encountered for too long + // or (maxLen=0) too much time has been spent here + break; + } + + optimistic_yield(1000); + } + + if (getLastSendReport() == Report::Success && maxLen > 0) + { + if (timeoutMs && timedOut) + { + setReport(Report::TimedOut); + } + else if ((ssize_t)written != len) + { + // This is happening when source cannot timeout (ex: a String) + // but has not enough data, or a dest has closed or cannot + // timeout but is too small (String, buffer...) + // + // Mark it as an error because user usually wants to get what is + // asked for. + setReport(Report::ShortOperation); + } + } + + return written; +} + +size_t +Stream::SendGenericRegularUntil(Print* to, const ssize_t len, const int readUntilChar, + const esp8266::polledTimeout::oneShotFastMs::timeType timeoutMs) +{ + // regular Stream API + // no other choice than reading byte by byte + + 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; + + while (!maxLen || written < maxLen) + { + size_t avr = available(); + if (avr == 0 && !inputCanTimeout()) + { + // no more data to read, ever + break; + } + + size_t w = to->availableForWrite(); + if (w == 0 && !to->outputCanTimeout()) + { + // no more data can be written, ever + break; + } + + int c = read(); + if (c != -1) + { + if (c == readUntilChar) + { + break; + } + w = to->write(c); + if (w != 1) + { + setReport(Report::WriteError); + break; + } + written += 1; + timedOut.reset(); // something has been written + } + + if (timedOut) + { + // either (maxLen>0) nothing has been transferred for too long + // or readUntilChar >= 0 but char is not encountered for too long + // or (maxLen=0) too much time has been spent here + break; + } + + optimistic_yield(1000); + } + + if (getLastSendReport() == Report::Success && maxLen > 0) + { + if (timeoutMs && timedOut) + { + setReport(Report::TimedOut); + } + else if ((ssize_t)written != len) + { + // This is happening when source cannot timeout (ex: a String) + // but has not enough data, or a dest has closed or cannot + // timeout but is too small (String, buffer...) + // + // Mark it as an error because user usually wants to get what is + // asked for. + setReport(Report::ShortOperation); + } + } + + return written; +} + +size_t Stream::SendGenericRegular(Print* to, const ssize_t len, + const esp8266::polledTimeout::oneShotFastMs::timeType timeoutMs) +{ + // regular Stream API + // use an intermediary buffer + + 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; + + while (!maxLen || written < maxLen) + { + size_t avr = available(); + if (avr == 0 && !inputCanTimeout()) + { + // no more data to read, ever + break; + } + + size_t w = to->availableForWrite(); + if (w == 0 && !to->outputCanTimeout()) + // no more data can be written, ever + { + break; + } + + w = std::min(w, avr); + if (maxLen) + { + w = std::min(w, maxLen - written); + } + w = std::min(w, (decltype(w))temporaryStackBufferSize); + if (w) + { + char temp[w]; + ssize_t r = read(temp, w); + if (r < 0) + { + setReport(Report::ReadError); + break; + } + w = to->write(temp, r); + written += w; + if ((size_t)r != w) + { + setReport(Report::WriteError); + break; + } + timedOut.reset(); // something has been written + } + + if (timedOut) + { + // either (maxLen>0) nothing has been transferred for too long + // or readUntilChar >= 0 but char is not encountered for too long + // or (maxLen=0) too much time has been spent here + break; + } + + optimistic_yield(1000); + } + + if (getLastSendReport() == Report::Success && maxLen > 0) + { + if (timeoutMs && timedOut) + { + setReport(Report::TimedOut); + } + else if ((ssize_t)written != len) + { + // This is happening when source cannot timeout (ex: a String) + // but has not enough data, or a dest has closed or cannot + // timeout but is too small (String, buffer...) + // + // Mark it as an error because user usually wants to get what is + // asked for. + setReport(Report::ShortOperation); + } + } + + return written; +} + +Stream& operator<<(Stream& out, String& string) +{ + StreamConstPtr(string).sendAll(out); + return out; +} + +Stream& operator<<(Stream& out, StreamString& stream) +{ + stream.sendAll(out); + return out; +} + +Stream& operator<<(Stream& out, Stream& stream) +{ + if (stream.streamRemaining() < 0) + { + if (stream.inputCanTimeout()) + { + // restrict with only what's buffered on input + stream.sendAvailable(out); + } + else + { + // take all what is in input + stream.sendAll(out); + } + } + else + { + stream.sendSize(out, stream.streamRemaining()); + } + return out; +} + +Stream& operator<<(Stream& out, const char* text) +{ + StreamConstPtr(text, strlen_P(text)).sendAll(out); + return out; +} + +Stream& operator<<(Stream& out, const __FlashStringHelper* text) +{ + StreamConstPtr(text).sendAll(out); + return out; +} + +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_STREAMDEV) +StreamNull devnull; +#endif diff --git a/cores/esp8266/StreamString.cpp b/cores/esp8266/StreamString.cpp deleted file mode 100644 index 24cfe0dd1a..0000000000 --- a/cores/esp8266/StreamString.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/** - StreamString.cpp - - Copyright (c) 2015 Markus Sattler. 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 - - */ - -#include -#include "StreamString.h" - -size_t StreamString::write(const uint8_t *data, size_t size) { - if(size && data) { - const unsigned int newlen = length() + size; - if(reserve(newlen + 1)) { - memcpy((void *) (wbuffer() + len()), (const void *) data, size); - setLen(newlen); - *(wbuffer() + newlen) = 0x00; // add null for string end - return size; - } - DEBUGV(":stream2string: OOM (%d->%d)\n", length(), newlen+1); - } - return 0; -} - -size_t StreamString::write(uint8_t data) { - return concat((char) data); -} - -int StreamString::available() { - return length(); -} - -int StreamString::read() { - if(length()) { - char c = charAt(0); - remove(0, 1); - return c; - - } - return -1; -} - -int StreamString::peek() { - if(length()) { - char c = charAt(0); - return c; - } - return -1; -} - -void StreamString::flush() { -} - diff --git a/cores/esp8266/StreamString.h b/cores/esp8266/StreamString.h index 2e81fa14a0..dced5aee80 100644 --- a/cores/esp8266/StreamString.h +++ b/cores/esp8266/StreamString.h @@ -1,39 +1,290 @@ /** - StreamString.h + StreamString.h - Copyright (c) 2015 Markus Sattler. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Copyright (c) 2020 D. Gauchard. 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 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. + 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 + 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 STREAMSTRING_H_ -#define STREAMSTRING_H_ +#ifndef __STREAMSTRING_H +#define __STREAMSTRING_H +#include +#include +#include "Stream.h" +#include "WString.h" -class StreamString: public Stream, public String { +/////////////////////////////////////////////////////////////// +// S2Stream ("String to Stream") points to a String and makes it a Stream +// (it is also the helper for StreamString) + +class S2Stream: public Stream +{ public: - size_t write(const uint8_t *buffer, size_t size) override; - size_t write(uint8_t data) override; + S2Stream(String& string, int peekPointer = -1) : string(&string), peekPointer(peekPointer) { } + + S2Stream(String* string, int peekPointer = -1) : string(string), peekPointer(peekPointer) { } + + virtual int available() override + { + return string->length(); + } + + virtual int availableForWrite() override + { + return std::numeric_limits::max(); + } + + virtual int read() override + { + if (peekPointer < 0) + { + // consume chars + if (string->length()) + { + char c = string->charAt(0); + string->remove(0, 1); + return c; + } + } + else if (peekPointer < (int)string->length()) + { + // return pointed and move pointer + return string->charAt(peekPointer++); + } + + // everything is read + return -1; + } + + virtual size_t write(uint8_t data) override + { + return string->concat((char)data); + } + + virtual int read(uint8_t* buffer, size_t len) override + { + if (peekPointer < 0) + { + // string will be consumed + size_t l = std::min(len, (size_t)string->length()); + memcpy(buffer, string->c_str(), l); + string->remove(0, l); + return l; + } + + if (peekPointer >= (int)string->length()) + { + return 0; + } + + // only the pointer is moved + size_t l = std::min(len, (size_t)(string->length() - peekPointer)); + memcpy(buffer, string->c_str() + peekPointer, l); + peekPointer += l; + return l; + } + + virtual size_t write(const uint8_t* buffer, size_t len) override + { + return string->concat((const char*)buffer, len) ? len : 0; + } + + virtual int peek() override + { + if (peekPointer < 0) + { + if (string->length()) + { + return string->charAt(0); + } + } + else if (peekPointer < (int)string->length()) + { + return string->charAt(peekPointer); + } + + return -1; + } + + virtual void flush() override + { + // nothing to do + } + + virtual bool inputCanTimeout() override + { + return false; + } + + virtual bool outputCanTimeout() override + { + return false; + } + + //// Stream's peekBufferAPI + + virtual bool hasPeekBufferAPI() const override + { + return true; + } - int available() override; - int read() override; - int peek() override; - void flush() override; + virtual size_t peekAvailable() + { + if (peekPointer < 0) + { + return string->length(); + } + return string->length() - peekPointer; + } + + virtual const char* peekBuffer() override + { + if (peekPointer < 0) + { + return string->c_str(); + } + if (peekPointer < (int)string->length()) + { + return string->c_str() + peekPointer; + } + return nullptr; + } + + virtual void peekConsume(size_t consume) override + { + if (peekPointer < 0) + { + // string is really consumed + string->remove(0, consume); + } + else + { + // only the pointer is moved + peekPointer = std::min((size_t)string->length(), peekPointer + consume); + } + } + + virtual ssize_t streamRemaining() override + { + return peekPointer < 0 ? string->length() : string->length() - peekPointer; + } + + // calling setConsume() will make the string consumed as the stream is read. + // (default behaviour) + void setConsume() + { + peekPointer = -1; + } + + // 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 = std::min(pointer, (size_t)string->length()); + } + +protected: + String* string; + int peekPointer; // -1:String is consumed / >=0:resettable pointer }; +/////////////////////////////////////////////////////////////// +// StreamString is a S2Stream holding the String + +class StreamString: public String, public S2Stream +{ +protected: + void resetpp() + { + if (peekPointer > 0) + { + peekPointer = 0; + } + } + +public: + StreamString(StreamString&& bro) : String(bro), S2Stream(this) { } + StreamString(const StreamString& bro) : String(bro), S2Stream(this) { } + + // duplicate String constructors and operator=: + + StreamString(const char* text = nullptr) : String(text), S2Stream(this) { } + StreamString(const String& string) : String(string), S2Stream(this) { } + StreamString(const __FlashStringHelper* str) : String(str), S2Stream(this) { } + StreamString(String&& string) : String(string), S2Stream(this) { } + + explicit StreamString(char c) : String(c), S2Stream(this) { } + explicit StreamString(unsigned char c, unsigned char base = 10) : + String(c, base), S2Stream(this) + { + } + explicit StreamString(int i, unsigned char base = 10) : String(i, base), S2Stream(this) { } + explicit StreamString(unsigned int i, unsigned char base = 10) : String(i, base), S2Stream(this) + { + } + explicit StreamString(long l, unsigned char base = 10) : String(l, base), S2Stream(this) { } + explicit StreamString(unsigned long l, unsigned char base = 10) : + String(l, base), S2Stream(this) + { + } + explicit StreamString(float f, unsigned char decimalPlaces = 2) : + String(f, decimalPlaces), S2Stream(this) + { + } + explicit StreamString(double d, unsigned char decimalPlaces = 2) : + String(d, decimalPlaces), S2Stream(this) + { + } + + StreamString& operator=(const StreamString& rhs) + { + String::operator=(rhs); + resetpp(); + return *this; + } + + StreamString& operator=(const String& rhs) + { + String::operator=(rhs); + resetpp(); + return *this; + } + + StreamString& operator=(const char* cstr) + { + String::operator=(cstr); + resetpp(); + return *this; + } + + StreamString& operator=(const __FlashStringHelper* str) + { + String::operator=(str); + resetpp(); + return *this; + } + + StreamString& operator=(String&& rval) + { + String::operator=(rval); + resetpp(); + return *this; + } +}; -#endif /* STREAMSTRING_H_ */ +#endif // __STREAMSTRING_H diff --git a/cores/esp8266/TZ.h b/cores/esp8266/TZ.h index cd083e7fd7..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 -// Thu Nov 12 04:07:03 UTC 2020 -// -// 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,13 +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_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") @@ -188,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") @@ -208,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") @@ -220,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") @@ -240,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") @@ -282,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") @@ -293,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") @@ -310,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") @@ -350,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") @@ -361,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") @@ -377,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("<+04>-4") +#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") @@ -399,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") @@ -408,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") @@ -429,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/Tone.cpp b/cores/esp8266/Tone.cpp index 8deeb63d09..b4dc93c642 100644 --- a/cores/esp8266/Tone.cpp +++ b/cores/esp8266/Tone.cpp @@ -30,10 +30,8 @@ static void _startTone(uint8_t _pin, uint32_t high, uint32_t low, uint32_t durat return; } -#ifndef WAVEFORM_LOCKED_PHASE // Stop any analogWrites (PWM) because they are a different generator _stopPWM(_pin); -#endif // If there's another Tone or startWaveform on this pin // it will be changed on-the-fly (no need to stop it) 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 d9d468765a..ef79a5cbf3 100644 --- a/cores/esp8266/Updater.cpp +++ b/cores/esp8266/Updater.cpp @@ -1,8 +1,12 @@ +#include #include "Updater.h" #include "eboot_command.h" #include +#include #include "StackThunk.h" +#include + //#define DEBUG_UPDATER Serial #include @@ -23,8 +27,7 @@ extern "C" { #include "user_interface.h" } -extern "C" uint32_t _FS_start; -extern "C" uint32_t _FS_end; +#include // not "flash_hal.h": can use hijacked MOCK version UpdaterClass::UpdaterClass() { @@ -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.")); @@ -117,7 +122,7 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) { if (command == U_FLASH) { //address of the end of the space available for sketch and update - uintptr_t updateEndAddress = (uintptr_t)&_FS_start - 0x40200000; + uintptr_t updateEndAddress = FS_start - 0x40200000; updateStartAddress = (updateEndAddress > roundedSize)? (updateEndAddress - roundedSize) : 0; @@ -129,19 +134,19 @@ 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; } } else if (command == U_FS) { - if((uintptr_t)&_FS_start + roundedSize > (uintptr_t)&_FS_end) { + if(FS_start + roundedSize > FS_end) { _setError(UPDATE_ERROR_SPACE); return false; } #ifdef ATOMIC_FS_UPDATE //address of the end of the space available for update - uintptr_t updateEndAddress = (uintptr_t)&_FS_start - 0x40200000; + uintptr_t updateEndAddress = FS_start - 0x40200000; updateStartAddress = (updateEndAddress > roundedSize)? (updateEndAddress - roundedSize) : 0; @@ -150,7 +155,7 @@ bool UpdaterClass::begin(size_t size, int command, int ledPin, uint8_t ledOn) { return false; } #else - updateStartAddress = (uintptr_t)&_FS_start - 0x40200000; + updateStartAddress = FS_start - 0x40200000; #endif } else { @@ -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] sigLen: %d\n"), sigLen); + DEBUG_UPDATER.printf_P(PSTR("[Updater] expected sigLen: %u\n"), expectedSigLen); #endif - if (sigLen != _verify->length()) { + if (expectedSigLen > 0) { + ESP.flashRead(sigLenAddr, &sigLen, SigSize); +#ifdef DEBUG_UPDATER + DEBUG_UPDATER.printf_P(PSTR("[Updater] sigLen from flash: %u\n"), sigLen); +#endif + } + + 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 } @@ -313,7 +360,7 @@ bool UpdaterClass::end(bool evenIfRemaining){ eboot_command ebcmd; ebcmd.action = ACTION_COPY_RAW; ebcmd.args[0] = _startAddress; - ebcmd.args[1] = (uintptr_t)&_FS_start - 0x40200000; + ebcmd.args[1] = FS_start - 0x40200000; ebcmd.args[2] = _size; eboot_command_write(&ebcmd); #endif @@ -359,7 +406,7 @@ bool UpdaterClass::_writeBuffer(){ modifyFlashMode = true; } } - + if (eraseResult) { if(!_async) yield(); writeResult = ESP.flashWrite(_currentAddress, _buffer, _bufferLen); @@ -409,7 +456,7 @@ size_t UpdaterClass::write(uint8_t *data, size_t len) { left -= toBuff; if(!_async) yield(); } - //lets see whats left + //lets see what's left memcpy(_buffer + _bufferLen, data + (len - left), left); _bufferLen += left; if(_bufferLen == remaining()){ @@ -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,18 +502,22 @@ bool UpdaterClass::_verifyEnd() { return true; } else if (buf[0] != 0xE9) { _currentAddress = (_startAddress); - _setError(UPDATE_ERROR_MAGIC_BYTE); + _setError(UPDATE_ERROR_MAGIC_BYTE); return false; } +// it makes no sense to check flash size in auto flash mode +// (sketch size would have to be set in bin header, instead of flash size) +#if !FLASH_MAP_SUPPORT uint32_t bin_flash_size = ESP.magicFlashChipSize((buf[3] & 0xf0) >> 4); // 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 return true; } else if(_command == U_FS) { @@ -476,7 +527,7 @@ bool UpdaterClass::_verifyEnd() { return false; } -size_t UpdaterClass::writeStream(Stream &data) { +size_t UpdaterClass::writeStream(Stream &data, uint16_t streamTimeout) { size_t written = 0; size_t toRead = 0; if(hasError() || !isRunning()) @@ -489,6 +540,7 @@ size_t UpdaterClass::writeStream(Stream &data) { _reset(); return 0; } + esp8266::polledTimeout::oneShotMs timeOut(streamTimeout); if (_progress_callback) { _progress_callback(0, _size); } @@ -506,13 +558,15 @@ size_t UpdaterClass::writeStream(Stream &data) { } toRead = data.readBytes(_buffer + _bufferLen, bytesToRead); if(toRead == 0) { //Timeout - delay(100); - toRead = data.readBytes(_buffer + _bufferLen, bytesToRead); - if(toRead == 0) { //Timeout - _currentAddress = (_startAddress + _size); - _setError(UPDATE_ERROR_STREAM); - return written; - } + if (timeOut) { + _currentAddress = (_startAddress + _size); + _setError(UPDATE_ERROR_STREAM); + _reset(); + return written; + } + delay(100); + } else { + timeOut.reset(); } if(_ledPin != -1) { digitalWrite(_ledPin, !_ledOn); // Switch LED off @@ -534,45 +588,85 @@ size_t UpdaterClass::writeStream(Stream &data) { 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 14a4a3baa9..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; } @@ -83,7 +88,7 @@ class UpdaterClass { Should be equal to the remaining bytes when called Usable for slow streams like Serial */ - size_t writeStream(Stream &data); + size_t writeStream(Stream &data, uint16_t streamTimeout = 60000); /* If all bytes are written @@ -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 */ @@ -108,19 +118,46 @@ class UpdaterClass { bool setMD5(const char * expected_md5); /* - returns the MD5 String of the sucessfully ended firmware + returns the MD5 String of the successfully ended firmware */ String md5String(void){ return _md5.toString(); } /* - populated the result with the md5 bytes of the sucessfully ended firmware + populated the result with the md5 bytes of the successfully ended firmware */ void md5(uint8_t * result){ return _md5.getBytes(result); } /* 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/WMath.cpp b/cores/esp8266/WMath.cpp index 2cc20b9f50..d8dc1f82f2 100644 --- a/cores/esp8266/WMath.cpp +++ b/cores/esp8266/WMath.cpp @@ -23,6 +23,8 @@ $Id$ */ +#include "Arduino.h" + extern "C" { #include } @@ -77,10 +79,10 @@ long map(long x, long in_min, long in_max, long out_min, long out_max) { return (delta * dividend + (divisor / 2)) / divisor + out_min; } -unsigned int makeWord(unsigned int w) { +uint16_t makeWord(uint16_t w) { return w; } -unsigned int makeWord(unsigned char h, unsigned char l) { +uint16_t makeWord(byte h, byte l) { return (h << 8) | l; } diff --git a/cores/esp8266/WString.cpp b/cores/esp8266/WString.cpp index 07ab99b2cf..1e608c3c92 100644 --- a/cores/esp8266/WString.cpp +++ b/cores/esp8266/WString.cpp @@ -21,10 +21,100 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include +#include "Arduino.h" #include "WString.h" #include "stdlib_noniso.h" +#include + +#define OOM_STRING_BORDER_DISPLAY 10 +#define OOM_STRING_THRESHOLD_REALLOC_WARN 128 + +#define __STRHELPER(x) #x +#define STR(x) __STRHELPER(x) // stringifier + +/*********************************************/ +/* Conversion helpers */ +/*********************************************/ + +static String toString(unsigned char value, unsigned char base) { + String out; + + char buf[1 + std::numeric_limits::digits]; + out = utoa(value, buf, base); + + return out; +} + +static String toString(int value, unsigned char base) { + String out; + + char buf[2 + std::numeric_limits::digits]; + out = itoa(value, buf, base); + + return out; +} + +static String toString(unsigned int value, unsigned char base) { + String out; + + char buf[1 + std::numeric_limits::digits]; + out = utoa(value, buf, base); + + return out; +} + +static String toString(long value, unsigned char base) { + String out; + + char buf[2 + std::numeric_limits::digits]; + out = ltoa(value, buf, base); + + return out; +} + +static String toString(unsigned long value, unsigned char base) { + String out; + + char buf[1 + std::numeric_limits::digits]; + out = ultoa(value, buf, base); + + return out; +} + +// TODO: {u,}lltoa don't guarantee that the buffer is usable directly, one should always use the returned pointer + +static String toString(long long value, unsigned char base) { + String out; + + char buf[2 + std::numeric_limits::digits]; + out = lltoa(value, buf, sizeof(buf), base); + + return out; +} + +static String toString(unsigned long long value, unsigned char base) { + String out; + + char buf[1 + std::numeric_limits::digits]; + out = ulltoa(value, buf, sizeof(buf), base); + + 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); +} + /*********************************************/ /* Constructors */ /*********************************************/ @@ -50,65 +140,41 @@ String::String(String &&rval) noexcept { move(rval); } -String::String(StringSumHelper &&rval) noexcept { - init(); - move(rval); -} +String::String(unsigned char value, unsigned char base) : + String(toString(value, base)) +{} -String::String(unsigned char value, unsigned char base) { - init(); - char buf[1 + 8 * sizeof(unsigned char)]; - utoa(value, buf, base); - *this = buf; -} +String::String(int value, unsigned char base) : + String(toString(value, base)) +{} -String::String(int value, unsigned char base) { - init(); - char buf[2 + 8 * sizeof(int)]; - if (base == 10) { - sprintf(buf, "%d", value); - } else { - itoa(value, buf, base); - } - *this = buf; -} +String::String(unsigned int value, unsigned char base) : + String(toString(value, base)) +{} -String::String(unsigned int value, unsigned char base) { - init(); - char buf[1 + 8 * sizeof(unsigned int)]; - utoa(value, buf, base); - *this = buf; -} +String::String(long value, unsigned char base) : + String(toString(value, base)) +{} -String::String(long value, unsigned char base) { - init(); - char buf[2 + 8 * sizeof(long)]; - if (base == 10) { - sprintf(buf, "%ld", value); - } else { - ltoa(value, buf, base); - } - *this = buf; -} +String::String(unsigned long value, unsigned char base) : + String(toString(value, base)) +{} -String::String(unsigned long value, unsigned char base) { - init(); - char buf[1 + 8 * sizeof(unsigned long)]; - ultoa(value, buf, base); - *this = buf; -} +String::String(long long value, unsigned char base) : + String(toString(value, base)) +{} -String::String(float value, unsigned char decimalPlaces) { - init(); - char buf[33]; - *this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf); -} +String::String(unsigned long long value, unsigned char base) : + String(toString(value, base)) +{} -String::String(double value, unsigned char decimalPlaces) { - init(); - char buf[33]; - *this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf); -} +String::String(float value, unsigned char decimalPlaces) : + String(toString(value, decimalPlaces)) +{} + +String::String(double value, unsigned char decimalPlaces) : + String(toString(value, decimalPlaces)) +{} /*********************************************/ /* Memory Management */ @@ -120,18 +186,27 @@ void String::invalidate(void) { init(); } -unsigned char String::reserve(unsigned int size) { +bool String::reserve(unsigned int size) { if (buffer() && capacity() >= size) - return 1; + return true; if (changeBuffer(size)) { if (len() == 0) wbuffer()[0] = 0; - return 1; + return true; } - return 0; + 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 -unsigned char String::changeBuffer(unsigned int maxStrLen) { +bool String::changeBuffer(unsigned int maxStrLen) { // Can we use SSO here to avoid allocation? if (maxStrLen < sizeof(sso.buff) - 1) { if (isSSO() || !buffer()) { @@ -148,13 +223,24 @@ unsigned char String::changeBuffer(unsigned int maxStrLen) { memcpy(wbuffer(), temp, maxStrLen); free((void *)temp); } - return 1; + return true; } // Fallthrough to normal allocator size_t newSize = (maxStrLen + 16) & (~0xf); +#ifdef DEBUG_ESP_PORT + if (!isSSO() && capacity() >= OOM_STRING_THRESHOLD_REALLOC_WARN && maxStrLen > capacity()) { + // warn when badly re-allocating + 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) { - return 0; +#ifdef DEBUG_ESP_PORT + identifyString(*this); + DEBUG_ESP_PORT.printf("Maximum capacity reached (" STR(CAPACITY_MAX) ")\n"); +#endif + return false; } uint16_t oldLen = len(); char *newbuffer = (char *)realloc(isSSO() ? nullptr : wbuffer(), newSize); @@ -171,9 +257,13 @@ unsigned char String::changeBuffer(unsigned int maxStrLen) { setCapacity(newSize - 1); setLen(oldLen); // Needed in case of SSO where len() never existed setBuffer(newbuffer); - return 1; + return true; } - return 0; +#ifdef DEBUG_ESP_PORT + identifyString(*this); + DEBUG_ESP_PORT.printf("OOM: %d -> %zu bytes\n", isSSO() ? 0: capacity(), newSize); +#endif + return false; } /*********************************************/ @@ -186,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; } @@ -196,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; } @@ -238,186 +330,198 @@ String &String::operator =(const __FlashStringHelper *pstr) { return *this; } +String &String::operator =(char c) { + char buffer[2] { c, '\0' }; + *this = buffer; + return *this; +} + /*********************************************/ /* concat */ /*********************************************/ -unsigned char String::concat(const String &s) { +bool String::concat(const String &s) { // Special case if we're concatting ourself (s += s;) since we may end up // realloc'ing the buffer and moving s.buffer in the method called if (&s == this) { unsigned int newlen = 2 * len(); if (!s.buffer()) - return 0; + return false; if (s.len() == 0) - return 1; + return true; if (!reserve(newlen)) - return 0; + return false; memmove_P(wbuffer() + len(), buffer(), len()); setLen(newlen); wbuffer()[newlen] = 0; - return 1; + return true; } else { return concat(s.buffer(), s.len()); } } -unsigned char String::concat(const char *cstr, unsigned int length) { +bool String::concat(const char *cstr, unsigned int length) { unsigned int newlen = len() + length; if (!cstr) - return 0; + return false; if (length == 0) - return 1; + return true; if (!reserve(newlen)) - return 0; - memmove_P(wbuffer() + len(), cstr, length + 1); + return false; + memmove_P(wbuffer() + len(), cstr, length); setLen(newlen); wbuffer()[newlen] = 0; - return 1; + return true; } -unsigned char String::concat(const char *cstr) { +bool String::concat(const char *cstr) { if (!cstr) - return 0; + return false; return concat(cstr, strlen(cstr)); } -unsigned char String::concat(char c) { +bool String::concat(char c) { return concat(&c, 1); } -unsigned char String::concat(unsigned char num) { - char buf[1 + 3 * sizeof(unsigned char)]; - return concat(buf, sprintf(buf, "%d", num)); +bool String::concat(unsigned char num) { + return concat(String(num)); +} + +bool String::concat(int num) { + return concat(String(num)); +} + +bool String::concat(unsigned int num) { + return concat(String(num)); } -unsigned char String::concat(int num) { - char buf[2 + 3 * sizeof(int)]; - return concat(buf, sprintf(buf, "%d", num)); +bool String::concat(long num) { + return concat(String(num)); } -unsigned char String::concat(unsigned int num) { - char buf[1 + 3 * sizeof(unsigned int)]; - utoa(num, buf, 10); - return concat(buf, strlen(buf)); +bool String::concat(unsigned long num) { + return concat(String(num)); } -unsigned char String::concat(long num) { - char buf[2 + 3 * sizeof(long)]; - return concat(buf, sprintf(buf, "%ld", num)); +bool String::concat(long long num) { + return concat(String(num)); } -unsigned char String::concat(unsigned long num) { - char buf[1 + 3 * sizeof(unsigned long)]; - ultoa(num, buf, 10); - return concat(buf, strlen(buf)); +bool String::concat(unsigned long long num) { + return concat(String(num)); } -unsigned char String::concat(float num) { - char buf[20]; - char *string = dtostrf(num, 4, 2, buf); - return concat(string, strlen(string)); +bool String::concat(float num) { + return concat(String(num)); } -unsigned char String::concat(double num) { - char buf[20]; - char *string = dtostrf(num, 4, 2, buf); - return concat(string, strlen(string)); +bool String::concat(double num) { + return concat(String(num)); } -unsigned char String::concat(const __FlashStringHelper *str) { +bool String::concat(const __FlashStringHelper *str) { if (!str) - return 0; + return false; int length = strlen_P((PGM_P)str); if (length == 0) - return 1; + return true; unsigned int newlen = len() + length; if (!reserve(newlen)) - return 0; - memcpy_P(wbuffer() + len(), (PGM_P)str, length + 1); + return false; + memcpy_P(wbuffer() + len(), (PGM_P)str, length); setLen(newlen); - return 1; + wbuffer()[newlen] = 0; + return true; } /*********************************************/ -/* Concatenate */ +/* Insert */ /*********************************************/ -StringSumHelper &operator +(const StringSumHelper &lhs, const String &rhs) { - StringSumHelper &a = const_cast(lhs); - if (!a.concat(rhs.buffer(), rhs.len())) - a.invalidate(); - return a; -} +String &String::insert(size_t position, const char *other, size_t other_length) { + if (position > length()) + return *this; -StringSumHelper &operator +(const StringSumHelper &lhs, const char *cstr) { - StringSumHelper &a = const_cast(lhs); - if (!cstr || !a.concat(cstr, strlen(cstr))) - a.invalidate(); - return a; -} + auto len = length(); + auto total = len + other_length; + if (!reserve(total)) + return *this; + + auto left = len - position; + setLen(total); + + auto *start = wbuffer() + position; + memmove(start + other_length, start, left); + memmove_P(start, other, other_length); + wbuffer()[total] = '\0'; -StringSumHelper &operator +(const StringSumHelper &lhs, char c) { - StringSumHelper &a = const_cast(lhs); - if (!a.concat(c)) - a.invalidate(); - return a; + return *this; } -StringSumHelper &operator +(const StringSumHelper &lhs, unsigned char num) { - StringSumHelper &a = const_cast(lhs); - if (!a.concat(num)) - a.invalidate(); - return a; +String &String::insert(size_t position, const __FlashStringHelper *other) { + auto *p = reinterpret_cast(other); + return insert(position, p, strlen_P(p)); } -StringSumHelper &operator +(const StringSumHelper &lhs, int num) { - StringSumHelper &a = const_cast(lhs); - if (!a.concat(num)) - a.invalidate(); - return a; +String &String::insert(size_t position, char other) { + char tmp[2] { other, '\0' }; + return insert(position, tmp, 1); } -StringSumHelper &operator +(const StringSumHelper &lhs, unsigned int num) { - StringSumHelper &a = const_cast(lhs); - if (!a.concat(num)) - a.invalidate(); - return a; +String &String::insert(size_t position, const char *other) { + return insert(position, other, strlen(other)); } -StringSumHelper &operator +(const StringSumHelper &lhs, long num) { - StringSumHelper &a = const_cast(lhs); - if (!a.concat(num)) - a.invalidate(); - return a; +String &String::insert(size_t position, const String &other) { + return insert(position, other.c_str(), other.length()); } -StringSumHelper &operator +(const StringSumHelper &lhs, unsigned long num) { - StringSumHelper &a = const_cast(lhs); - if (!a.concat(num)) - a.invalidate(); - return a; +String operator +(const String &lhs, String &&rhs) { + String res; + auto total = lhs.length() + rhs.length(); + if (rhs.capacity() > total) { + rhs.insert(0, lhs); + res = std::move(rhs); + } else { + res.reserve(total); + res += lhs; + res += rhs; + rhs.invalidate(); + } + + return res; } -StringSumHelper &operator +(const StringSumHelper &lhs, float num) { - StringSumHelper &a = const_cast(lhs); - if (!a.concat(num)) - a.invalidate(); - return a; +String operator +(String &&lhs, String &&rhs) { + String res; + auto total = lhs.length() + rhs.length(); + if ((total > lhs.capacity()) && (total < rhs.capacity())) { + rhs.insert(0, lhs); + res = std::move(rhs); + } else { + lhs += rhs; + rhs.invalidate(); + res = std::move(lhs); + } + + return res; } -StringSumHelper &operator +(const StringSumHelper &lhs, double num) { - StringSumHelper &a = const_cast(lhs); - if (!a.concat(num)) - a.invalidate(); - return a; +String operator +(char lhs, const String &rhs) { + String res; + res.reserve(rhs.length() + 1); + res += lhs; + res += rhs; + return res; } -StringSumHelper &operator +(const StringSumHelper &lhs, const __FlashStringHelper *rhs) { - StringSumHelper &a = const_cast(lhs); - if (!a.concat(rhs)) - a.invalidate(); - return a; +String operator +(const char *lhs, const String &rhs) { + String res; + res.reserve(strlen_P(lhs) + rhs.length()); + res += lhs; + res += rhs; + return res; } /*********************************************/ @@ -435,11 +539,11 @@ int String::compareTo(const String &s) const { return strcmp(buffer(), s.buffer()); } -unsigned char String::equals(const String &s2) const { +bool String::equals(const String &s2) const { return (len() == s2.len() && compareTo(s2) == 0); } -unsigned char String::equals(const char *cstr) const { +bool String::equals(const char *cstr) const { if (len() == 0) return (cstr == NULL || *cstr == 0); if (cstr == NULL) @@ -447,36 +551,44 @@ unsigned char String::equals(const char *cstr) const { return strcmp(buffer(), cstr) == 0; } -unsigned char String::operator<(const String &rhs) const { +bool String::equals(const __FlashStringHelper *s) const { + return equals(String(s)); +} + +bool String::operator<(const String &rhs) const { return compareTo(rhs) < 0; } -unsigned char String::operator>(const String &rhs) const { +bool String::operator>(const String &rhs) const { return compareTo(rhs) > 0; } -unsigned char String::operator<=(const String &rhs) const { +bool String::operator<=(const String &rhs) const { return compareTo(rhs) <= 0; } -unsigned char String::operator>=(const String &rhs) const { +bool String::operator>=(const String &rhs) const { return compareTo(rhs) >= 0; } -unsigned char String::equalsIgnoreCase(const String &s2) const { +bool String::equalsIgnoreCase(const String &s2) const { if (this == &s2) - return 1; + return true; if (len() != s2.len()) - return 0; + return false; if (len() == 0) - return 1; + return true; const char *p1 = buffer(); const char *p2 = s2.buffer(); while (*p1) { if (tolower(*p1++) != tolower(*p2++)) - return 0; + return false; } - return 1; + return true; +} + +bool String::equalsIgnoreCase(const __FlashStringHelper *s) const { + return equalsIgnoreCase(String(s)); } unsigned char String::equalsConstantTime(const String &s2) const { @@ -506,24 +618,43 @@ unsigned char String::equalsConstantTime(const String &s2) const { return (equalcond & diffcond); //bitwise AND } -unsigned char String::startsWith(const String &s2) const { +bool String::startsWith(const String &s2) const { if (len() < s2.len()) - return 0; + return false; return startsWith(s2, 0); } -unsigned char String::startsWith(const String &s2, unsigned int offset) const { +bool String::startsWith(const char *prefix) const { + return this->startsWith(String(prefix)); +} +bool String::startsWith(const __FlashStringHelper *prefix) const { + return this->startsWith(String(prefix)); +} + +bool String::startsWith(const String &s2, unsigned int offset) const { if (offset > (unsigned)(len() - s2.len()) || !buffer() || !s2.buffer()) - return 0; + return false; return strncmp(&buffer()[offset], s2.buffer(), s2.len()) == 0; } -unsigned char String::endsWith(const String &s2) const { +bool String::startsWith(const __FlashStringHelper *prefix, unsigned int offset) const { + return startsWith(String(prefix), offset); +} + +bool String::endsWith(const String &s2) const { if (len() < s2.len() || !buffer() || !s2.buffer()) - return 0; + return false; return strcmp(&buffer()[len() - s2.len()], s2.buffer()) == 0; } +bool String::endsWith(const char *suffix) const { + return this->endsWith(String(suffix)); +} +bool String::endsWith(const __FlashStringHelper *suffix) const { + return this->endsWith(String(suffix)); +} + + /*********************************************/ /* Character Access */ /*********************************************/ @@ -544,7 +675,7 @@ char &String::operator[](unsigned int index) { char String::operator[](unsigned int index) const { if (index >= len() || !buffer()) - return 0; + return '\0'; return buffer()[index]; } @@ -595,14 +726,9 @@ int String::lastIndexOf(char ch) const { int String::lastIndexOf(char ch, unsigned int fromIndex) const { if (fromIndex >= len()) return -1; - char *writeTo = wbuffer(); - char tempchar = writeTo[fromIndex + 1]; // save the replaced character - writeTo[fromIndex + 1] = '\0'; - char *temp = strrchr(writeTo, ch); - writeTo[fromIndex + 1] = tempchar; // restore character - if (temp == NULL) - return -1; - return temp - writeTo; + int index = fromIndex + 1; + while (index-- > 0 && buffer()[index] != ch); + return index; } int String::lastIndexOf(const String &s2) const { @@ -625,6 +751,15 @@ int String::lastIndexOf(const String &s2, unsigned int fromIndex) const { return found; } +int String::lastIndexOf(const __FlashStringHelper *str) const { + return lastIndexOf(String(str)); +} + +int String::lastIndexOf(const __FlashStringHelper *str, unsigned int fromIndex) const { + return lastIndexOf(String(str), fromIndex); +} + + String String::substring(unsigned int left, unsigned int right) const { if (left > right) { unsigned int temp = right; @@ -636,11 +771,7 @@ String String::substring(unsigned int left, unsigned int right) const { return out; if (right > len()) right = len(); - char *writeTo = wbuffer(); - char tempchar = writeTo[right]; // save the replaced character - writeTo[right] = '\0'; - out = writeTo + left; // pointer arithmetic - writeTo[right] = tempchar; // restore character + out.concat(buffer() + left, right - left); return out; } @@ -689,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(); @@ -703,6 +834,24 @@ void String::replace(const String &find, const String &replace) { } } + +void String::replace(const char *find, const String &replace) { + this->replace(String(find), replace); +} +void String::replace(const __FlashStringHelper *find, const String &replace) { + this->replace(String(find), replace); +} +void String::replace(const char *find, const char *replace) { + this->replace(String(find), String(replace)); +} +void String::replace(const __FlashStringHelper *find, const char *replace) { + this->replace(String(find), String(replace)); +} +void String::replace(const __FlashStringHelper *find, const __FlashStringHelper *replace) { + this->replace(String(find), String(replace)); +} + + void String::remove(unsigned int index, unsigned int count) { if (index >= len()) { return; diff --git a/cores/esp8266/WString.h b/cores/esp8266/WString.h index dcb0982387..1a2aebb298 100644 --- a/cores/esp8266/WString.h +++ b/cores/esp8266/WString.h @@ -23,30 +23,28 @@ #define String_class_h #ifdef __cplusplus -#include -#include -#include #include -// An inherited class for holding the result of a concatenation. These -// result objects are assumed to be writable by subsequent concatenations. -class StringSumHelper; +#include +#include +#include +#include + +#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)) #define F(string_literal) (FPSTR(PSTR(string_literal))) +// support libraries that expect this name to be available +// replace with `using StringSumHelper = String;` in case something wants this constructible +class StringSumHelper; + // The string class class String { - // use a function pointer to allow for "if (s)" without the - // complications of an operator bool(). for more information, see: - // http://www.artima.com/cppsource/safebool.html - typedef void (String::*StringIfHelperType)() const; - void StringIfHelper() const { - } - public: // constructors // creates a copy of the initial value. @@ -60,20 +58,59 @@ class String { String(const String &str); String(const __FlashStringHelper *str); String(String &&rval) noexcept; - String(StringSumHelper &&rval) noexcept; + explicit String(char c) { sso.buff[0] = c; sso.buff[1] = 0; sso.len = 1; sso.isHeap = 0; } - explicit String(unsigned char, unsigned char base = 10); - explicit String(int, unsigned char base = 10); - explicit String(unsigned int, unsigned char base = 10); - explicit String(long, unsigned char base = 10); - explicit String(unsigned long, unsigned char base = 10); - explicit String(float, unsigned char decimalPlaces = 2); - explicit String(double, unsigned char decimalPlaces = 2); + + String(unsigned char, unsigned char base); + explicit String(unsigned char value) : + String(value, 10) + {} + + String(int, unsigned char base); + explicit String(int value) : + String(value, 10) + {} + + String(unsigned int, unsigned char base); + explicit String(unsigned int value) : + String(value, 10) + {} + + String(long, unsigned char base); + explicit String(long value) : + String(value, 10) + {} + + String(unsigned long, unsigned char base); + explicit String(unsigned long value) : + String(value, 10) + {} + + String(long long, unsigned char base); + explicit String(long long value) : + String(value, 10) + {} + + String(unsigned long long, unsigned char base); + explicit String(unsigned long long value) : + String(value, 10) + {} + + String(float, unsigned char decimalPlaces); + explicit String(float value) : + String(value, 2) + {} + + String(double, unsigned char decimalPlaces); + explicit String(double value) : + String(value, 2) + {} + ~String() { invalidate(); } @@ -82,7 +119,7 @@ class String { // return true on success, false on failure (in which case, the string // is left unchanged). reserve(0), if successful, will validate an // invalid string (i.e., "if (s)" will be true afterwards) - unsigned char reserve(unsigned int size); + bool reserve(unsigned int size); unsigned int length(void) const { return buffer() ? len() : 0; } @@ -93,134 +130,124 @@ class String { return length() == 0; } - // creates a copy of the assigned value. if the value is null or - // invalid, or if the memory allocation fails, the string will be - // marked as invalid ("if (s)" will be false). + // assign string types as well as built-in numeric types String &operator =(const String &rhs); + String &operator =(String &&rval) noexcept; String &operator =(const char *cstr); String &operator =(const __FlashStringHelper *str); - String &operator =(String &&rval) noexcept; - String &operator =(StringSumHelper &&rval) noexcept { - return operator =((String &&)rval); - } - - // concatenate (works w/ built-in types) - - // returns true on success, false on failure (in which case, the string - // is left unchanged). if the argument is null or invalid, the - // concatenation is considered unsuccessful. - unsigned char concat(const String &str); - unsigned char concat(const char *cstr); - unsigned char concat(char c); - unsigned char concat(unsigned char c); - unsigned char concat(int num); - unsigned char concat(unsigned int num); - unsigned char concat(long num); - unsigned char concat(unsigned long num); - unsigned char concat(float num); - unsigned char concat(double num); - unsigned char concat(const __FlashStringHelper *str); - unsigned char concat(const char *cstr, unsigned int length); + String &operator =(char c); - // if there's not enough memory for the concatenated value, the string - // will be left unchanged (but this isn't signalled in any way) - String &operator +=(const String &rhs) { - concat(rhs); + String &operator =(unsigned char value) { + *this = String(value); return *this; } - String &operator +=(const char *cstr) { - concat(cstr); - return *this; - } - String &operator +=(char c) { - concat(c); + + String &operator =(int value) { + *this = String(value); return *this; } - String &operator +=(unsigned char num) { - concat(num); + + String &operator =(unsigned int value) { + *this = String(value); return *this; } - String &operator +=(int num) { - concat(num); + + String &operator =(long value) { + *this = String(value); return *this; } - String &operator +=(unsigned int num) { - concat(num); + + String &operator =(unsigned long value) { + *this = String(value); return *this; } - String &operator +=(long num) { - concat(num); + + String &operator =(long long value) { + *this = String(value); return *this; } - String &operator +=(unsigned long num) { - concat(num); + + String &operator =(unsigned long long value) { + *this = String(value); return *this; } - String &operator +=(float num) { - concat(num); + + String &operator =(float value) { + *this = String(value); return *this; } - String &operator +=(double num) { - concat(num); + + String &operator =(double value) { + *this = String(value); return *this; } - String &operator +=(const __FlashStringHelper *str) { - concat(str); + + // concatenate (works w/ built-in types, same as assignment) + + // returns true on success, false on failure (in which case, the string + // is left unchanged). if the argument is null or invalid, the + // concatenation is considered unsuccessful. + bool concat(const String &str); + bool concat(const char *cstr); + bool concat(const char *cstr, unsigned int length); + bool concat(const __FlashStringHelper *str); + bool concat(char c); + + bool concat(unsigned char c); + bool concat(int num); + bool concat(unsigned int num); + bool concat(long num); + bool concat(unsigned long num); + bool concat(long long num); + bool concat(unsigned long long num); + bool concat(float num); + bool concat(double num); + + // if there's not enough memory for the concatenated value, the string + // will be left unchanged (but this isn't signaled in any way) + template + String &operator +=(const T &rhs) { + concat(rhs); return *this; } - friend StringSumHelper &operator +(const StringSumHelper &lhs, const String &rhs); - friend StringSumHelper &operator +(const StringSumHelper &lhs, const char *cstr); - friend StringSumHelper &operator +(const StringSumHelper &lhs, char c); - friend StringSumHelper &operator +(const StringSumHelper &lhs, unsigned char num); - friend StringSumHelper &operator +(const StringSumHelper &lhs, int num); - friend StringSumHelper &operator +(const StringSumHelper &lhs, unsigned int num); - friend StringSumHelper &operator +(const StringSumHelper &lhs, long num); - friend StringSumHelper &operator +(const StringSumHelper &lhs, unsigned long num); - friend StringSumHelper &operator +(const StringSumHelper &lhs, float num); - friend StringSumHelper &operator +(const StringSumHelper &lhs, double num); - friend StringSumHelper &operator +(const StringSumHelper &lhs, const __FlashStringHelper *rhs); - - // comparison (only works w/ Strings and "strings") - operator StringIfHelperType() const { - return buffer() ? &String::StringIfHelper : 0; + // checks whether the internal buffer pointer is set. + // (should not be the case for us, since we always reset the pointer to the SSO buffer instead of setting it to nullptr) + explicit operator bool() const { + return buffer() != nullptr; } + int compareTo(const String &s) const; - unsigned char equals(const String &s) const; - unsigned char equals(const char *cstr) const; - unsigned char operator ==(const String &rhs) const { + bool equals(const String &s) const; + bool equals(const char *cstr) const; + bool equals(const __FlashStringHelper *s) const; + bool operator ==(const String &rhs) const { return equals(rhs); } - unsigned char operator ==(const char *cstr) const { + bool operator ==(const char *cstr) const { return equals(cstr); } - unsigned char operator !=(const String &rhs) const { + bool operator !=(const String &rhs) const { return !equals(rhs); } - unsigned char operator !=(const char *cstr) const { + bool operator !=(const char *cstr) const { return !equals(cstr); } - unsigned char operator <(const String &rhs) const; - unsigned char operator >(const String &rhs) const; - unsigned char operator <=(const String &rhs) const; - unsigned char operator >=(const String &rhs) const; - unsigned char equalsIgnoreCase(const String &s) const; + bool operator <(const String &rhs) const; + bool operator >(const String &rhs) const; + bool operator <=(const String &rhs) const; + bool operator >=(const String &rhs) const; + bool equalsIgnoreCase(const String &s) const; + bool equalsIgnoreCase(const __FlashStringHelper *s) const; unsigned char equalsConstantTime(const String &s) const; - unsigned char startsWith(const String &prefix) const; - unsigned char startsWith(const char *prefix) const { - return this->startsWith(String(prefix)); - } - unsigned char startsWith(const __FlashStringHelper *prefix) const { - return this->startsWith(String(prefix)); - } - unsigned char startsWith(const String &prefix, unsigned int offset) const; - unsigned char endsWith(const String &suffix) const; - unsigned char endsWith(const char *suffix) const { - return this->endsWith(String(suffix)); - } - unsigned char endsWith(const __FlashStringHelper *suffix) const { - return this->endsWith(String(suffix)); - } + bool startsWith(const String &prefix) const; + bool startsWith(const char *prefix) const; + bool startsWith(const __FlashStringHelper *prefix) const; + bool startsWith(const String &prefix, unsigned int offset) const; + bool startsWith(const __FlashStringHelper *prefix, unsigned int offset) const; + bool endsWith(const String &suffix) const; + bool endsWith(const char *suffix) const; + bool endsWith(const __FlashStringHelper *suffix) const; // character access char charAt(unsigned int index) const { @@ -250,6 +277,8 @@ class String { int lastIndexOf(char ch, unsigned int fromIndex) const; int lastIndexOf(const String &str) const; int lastIndexOf(const String &str, unsigned int fromIndex) const; + int lastIndexOf(const __FlashStringHelper *str) const; + int lastIndexOf(const __FlashStringHelper *str, unsigned int fromIndex) const; String substring(unsigned int beginIndex) const { return substring(beginIndex, len()); } @@ -258,21 +287,12 @@ class String { // modification void replace(char find, char replace); void replace(const String &find, const String &replace); - void replace(const char *find, const String &replace) { - this->replace(String(find), replace); - } - void replace(const __FlashStringHelper *find, const String &replace) { - this->replace(String(find), replace); - } - void replace(const char *find, const char *replace) { - this->replace(String(find), String(replace)); - } - void replace(const __FlashStringHelper *find, const char *replace) { - this->replace(String(find), String(replace)); - } - void replace(const __FlashStringHelper *find, const __FlashStringHelper *replace) { - this->replace(String(find), String(replace)); - } + void replace(const char *find, const String &replace); + void replace(const __FlashStringHelper *find, const String &replace); + void replace(const char *find, const char *replace); + void replace(const __FlashStringHelper *find, const char *replace); + void replace(const __FlashStringHelper *find, const __FlashStringHelper *replace); + // Pass the biggest integer if the count is not specified. // The remove method below will take care of truncating it at the end of the string. void remove(unsigned int index, unsigned int count = (unsigned int)-1); @@ -319,10 +339,20 @@ class String { void setCapacity(int cap) { if (!isSSO()) ptr.cap = cap; } void setBuffer(char *buff) { if (!isSSO()) ptr.buff = buff; } // Buffer accessor functions - const char *buffer() const { return wbuffer(); } - char *wbuffer() const { return isSSO() ? const_cast(sso.buff) : ptr.buff; } // Writable version of buffer + const char *buffer() const { return isSSO() ? sso.buff : ptr.buff; } + 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 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); + friend String operator +(const char *lhs, String &&rhs); + friend String operator +(const __FlashStringHelper *lhs, String &&rhs); protected: + // TODO: replace init() with a union constructor, so it's called implicitly + void init(void) __attribute__((always_inline)) { sso.buff[0] = 0; sso.len = 0; @@ -340,51 +370,86 @@ class String { // Unfortunately, GCC seems not to re-evaluate the cost of inlining after the store-merging optimizer stage, // `always_inline` attribute is necessary in order to keep inlining. } + + // resets the string storage to the initial state void invalidate(void); - unsigned char changeBuffer(unsigned int maxStrLen); + bool changeBuffer(unsigned int maxStrLen); - // copy and move + // copy or insert at a specific position String ©(const char *cstr, unsigned int length); String ©(const __FlashStringHelper *pstr, unsigned int length); + + String &insert(size_t position, char); + String &insert(size_t position, const char *); + String &insert(size_t position, const __FlashStringHelper *); + String &insert(size_t position, const char *, size_t length); + String &insert(size_t position, const String &); + + // rvalue helper void move(String &rhs) noexcept; }; -class StringSumHelper: public String { - public: - StringSumHelper(const String &s) : - String(s) { - } - StringSumHelper(const char *p) : - String(p) { - } - StringSumHelper(char c) : - String(c) { - } - StringSumHelper(unsigned char num) : - String(num) { - } - StringSumHelper(int num) : - String(num) { - } - StringSumHelper(unsigned int num) : - String(num) { - } - StringSumHelper(long num) : - String(num) { - } - StringSumHelper(unsigned long num) : - String(num) { - } - StringSumHelper(float num) : - String(num) { - } - StringSumHelper(double num) : - String(num) { - } - StringSumHelper(const __FlashStringHelper *s) : - String(s) { - } -}; +// concatenation (note that it's done using non-method operators to handle both possible type refs) + +inline String operator +(const String &lhs, const String &rhs) { + String res; + res.reserve(lhs.length() + rhs.length()); + res += lhs; + res += rhs; + return res; +} + +inline String operator +(String &&lhs, const String &rhs) { + lhs += rhs; + return std::move(lhs); +} + +String operator +(const String &lhs, String &&rhs); +String operator +(String &&lhs, String &&rhs); + +template >>> +inline String operator +(const String &lhs, const T &value) { + String res(lhs); + res += value; + return res; +} + +template >>> +inline String operator +(String &&lhs, const T &value) { + lhs += value; + return std::move(lhs); +} + +// `String(char)` is explicit, but we used to have StringSumHelper silently allowing the following: +// `String x; x = 'a' + String('b') + 'c';` +// For comparison, `std::string(char)` does not exist. However, we are allowed to chain `char` as both lhs and rhs + +String operator +(char lhs, const String &rhs); + +inline String operator +(char lhs, String &&rhs) { + return std::move(rhs.insert(0, lhs)); +} + +// both `char*` and `__FlashStringHelper*` are implicitly converted into `String()`, calling the `operator+(const String& ...);` +// however, here we: +// - do an automatic `reserve(total length)` for the resulting string +// - possibly do rhs.insert(0, ...), when &&rhs capacity could fit both + +String operator +(const char *lhs, const String &rhs); + +inline String operator +(const char *lhs, String &&rhs) { + return std::move(rhs.insert(0, lhs)); +} + +inline String operator +(const __FlashStringHelper *lhs, const String &rhs) { + return reinterpret_cast(lhs) + rhs; +} + +inline String operator +(const __FlashStringHelper *lhs, String &&rhs) { + return std::move(rhs.insert(0, lhs)); +} extern const String emptyString; diff --git a/cores/esp8266/aes_unwrap.cpp b/cores/esp8266/aes_unwrap.cpp new file mode 100644 index 0000000000..b3bf2066bc --- /dev/null +++ b/cores/esp8266/aes_unwrap.cpp @@ -0,0 +1,163 @@ +/* + * Replacement for the ROM aes_unwrap() function. It uses the heap instead of + * the static DRAM address at 0x3FFFEA80, which may step on the SYS stack in + * special circumstances such as HWDT Stack Dump. + * + * When not using WPS, the address space 0x3FFFE000 up to 0x40000000 is mostly + * available for the stacks. The one known exception is the ROM AES APIs. When + * `aes_decrypt_init` is called, it uses memory at 0x3FFFEA80 up to 0x3FFFEB30 + * for a buffer. At the finish, `aes_decrypt_deinit` zeros out the buffer. + * + * The NONOS SDK appears to have replacements for most of the ROM's AES APIs. + * However, the SDK still calls on the ROM's aes_unwrap function, which uses + * the ROM's AES APIs to operate. These calls can overwrite some of the stack + * space. To resolve the problem, this module replaces `aes_unwrap`. + * + * Final note, so far, I have not seen a problem when using the extra 4K heap + * option without the "debug HWDT". It is when combined with the HWDT Stack + * Dump that a problem shows. This combination adds a Boot ROM stack, which + * pushes up the SYS and CONT stacks into the AES Buffer space. Then the + * problem shows. + * + * While debugging with painted stack space, during WiFi Connect, Reconnect, + * and about every hour, a block of memory 0x3FFFEA80 - 0x3FFFEB30 (176 bytes) + * was zeroed by the Boot ROM function aes_decrypt_init. All other painted + * memory in the area was untouched after starting WiFi. + */ + +#if defined(KEEP_ROM_AES_UNWRAP) +// Using the ROM version of aes_unwrap should be fine for the no extra 4K case +// which is usually used in conjunction with WPS. + +#else +// This is required for DEBUG_ESP_HWDT. +// The need is unconfirmed for the extra 4K heap case. +#include "umm_malloc/umm_malloc.h" + +extern "C" { + +// Uses this function from the Boot ROM +void rijndaelKeySetupDec(u32 rk[], const u8 cipherKey[]); + +// This replaces the Boot ROM version just for this module +// Uses a malloc-ed buffer instead of the static buffer in stack address space. +static void *aes_decrypt_init(const u8 *key, size_t len) { + if (16u != len) { + return 0; + } + u32 *rk = (u32 *)malloc(16*11); + // u32 *rk = (u32 *)0x3FFFEA80u; // This is what the ROM would have used. + if (rk) { + rijndaelKeySetupDec(rk, key); + } + return (void *)rk; +} + +// This replaces the Boot ROM version just for this module +static void aes_decrypt_deinit(void *ctx) { + if (ctx) { + ets_memset(ctx, 0, 16*11); + free(ctx); + } + return; +} + +/* + * The NONOS SDK has an override on this function. To replace the aes_unwrap + * without changing its behavior too much. We need access to the ROM version of + * the AES APIs to make our aes_unwrap functionally equal to the current + * environment except for the AES Buffer. + */ +#ifndef ROM_aes_decrypt +#define ROM_aes_decrypt 0x400092d4 +#endif + +typedef void (*fp_aes_decrypt_t)(void *ctx, const u8 *crypt, u8 *plain); +#define AES_DECRYPT (reinterpret_cast(ROM_aes_decrypt)) + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/* + * This aes_unwrap() function overrides/replaces the Boot ROM version. + * + * It was adapted from aes_unwrap() found in the ESP8266 RTOS SDK + * .../components/wap_supplicant/src/crypto/aes-unwrap.c + * + */ +/////////////////////////////////////////////////////////////////////////////// +/* + * AES key unwrap (128-bit KEK, RFC3394) + * + * Copyright (c) 2003-2007, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +/** based on RTOS SDK + * aes_unwrap - Unwrap key with AES Key Wrap Algorithm (128-bit KEK) (RFC3394) + * @kek: Key encryption key (KEK) + * @n: Length of the plaintext key in 64-bit units; e.g., 2 = 128-bit = 16 + * bytes + * @cipher: Wrapped key to be unwrapped, (n + 1) * 64 bits + * @plain: Plaintext key, n * 64 bits + * Returns: 0 on success, -1 on failure (e.g., integrity verification failed) + */ +int aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain) +{ + u8 a[8], *r, b[16]; + int i, j; + void *ctx; + + /* 1) Initialize variables. */ + ets_memcpy(a, cipher, 8); + r = plain; + ets_memcpy(r, cipher + 8, 8 * n); + + ctx = aes_decrypt_init(kek, 16); + if (ctx == NULL) + return -1; + + /* 2) Compute intermediate values. + * For j = 5 to 0 + * For i = n to 1 + * B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i + * A = MSB(64, B) + * R[i] = LSB(64, B) + */ + for (j = 5; j >= 0; j--) { + r = plain + (n - 1) * 8; + for (i = n; i >= 1; i--) { + ets_memcpy(b, a, 8); + b[7] ^= n * j + i; + + ets_memcpy(b + 8, r, 8); + AES_DECRYPT(ctx, b, b); + ets_memcpy(a, b, 8); + ets_memcpy(r, b + 8, 8); + r -= 8; + } + } + aes_decrypt_deinit(ctx); + + /* 3) Output results. + * + * These are already in @plain due to the location of temporary + * variables. Just verify that the IV matches with the expected value. + */ + for (i = 0; i < 8; i++) { + if (a[i] != 0xa6) + return -1; + } + + return 0; +} +}; +#endif diff --git a/cores/esp8266/base64.h b/cores/esp8266/base64.h index 49a6cdfb01..d6857d1d23 100644 --- a/cores/esp8266/base64.h +++ b/cores/esp8266/base64.h @@ -33,11 +33,23 @@ class base64 // NOTE: The default behaviour of backend (lib64) // is to add a newline every 72 (encoded) characters output. // This may 'break' longer uris and json variables - static String encode(const uint8_t * data, size_t length, bool doNewLines = true); - static String inline encode(const String& text, bool doNewLines = true) + static String encode(const uint8_t * data, size_t length, bool doNewLines); + static inline String encode(const String& text, bool doNewLines) { return encode( (const uint8_t *) text.c_str(), text.length(), doNewLines ); } + + // esp32 compat: + + static inline String encode(const uint8_t * data, size_t length) + { + return encode(data, length, false); + } + + static inline String encode(const String& text) + { + return encode(text, false); + } private: }; diff --git a/cores/esp8266/cbuf.cpp b/cores/esp8266/cbuf.cpp index b9a2880e30..3a9e33a8a2 100644 --- a/cores/esp8266/cbuf.cpp +++ b/cores/esp8266/cbuf.cpp @@ -67,7 +67,7 @@ size_t cbuf::resize(size_t newSize) { return _size; } -size_t ICACHE_RAM_ATTR cbuf::available() const { +size_t IRAM_ATTR cbuf::available() const { if(_end >= _begin) { return _end - _begin; } @@ -108,7 +108,7 @@ size_t cbuf::peek(char *dst, size_t size) { return size_read; } -int ICACHE_RAM_ATTR cbuf::read() { +int IRAM_ATTR cbuf::read() { if(empty()) return -1; @@ -133,7 +133,7 @@ size_t cbuf::read(char* dst, size_t size) { return size_read; } -size_t ICACHE_RAM_ATTR cbuf::write(char c) { +size_t IRAM_ATTR cbuf::write(char c) { if(full()) return 0; diff --git a/cores/esp8266/cont.S b/cores/esp8266/cont.S index 00ca017785..ee049d46f5 100644 --- a/cores/esp8266/cont.S +++ b/cores/esp8266/cont.S @@ -21,13 +21,19 @@ .section .irom0.text .align 4 .literal_position - .global cont_yield - .type cont_yield, @function -cont_yield: + .global cont_suspend + .type cont_suspend, @function +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 @@ -35,10 +41,10 @@ cont_yield: s32i a0, a1, 16 s32i a2, a1, 20 - /* &cont_continue -> cont_ctx.pc_yield */ + /* &cont_continue -> cont_ctx.pc_suspend */ movi a3, cont_continue s32i a3, a2, 8 - /* sp -> cont_ctx.sp_yield */ + /* sp -> cont_ctx.sp_suspend */ s32i a1, a2, 12 /* a0 <- cont_ctx.pc_ret */ @@ -47,6 +53,11 @@ cont_yield: 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 @@ -56,7 +67,7 @@ cont_continue: l32i a2, a1, 20 addi a1, a1, 24 ret - .size cont_yield, . - cont_yield + .size cont_suspend, . - cont_suspend //////////////////////////////////////////////////// @@ -108,25 +119,28 @@ cont_run: /* sp -> cont_ctx.sp_ret */ s32i a1, a2, 4 - /* if cont_ctx.pc_yield != 0, goto cont_resume */ + /* if cont_ctx.pc_suspend != 0, goto cont_resume */ l32i a4, a2, 8 bnez a4, cont_resume /* else */ /* set new stack*/ - l32i a1, a2, 16; + l32i a1, a2, 16 /* goto pfn */ movi a2, cont_wrapper jx a2 cont_resume: - /* a1 <- cont_ctx.sp_yield */ + /* a1 <- cont_ctx.sp_suspend */ l32i a1, a2, 12 - /* reset yield flag, 0 -> cont_ctx.pc_yield */ + /* 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_yield */ + /* 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 21ecad2806..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 @@ -35,8 +40,8 @@ typedef struct cont_ { void (*pc_ret)(void); unsigned* sp_ret; - void (*pc_yield)(void); - unsigned* sp_yield; + void (*pc_suspend)(void); + unsigned* sp_suspend; unsigned* stack_end; unsigned unused1; @@ -55,31 +60,32 @@ extern cont_t* g_pcont; void cont_init(cont_t*); // Run function pfn in a separate stack, or continue execution -// at the point where cont_yield was called +// at the point where cont_suspend was called void cont_run(cont_t*, void (*pfn)(void)); // Return to the point where cont_run was called, saving the // execution state (registers and stack) -void cont_yield(cont_t*); +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) int cont_get_free_stack(cont_t* cont); -// Check if yield() may be called. Returns true if we are running inside +// Check if cont_suspend() may be called. Returns true if we are running inside // continuation stack -bool cont_can_yield(cont_t* cont); +bool cont_can_suspend(cont_t* cont); // Repaint the stack from the current SP to the end, to allow individual // routines' stack usages to be calculated by re-painting, checking current // 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 d21a064e35..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 ICACHE_RAM_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 @@ -62,9 +78,9 @@ int cont_get_free_stack(cont_t* cont) { return freeWords * 4; } -bool ICACHE_RAM_ATTR cont_can_yield(cont_t* cont) { +bool IRAM_ATTR cont_can_suspend(cont_t* cont) { return !ETS_INTR_WITHINISR() && - cont->pc_ret != 0 && cont->pc_yield == 0; + cont->pc_ret != 0 && cont->pc_suspend == 0; } // No need for this to be in IRAM, not expected to be IRQ called diff --git a/cores/esp8266/core_esp8266_app_entry_noextra4k.cpp b/cores/esp8266/core_esp8266_app_entry_noextra4k.cpp index 56e4b1d598..0c0eac4d48 100644 --- a/cores/esp8266/core_esp8266_app_entry_noextra4k.cpp +++ b/cores/esp8266/core_esp8266_app_entry_noextra4k.cpp @@ -9,6 +9,7 @@ #include #include "cont.h" #include "coredecls.h" +#include void disable_extra4k_at_link_time (void) { @@ -27,10 +28,31 @@ extern "C" void call_user_start(); /* this is the default NONOS-SDK user's heap location */ static cont_t g_cont __attribute__ ((aligned (16))); +#if defined(DEBUG_ESP_HWDT_NOEXTRA4K) || defined(DEBUG_ESP_HWDT) +extern "C" cont_t * IRAM_ATTR get_noextra4k_g_pcont(void) +{ + return &g_cont; +} + +#else extern "C" void app_entry_redefinable(void) { g_pcont = &g_cont; + #ifdef UMM_INIT_USE_IRAM + /* + * Legacy option: the umm_init() call path must reside in IRAM. + */ + umm_init(); + #else + /* + * Instruction cache is enabled/disabled around running umm_init(). + * Allows the use of IROM (flash) to store umm_init(). + */ + mmu_wrap_irom_fn(umm_init); + #endif + /* Call the entry point of the SDK code. */ call_user_start(); } +#endif 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 5dea897bc0..9c574f20c7 100644 --- a/cores/esp8266/core_esp8266_features.h +++ b/cores/esp8266/core_esp8266_features.h @@ -20,21 +20,21 @@ */ - #ifndef CORE_ESP8266_FEATURES_H #define CORE_ESP8266_FEATURES_H - #define CORE_HAS_LIBB64 #define CORE_HAS_BASE64_CLASS #define CORE_HAS_CXA_GUARD #define CORE_HAS_UMM #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 @@ -117,11 +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_i2s.cpp b/cores/esp8266/core_esp8266_i2s.cpp index dccd7c1df8..05a70f18d6 100644 --- a/cores/esp8266/core_esp8266_i2s.cpp +++ b/cores/esp8266/core_esp8266_i2s.cpp @@ -24,7 +24,7 @@ #include "osapi.h" #include "ets_sys.h" #include "i2s_reg.h" -#include "i2s.h" +#include "core_esp8266_i2s.h" extern "C" { @@ -61,7 +61,7 @@ typedef struct i2s_state { uint32_t * curr_slc_buf; // Current buffer for writing uint32_t curr_slc_buf_pos; // Position in the current buffer void (*callback) (void); - // Callback function should be defined as 'void ICACHE_RAM_ATTR function_name()', + // Callback function should be defined as 'void IRAM_ATTR function_name()', // and be placed in IRAM for faster execution. Avoid long computational tasks in this // function, use it to set flags and process later. bool driveClocks; @@ -73,6 +73,7 @@ static i2s_state_t *tx = NULL; // Last I2S sample rate requested static uint32_t _i2s_sample_rate; +static int _i2s_bits = 16; // IOs used for I2S. Not defined in i2s.h, unfortunately. // Note these are internal GPIO numbers and not pins on an @@ -84,6 +85,14 @@ static uint32_t _i2s_sample_rate; #define I2SI_BCK 13 #define I2SI_WS 14 +bool i2s_set_bits(int bits) { + if (tx || rx || (bits != 16 && bits != 24)) { + return false; + } + _i2s_bits = bits; + return true; +} + static bool _i2s_is_full(const i2s_state_t *ch) { if (!ch) { return false; @@ -130,7 +139,7 @@ uint16_t i2s_rx_available(){ } // Pop the top off of the queue and return it -static uint32_t * ICACHE_RAM_ATTR i2s_slc_queue_next_item(i2s_state_t *ch) { +static uint32_t * IRAM_ATTR i2s_slc_queue_next_item(i2s_state_t *ch) { uint8_t i; uint32_t *item = ch->slc_queue[0]; ch->slc_queue_len--; @@ -141,7 +150,7 @@ static uint32_t * ICACHE_RAM_ATTR i2s_slc_queue_next_item(i2s_state_t *ch) { } // Append an item to the end of the queue from receive -static void ICACHE_RAM_ATTR i2s_slc_queue_append_item(i2s_state_t *ch, uint32_t *item) { +static void IRAM_ATTR i2s_slc_queue_append_item(i2s_state_t *ch, uint32_t *item) { // Shift everything up, except for the one corresponding to this item for (int i=0, dest=0; i < ch->slc_queue_len; i++) { if (ch->slc_queue[i] != item) { @@ -155,7 +164,7 @@ static void ICACHE_RAM_ATTR i2s_slc_queue_append_item(i2s_state_t *ch, uint32_t } } -static void ICACHE_RAM_ATTR i2s_slc_isr(void) { +static void IRAM_ATTR i2s_slc_isr(void) { ETS_SLC_INTR_DISABLE(); uint32_t slc_intr_status = SLCIS; SLCIC = 0xFFFFFFFF; @@ -185,11 +194,11 @@ static void ICACHE_RAM_ATTR i2s_slc_isr(void) { } void i2s_set_callback(void (*callback) (void)) { - tx->callback = callback; + if (tx) tx->callback = callback; } void i2s_rx_set_callback(void (*callback) (void)) { - rx->callback = callback; + if (rx) rx->callback = callback; } static bool _alloc_channel(i2s_state_t *ch) { @@ -334,7 +343,7 @@ bool i2s_write_lr(int16_t left, int16_t right){ // writes a buffer of frames into the DMA memory, returns the amount of frames written // A frame is just a int16_t for mono, for stereo a frame is two int16_t, one for each channel. -static uint16_t _i2s_write_buffer(int16_t *frames, uint16_t frame_count, bool mono, bool nb) { +static uint16_t _i2s_write_buffer(const int16_t *frames, uint16_t frame_count, bool mono, bool nb) { uint16_t frames_written=0; while(frame_count>0) { @@ -392,13 +401,13 @@ static uint16_t _i2s_write_buffer(int16_t *frames, uint16_t frame_count, bool mo return frames_written; } -uint16_t i2s_write_buffer_mono_nb(int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, true, true); } +uint16_t i2s_write_buffer_mono_nb(const int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, true, true); } -uint16_t i2s_write_buffer_mono(int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, true, false); } +uint16_t i2s_write_buffer_mono(const int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, true, false); } -uint16_t i2s_write_buffer_nb(int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, false, true); } +uint16_t i2s_write_buffer_nb(const int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, false, true); } -uint16_t i2s_write_buffer(int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, false, false); } +uint16_t i2s_write_buffer(const int16_t *frames, uint16_t frame_count) { return _i2s_write_buffer(frames, frame_count, false, false); } bool i2s_read_sample(int16_t *left, int16_t *right, bool blocking) { if (!rx) { @@ -441,7 +450,7 @@ void i2s_set_rate(uint32_t rate) { //Rate in HZ } _i2s_sample_rate = rate; - uint32_t scaled_base_freq = I2SBASEFREQ/32; + uint32_t scaled_base_freq = I2SBASEFREQ / (_i2s_bits * 2); float delta_best = scaled_base_freq; uint8_t sbd_div_best=1; @@ -474,7 +483,7 @@ void i2s_set_dividers(uint8_t div1, uint8_t div2) { i2sc_temp |= (I2STXR); // Hold transmitter in reset I2SC = i2sc_temp; - // trans master(active low), recv master(active_low), !bits mod(==16 bits/chanel), clear clock dividers + // trans master(active low), recv master(active_low), !bits mod(==16 bits/channel), clear clock dividers i2sc_temp &= ~(I2STSM | I2SRSM | (I2SBMM << I2SBM) | (I2SBDM << I2SBD) | (I2SCDM << I2SCD)); // I2SRF = Send/recv right channel first (? may be swapped form I2S spec of WS=0 => left) @@ -483,6 +492,9 @@ void i2s_set_dividers(uint8_t div1, uint8_t div2) { // div1, div2 = Set I2S WS clock frequency. BCLK seems to be generated from 32x this i2sc_temp |= I2SRF | I2SMR | I2SRMS | I2STMS | (div1 << I2SBD) | (div2 << I2SCD); + // Adjust the shift count for 16/24b output + i2sc_temp |= (_i2s_bits == 24 ? 8 : 0) << I2SBM; + I2SC = i2sc_temp; i2sc_temp &= ~(I2STXR); // Release reset @@ -490,7 +502,7 @@ void i2s_set_dividers(uint8_t div1, uint8_t div2) { } float i2s_get_real_rate(){ - return (float)I2SBASEFREQ/32/((I2SC>>I2SBD) & I2SBDM)/((I2SC >> I2SCD) & I2SCDM); + return (float)I2SBASEFREQ/(_i2s_bits * 2)/((I2SC>>I2SBD) & I2SBDM)/((I2SC >> I2SCD) & I2SCDM); } bool i2s_rxtx_begin(bool enableRx, bool enableTx) { @@ -549,6 +561,9 @@ bool i2s_rxtxdrive_begin(bool enableRx, bool enableTx, bool driveRxClocks, bool // I2STXFMM, I2SRXFMM=0 => 16-bit, dual channel data shifted in/out I2SFC &= ~(I2SDE | (I2STXFMM << I2STXFM) | (I2SRXFMM << I2SRXFM)); // Set RX/TX FIFO_MOD=0 and disable DMA (FIFO only) + if (_i2s_bits == 24) { + I2SFC |= (2 << I2STXFM) | (2 << I2SRXFM); + } I2SFC |= I2SDE; // Enable DMA // I2STXCMM, I2SRXCMM=0 => Dual channel mode diff --git a/cores/esp8266/i2s.h b/cores/esp8266/core_esp8266_i2s.h similarity index 83% rename from cores/esp8266/i2s.h rename to cores/esp8266/core_esp8266_i2s.h index f2978286af..07a18ef554 100644 --- a/cores/esp8266/i2s.h +++ b/cores/esp8266/core_esp8266_i2s.h @@ -18,8 +18,8 @@ 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 I2S_h -#define I2S_h +#ifndef CORE_ESP8266_I2S_H +#define CORE_ESP8266_I2S_H #define I2S_HAS_BEGIN_RXTX_DRIVE_CLOCKS 1 @@ -27,6 +27,7 @@ How does this work? Basically, to get sound, you need to: - Connect an I2S codec to the I2S pins on the ESP. - Start up a thread that's going to do the sound output +- Call i2s_set_bits() if you want to enable 24-bit mode - Call i2s_begin() - Call i2s_set_rate() with the sample rate you want. - Generate sound and call i2s_write_sample() with 32-bit samples. @@ -42,6 +43,10 @@ speed. extern "C" { #endif +bool i2s_set_bits(int bits); // Set bits per sample, only 16 or 24 supported. Call before begin. +// Note that in 24 bit mode each sample must be left-aligned (i.e. 0x00000000 .. 0xffffff00) as the +// hardware shifts starting at bit 31, not bit 23. + void i2s_begin(); // Enable TX only, for compatibility bool i2s_rxtx_begin(bool enableRx, bool enableTx); // Allow TX and/or RX, returns false on OOM error bool i2s_rxtxdrive_begin(bool enableRx, bool enableTx, bool driveRxClocks, bool driveTxClocks); @@ -64,10 +69,10 @@ void i2s_rx_set_callback(void (*callback) (void)); // writes a buffer of frames into the DMA memory, returns the amount of frames written // A frame is just a int16_t for mono, for stereo a frame is two int16_t, one for each channel. -uint16_t i2s_write_buffer_mono(int16_t *frames, uint16_t frame_count); -uint16_t i2s_write_buffer_mono_nb(int16_t *frames, uint16_t frame_count); -uint16_t i2s_write_buffer(int16_t *frames, uint16_t frame_count); -uint16_t i2s_write_buffer_nb(int16_t *frames, uint16_t frame_count); +uint16_t i2s_write_buffer_mono(const int16_t *frames, uint16_t frame_count); +uint16_t i2s_write_buffer_mono_nb(const int16_t *frames, uint16_t frame_count); +uint16_t i2s_write_buffer(const int16_t *frames, uint16_t frame_count); +uint16_t i2s_write_buffer_nb(const int16_t *frames, uint16_t frame_count); #ifdef __cplusplus } diff --git a/cores/esp8266/core_esp8266_main.cpp b/cores/esp8266/core_esp8266_main.cpp index 417c3f26e8..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" { @@ -35,9 +39,10 @@ extern "C" { #include #include "gdb_hooks.h" #include "flash_quirks.h" +#include "hwdt_app_entry.h" #include #include - +#include "core_esp8266_vm.h" #define LOOP_TASK_PRIORITY 1 #define LOOP_QUEUE_SIZE 1 @@ -61,14 +66,14 @@ cont_t* g_pcont __attribute__((section(".noinit"))); static os_event_t s_loop_queue[LOOP_QUEUE_SIZE]; /* Used to implement optimistic_yield */ -static uint32_t s_cycles_at_yield_start; +static uint32_t s_cycles_at_resume; /* For ets_intr_lock_nest / ets_intr_unlock_nest * Max nesting seen by SDK so far is 2. */ #define ETS_INTR_LOCK_NEST_MAX 7 static uint16_t ets_intr_lock_stack[ETS_INTR_LOCK_NEST_MAX]; -static byte ets_intr_lock_stack_ptr=0; +static uint8_t ets_intr_lock_stack_ptr=0; extern "C" { @@ -79,6 +84,10 @@ const char* core_release = #else NULL; #endif + +static os_timer_t delay_timer; +#define ONCE 0 +#define REPEAT 1 } // extern "C" void initVariant() __attribute__((weak)); @@ -105,32 +114,84 @@ extern "C" void __preloop_update_frequency() { extern "C" void preloop_update_frequency() __attribute__((weak, alias("__preloop_update_frequency"))); extern "C" bool can_yield() { - return cont_can_yield(g_pcont); + return cont_can_suspend(g_pcont); } -static inline void esp_yield_within_cont() __attribute__((always_inline)); -static void esp_yield_within_cont() { - cont_yield(g_pcont); - s_cycles_at_yield_start = ESP.getCycleCount(); +static inline void esp_suspend_within_cont() __attribute__((always_inline)); +static void esp_suspend_within_cont() { + cont_suspend(g_pcont); + s_cycles_at_resume = ESP.getCycleCount(); run_scheduled_recurrent_functions(); } -extern "C" void __esp_yield() { - if (can_yield()) { - esp_yield_within_cont(); +extern "C" void __esp_suspend() { + if (cont_can_suspend(g_pcont)) { + esp_suspend_within_cont(); } } -extern "C" void esp_yield() __attribute__ ((weak, alias("__esp_yield"))); +extern "C" void esp_suspend() __attribute__ ((weak, alias("__esp_suspend"))); extern "C" IRAM_ATTR void esp_schedule() { ets_post(LOOP_TASK_PRIORITY, 0, 0); } +// Replacement for delay(0). In CONT, same as yield(). Whereas yield() panics +// in SYS, esp_yield() is safe to call and only schedules CONT. Use yield() +// whereever only called from CONT, use esp_yield() if code is called from SYS +// or both CONT and SYS. +extern "C" void esp_yield() { + esp_schedule(); + esp_suspend(); +} + +void delay_end(void* arg) { + (void)arg; + esp_schedule(); +} + +extern "C" void __esp_delay(unsigned long ms) { + if (ms) { + os_timer_setfn(&delay_timer, (os_timer_func_t*)&delay_end, 0); + os_timer_arm(&delay_timer, ms, ONCE); + } + else { + esp_schedule(); + } + esp_suspend(); + if (ms) { + os_timer_disarm(&delay_timer); + } +} + +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; // expired + } + + // 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() { - if (can_yield()) { + if (cont_can_suspend(g_pcont)) { esp_schedule(); - esp_yield_within_cont(); + esp_suspend_within_cont(); } else { panic(); @@ -139,6 +200,9 @@ extern "C" void __yield() { extern "C" void yield(void) __attribute__ ((weak, alias("__yield"))); +// In CONT, actually performs yield() only once the given time interval +// has elapsed since the last time yield() occured. Whereas yield() panics +// in SYS, optimistic_yield() additionally is safe to call and does nothing. extern "C" void optimistic_yield(uint32_t interval_us) { const uint32_t intvl_cycles = interval_us * #if defined(F_CPU) @@ -146,7 +210,7 @@ extern "C" void optimistic_yield(uint32_t interval_us) { #else ESP.getCpuFreqMHz(); #endif - if ((ESP.getCycleCount() - s_cycles_at_yield_start) > intvl_cycles && + if ((ESP.getCycleCount() - s_cycles_at_resume) > intvl_cycles && can_yield()) { yield(); @@ -198,24 +262,24 @@ 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_yield_start = ESP.getCycleCount(); + 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" { +extern "C" { struct object { long placeholder[ 10 ]; }; void __register_frame_info (const void *begin, struct object *ob); extern char __eh_frame[]; @@ -252,7 +316,6 @@ static void __unhandled_exception_cpp() } #endif } - } void init_done() { @@ -289,7 +352,7 @@ void init_done() { know if other features are using this, or if this memory is going to be used in future SDK releases. - WPS beeing flawed by its poor security, or not beeing used by lots of + WPS being flawed by its poor security, or not being used by lots of users, it has been decided that we are still going to use that memory for user's stack and disable the use of WPS. @@ -317,6 +380,14 @@ extern "C" void app_entry_redefinable(void) cont_t s_cont __attribute__((aligned(16))); g_pcont = &s_cont; + /* Doing umm_init just once before starting the SDK, allowed us to remove + test and init calls at each malloc API entry point, saving IRAM. */ +#ifdef UMM_INIT_USE_IRAM + umm_init(); +#else + // umm_init() is in IROM + mmu_wrap_irom_fn(umm_init); +#endif /* Call the entry point of the SDK code. */ call_user_start(); } @@ -324,22 +395,272 @@ static void app_entry_custom (void) __attribute__((weakref("app_entry_redefinabl extern "C" void app_entry (void) { - umm_init(); return app_entry_custom(); } extern "C" void preinit (void) __attribute__((weak)); extern "C" void preinit (void) { - /* do nothing by default */ + /* does nothing, kept for backward compatibility */ } +extern "C" void __disableWiFiAtBootTime (void) __attribute__((weak)); +extern "C" void __disableWiFiAtBootTime (void) +{ + // Starting from arduino core v3: wifi is disabled at boot time + // WiFi.begin() or WiFi.softAP() will wake WiFi up + wifi_set_opmode_current(0/*WIFI_OFF*/); + wifi_fpm_set_sleep_type(MODEM_SLEEP_T); + wifi_fpm_open(); + wifi_fpm_do_sleep(0xFFFFFFF); +} + +#if FLASH_MAP_SUPPORT +#include "flash_hal.h" +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(); @@ -348,13 +669,23 @@ extern "C" void user_init(void) { cont_init(g_pcont); -#if defined(NON32XFER_HANDLER) || defined(MMU_IRAM_HEAP) +#if defined(DEBUG_ESP_HWDT) || defined(DEBUG_ESP_HWDT_NOEXTRA4K) + debug_hwdt_init(); +#endif + +#if defined(UMM_HEAP_EXTERNAL) + install_vm_exception_handler(); +#endif + +#if defined(NON32XFER_HANDLER) || (defined(MMU_IRAM_HEAP) && (NONOSDK < (0x30000))) install_non32xfer_exception_handler(); #endif + #if defined(MMU_IRAM_HEAP) umm_init_iram(); #endif preinit(); // Prior to C++ Dynamic Init (not related to above init() ). Meant to be user redefinable. + __disableWiFiAtBootTime(); // default weak function disables WiFi ets_task(loop_task, LOOP_TASK_PRIORITY, s_loop_queue, diff --git a/cores/esp8266/core_esp8266_non32xfer.cpp b/cores/esp8266/core_esp8266_non32xfer.cpp index eb4741e1ed..eaf2323c96 100644 --- a/cores/esp8266/core_esp8266_non32xfer.cpp +++ b/cores/esp8266/core_esp8266_non32xfer.cpp @@ -18,18 +18,22 @@ */ /* - * This exception handler, allows for byte or short accesses to iRAM or PROGMEM - * to succeed without causing a crash. It is still preferred to use the xxx_P - * macros whenever possible, since they are probably 30x faster than this - * exception handler method. + * This exception handler handles EXCCAUSE_LOAD_STORE_ERROR. It allows for a + * byte or short access to iRAM or PROGMEM to succeed without causing a crash. + * When reading, it is still preferred to use the xxx_P macros when possible + * since they are probably 30x faster than this exception handler method. * * 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 @@ -37,6 +41,16 @@ #include #include +// All of these optimization were tried and now work +// These results were from irammem.ino using GCC 10.2 +// DRAM reference uint16 9 AVG cycles/transfer +// #pragma GCC optimize("O0") // uint16, 289 AVG cycles/transfer, IRAM: +180 +// #pragma GCC optimize("O1") // uint16, 241 AVG cycles/transfer, IRAM: +16 +#pragma GCC optimize("O2") // uint16, 230 AVG cycles/transfer, IRAM: +4 +// #pragma GCC optimize("O3") // uint16, 230 AVG cycles/transfer, IRAM: +4 +// #pragma GCC optimize("Ofast") // uint16, 230 AVG cycles/transfer, IRAM: +4 +// #pragma GCC optimize("Os") // uint16, 233 AVG cycles/transfer, IRAM: 27556 +0 + extern "C" { #define LOAD_MASK 0x00f00fu @@ -48,34 +62,19 @@ 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 -static IRAM_ATTR void non32xfer_exception_handler(struct __exception_frame *ef, int cause) +#if defined(NON32XFER_HANDLER) || (defined(MMU_IRAM_HEAP) && (NONOSDK < (0x30000))) +static +IRAM_ATTR void non32xfer_exception_handler(struct __exception_frame *ef, [[maybe_unused]] int cause) { do { - /* - Had to split out some of the asm, compiler was reusing a register that it - needed later. A crash would come or go away with the slightest unrelated - changes elsewhere in the function. - - Register a15 was used for epc1, then clobbered for rsr. Maybe an - __asm("":::"memory") before starting the asm would help for these cases. - For this instance moved setting epc1 closer to where it was used. - Edit. "&" on output register would have resolved the problem. - Refactored to reduce and consolidate register usage. - */ - uint32_t insn; - __asm( - "movi %0, ~3\n\t" /* prepare a mask for the EPC */ - "and %0, %0, %1\n\t" /* apply mask for 32bit aligned base */ - "ssa8l %1\n\t" /* set up shift register for src op */ - "l32i %1, %0, 0\n\t" /* load part 1 */ - "l32i %0, %0, 4\n\t" /* load part 2 */ - "src %0, %0, %1\n\t" /* right shift to get faulting instruction */ - :"=&r"(insn) - :"r"(ef->epc) - : - ); + uint32_t insn, excvaddr; + + /* Extract instruction and faulting data address */ + __EXCEPTION_HANDLER_PREAMBLE(ef, excvaddr, insn); uint32_t what = insn & LOAD_MASK; uint32_t valmask = 0; @@ -102,10 +101,6 @@ static IRAM_ATTR void non32xfer_exception_handler(struct __exception_frame *ef, --regno; /* account for skipped a1 in exception_frame */ } - uint32_t excvaddr; - /* read out the faulting address */ - __asm("rsr %0, EXCVADDR;" :"=r"(excvaddr)::); - #ifdef DEBUG_ESP_MMU /* debug option to validate address so we don't hide memory access bugs in APP */ if (mmu_is_iram((void *)excvaddr) || (is_read && mmu_is_icache((void *)excvaddr))) { @@ -114,31 +109,34 @@ static IRAM_ATTR void non32xfer_exception_handler(struct __exception_frame *ef, continue; /* fail */ } #endif - - if (is_read) { - /* Load, shift and mask down to correct size */ - uint32_t val = (*(uint32_t *)(excvaddr & ~0x3)); - val >>= (excvaddr & 0x3) * 8; - val &= valmask; - - /* Sign-extend for L16SI, if applicable */ - if (what == L16SI_MATCH && (val & 0x8000)) { - val |= 0xffff0000; + { + uint32_t *pWord = (uint32_t *)(excvaddr & ~0x3); + uint32_t pos = (excvaddr & 0x3) * 8; + uint32_t mem_val = *pWord; + + if (is_read) { + /* shift and mask down to correct size */ + mem_val >>= pos; + mem_val &= valmask; + + /* Sign-extend for L16SI, if applicable */ + if (what == L16SI_MATCH && (mem_val & 0x8000)) { + mem_val |= 0xffff0000; + } + + ef->a_reg[regno] = mem_val; /* carry out the load */ + + } else { /* is write */ + uint32_t val = ef->a_reg[regno]; /* get value to store from register */ + val <<= pos; + valmask <<= pos; + val &= valmask; + + /* mask out field, and merge */ + mem_val &= (~valmask); + mem_val |= val; + *pWord = mem_val; /* carry out the store */ } - - ef->a_reg[regno] = val; /* carry out the load */ - - } else { /* is write */ - uint32_t val = ef->a_reg[regno]; /* get value to store from register */ - val <<= (excvaddr & 0x3) * 8; - valmask <<= (excvaddr & 0x3) * 8; - val &= valmask; - - /* Load, mask out field, and merge */ - uint32_t dst_val = (*(uint32_t *)(excvaddr & ~0x3)); - dst_val &= (~valmask); - dst_val |= val; - (*(uint32_t *)(excvaddr & ~0x3)) = dst_val; /* carry out the store */ } ef->epc += 3; /* resume at following instruction */ @@ -147,6 +145,7 @@ static IRAM_ATTR void non32xfer_exception_handler(struct __exception_frame *ef, } 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 @@ -157,61 +156,50 @@ static IRAM_ATTR void non32xfer_exception_handler(struct __exception_frame *ef, 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)) /* - An issue, the Boot ROM "C" wrapper for exception handlers, - _xtos_c_wrapper_handler, turns interrupts back on. To address this issue we - use our replacement in file `exc-c-wrapper-handler.S`. - - An overview, of an exception at entry: New interrupts are blocked by EXCM - being set. Once cleared, interrupts above the current INTLEVEL and exceptions - (w/o creating a DoubleException) can occur. - - Using our replacement for _xtos_c_wrapper_handler, INTLEVEL is raised to 15 - with EXCM cleared. - - The original Boot ROM `_xtos_c_wrapper_handler` would set INTLEVEL to 1 with - EXCM cleared, saved registers, then do a `rsil 0`, and called the registered - "C" Exception handler with interrupts fully enabled! Our replacement keeps - INTLEVEL at 15. This is needed to support the Arduino model of interrupts - disabled while an ISR runs. - - And we also need it for umm_malloc to work safely with an IRAM heap from an - ISR call. While malloc() will supply DRAM for all allocation from an ISR, - we want free() to safely operate from an ISR to avoid a leak potential. - - This replacement "C" Wrapper is only applied to this exception handler. + To operate reliably, this module requires the new + `_xtos_set_exception_handler` from `exc-sethandler.cpp` and + `_xtos_c_wrapper_handler` from `exc-c-wrapper-handler.S`. See comment block in + `exc-sethandler.cpp` for details on issues with interrupts being enabled by + "C" wrapper. */ - -#define ROM_xtos_c_wrapper_handler (reinterpret_cast<_xtos_handler>(0x40000598)) - -static void _set_exception_handler_wrapper(int cause) { - _xtos_handler old_wrapper = _xtos_exc_handler_table[cause]; - if (old_wrapper == ROM_xtos_c_wrapper_handler) { - _xtos_exc_handler_table[cause] = _xtos_c_wrapper_handler; - } -} - +void install_non32xfer_exception_handler(void) __attribute__((weak)); void install_non32xfer_exception_handler(void) { if (NULL == old_c_handler) { // Set the "C" exception handler the wrapper will call old_c_handler = _xtos_set_exception_handler(EXCCAUSE_LOAD_STORE_ERROR, non32xfer_exception_handler); - - // Set the replacement ASM based exception "C" wrapper function which will - // be calling `non32xfer_exception_handler`. - _set_exception_handler_wrapper(EXCCAUSE_LOAD_STORE_ERROR); } } +#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_non32xfer.h b/cores/esp8266/core_esp8266_non32xfer.h index 93151239c4..83f8a06cf7 100644 --- a/cores/esp8266/core_esp8266_non32xfer.h +++ b/cores/esp8266/core_esp8266_non32xfer.h @@ -7,6 +7,54 @@ extern "C" { extern void install_non32xfer_exception_handler(); + + /* + In adapting the public domain version, a crash would come or go away with + the slightest unrelated changes elsewhere in the function. Observed that + register a15 was used for epc1, then clobbered by `rsr.` I now believe a + "&" on the output register would have resolved the problem. + + However, I have refactored the Extended ASM to reduce and consolidate + register usage and corrected the issue. + + The positioning of the Extended ASM block (as early as possible in the + compiled function) is in part controlled by the immediate need for + output variable `insn`. This placement aids in getting excvaddr read as + early as possible. + */ + +#if 0 + { + __asm__ __volatile__ ("rsr.excvaddr %0;" : "=r"(excvaddr):: "memory"); + /* + "C" reference code for the ASM to document intent. + May also prove useful when issolating possible issues with Extended ASM, + optimizations, new compilers, etc. + */ + uint32_t epc = ef->epc; + uint32_t *pWord = (uint32_t *)(epc & ~3); + uint64_t big_word = ((uint64_t)pWord[1] << 32) | pWord[0]; + uint32_t pos = (epc & 3) * 8; + insn = (uint32_t)(big_word >>= pos); + } +#endif + +#define __EXCEPTION_HANDLER_PREAMBLE(ef, excvaddr, insn) \ + { \ + uint32_t tmp; \ + __asm__ ( \ + "rsr.excvaddr %[vaddr]\n\t" /* Read faulting address as early as possible */ \ + "movi.n %[tmp], ~3\n\t" /* prepare a mask for the EPC */ \ + "and %[tmp], %[tmp], %[epc]\n\t" /* apply mask for 32-bit aligned base */ \ + "ssa8l %[epc]\n\t" /* set up shift register for src op */ \ + "l32i %[insn], %[tmp], 0\n\t" /* load part 1 */ \ + "l32i %[tmp], %[tmp], 4\n\t" /* load part 2 */ \ + "src %[insn], %[tmp], %[insn]\n\t" /* right shift to get faulting instruction */ \ + : [vaddr]"=&r"(excvaddr), [insn]"=&r"(insn), [tmp]"=&r"(tmp) \ + : [epc]"r"(ef->epc) :); \ + } + + #ifdef __cplusplus } #endif diff --git a/cores/esp8266/core_esp8266_noniso.cpp b/cores/esp8266/core_esp8266_noniso.cpp index b341599b0f..fb5d8f573a 100644 --- a/cores/esp8266/core_esp8266_noniso.cpp +++ b/cores/esp8266/core_esp8266_noniso.cpp @@ -1,5 +1,5 @@ /* - core_esp8266_noniso.c - nonstandard (but usefull) conversion functions + core_esp8266_noniso.c - nonstandard (but useful) conversion functions Copyright (c) 2014 Ivan Grokhotkov. All rights reserved. This file is part of the esp8266 core for Arduino environment. @@ -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 3fb027f4fb..4657bb0081 100644 --- a/cores/esp8266/core_esp8266_phy.cpp +++ b/cores/esp8266/core_esp8266_phy.cpp @@ -289,7 +289,7 @@ static const uint8_t ICACHE_FLASH_ATTR phy_init_data[128] = }; -// These functions will be overriden from C++ code. +// These functions will be overridden from C++ code. // Unfortunately, we can't use extern "C" because Arduino preprocessor // doesn't generate forward declarations for extern "C" functions correctly, // so we use mangled names here. @@ -300,15 +300,29 @@ static const uint8_t ICACHE_FLASH_ATTR phy_init_data[128] = static bool spoof_init_data = false; extern int __real_spi_flash_read(uint32_t addr, uint32_t* dst, size_t size); -extern int ICACHE_RAM_ATTR __wrap_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(); -extern int ICACHE_RAM_ATTR __wrap_spi_flash_read(uint32_t addr, uint32_t* dst, size_t size) +/* + 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); @@ -354,6 +374,6 @@ void user_rf_pre_init() } -void ICACHE_RAM_ATTR user_spi_flash_dio_to_qio_pre_init() {} +void IRAM_ATTR user_spi_flash_dio_to_qio_pre_init() {} }; diff --git a/cores/esp8266/core_esp8266_postmortem.cpp b/cores/esp8266/core_esp8266_postmortem.cpp index 3e75342d05..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,20 +309,29 @@ 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) ? '<' : ' '); } } -static void uart_write_char_d(char c) { +static void IRAM_ATTR uart_write_char_d(char c) { uart0_write_char_d(c); uart1_write_char_d(c); } -static void uart0_write_char_d(char c) { +static void IRAM_ATTR uart0_write_char_d(char c) { while (((USS(0) >> USTXC) & 0xff)) { } if (c == '\n') { @@ -259,7 +340,7 @@ static void uart0_write_char_d(char c) { USF(0) = c; } -static void uart1_write_char_d(char c) { +static void IRAM_ATTR uart1_write_char_d(char c) { while (((USS(1) >> USTXC) & 0xff) >= 0x7e) { } if (c == '\n') { @@ -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_si2c.cpp b/cores/esp8266/core_esp8266_si2c.cpp index fa6d164342..98f8115b8b 100644 --- a/cores/esp8266/core_esp8266_si2c.cpp +++ b/cores/esp8266/core_esp8266_si2c.cpp @@ -24,9 +24,8 @@ #include "wiring_private.h" #include "PolledTimeout.h" - - -extern "C" { +extern "C" +{ #include "twi_util.h" #include "ets_sys.h" }; @@ -57,78 +56,113 @@ static inline __attribute__((always_inline)) bool SCL_READ(const int twi_scl) return (GPI & (1 << twi_scl)) != 0; } - -// Implement as a class to reduce code size by allowing access to many global variables with a single base pointer +// Implement as a class to reduce code size by allowing access to many global variables with a +// single base pointer class Twi { private: - unsigned int preferred_si2c_clock = 100000; - uint32_t twi_dcount = 18; - unsigned char twi_sda = 0; - unsigned char twi_scl = 0; - unsigned char twi_addr = 0; - uint32_t twi_clockStretchLimit = 0; - - // These are int-wide, even though they could all fit in a byte, to reduce code size and avoid any potential - // issues about RmW on packed bytes. The int-wide variations of asm instructions are smaller than the equivalent - // byte-wide ones, and since these emums are used everywhere, the difference adds up fast. There is only a single - // instance of the class, though, so the extra 12 bytes of RAM used here saves a lot more IRAM. - volatile enum { TWIPM_UNKNOWN = 0, TWIPM_IDLE, TWIPM_ADDRESSED, TWIPM_WAIT} twip_mode = TWIPM_IDLE; - volatile enum { TWIP_UNKNOWN = 0, TWIP_IDLE, TWIP_START, TWIP_SEND_ACK, TWIP_WAIT_ACK, TWIP_WAIT_STOP, TWIP_SLA_W, TWIP_SLA_R, TWIP_REP_START, TWIP_READ, TWIP_STOP, TWIP_REC_ACK, TWIP_READ_ACK, TWIP_RWAIT_ACK, TWIP_WRITE, TWIP_BUS_ERR } twip_state = TWIP_IDLE; + unsigned int preferred_si2c_clock = 100000; + uint32_t twi_dcount = 18; + unsigned char twi_sda = 0; + unsigned char twi_scl = 0; + unsigned char twi_addr = 0; + uint32_t twi_clockStretchLimit = 0; + + // These are int-wide, even though they could all fit in a byte, to reduce code size and avoid + // any potential issues about RmW on packed bytes. The int-wide variations of asm instructions + // are smaller than the equivalent byte-wide ones, and since these emums are used everywhere, + // the difference adds up fast. There is only a single instance of the class, though, so the + // extra 12 bytes of RAM used here saves a lot more IRAM. + volatile enum { + TWIPM_UNKNOWN = 0, + TWIPM_IDLE, + TWIPM_ADDRESSED, + TWIPM_WAIT + } twip_mode + = TWIPM_IDLE; + volatile enum { + TWIP_UNKNOWN = 0, + TWIP_IDLE, + TWIP_START, + TWIP_SEND_ACK, + TWIP_WAIT_ACK, + TWIP_WAIT_STOP, + TWIP_SLA_W, + TWIP_SLA_R, + TWIP_REP_START, + TWIP_READ, + TWIP_STOP, + TWIP_REC_ACK, + TWIP_READ_ACK, + TWIP_RWAIT_ACK, + TWIP_WRITE, + TWIP_BUS_ERR + } twip_state + = TWIP_IDLE; volatile int twip_status = TW_NO_INFO; - volatile int bitCount = 0; + volatile int bitCount = 0; - volatile uint8_t twi_data = 0x00; - volatile int twi_ack = 0; - volatile int twi_ack_rec = 0; - volatile int twi_timeout_ms = 10; + volatile uint8_t twi_data = 0x00; + volatile int twi_ack = 0; + volatile int twi_ack_rec = 0; + volatile int twi_timeout_ms = 10; volatile enum { TWI_READY = 0, TWI_MRX, TWI_MTX, TWI_SRX, TWI_STX } twi_state = TWI_READY; - volatile uint8_t twi_error = 0xFF; + volatile uint8_t twi_error = 0xFF; - uint8_t twi_txBuffer[TWI_BUFFER_LENGTH]; - volatile int twi_txBufferIndex = 0; + uint8_t twi_txBuffer[TWI_BUFFER_LENGTH]; + volatile int twi_txBufferIndex = 0; volatile int twi_txBufferLength = 0; - uint8_t twi_rxBuffer[TWI_BUFFER_LENGTH]; + uint8_t twi_rxBuffer[TWI_BUFFER_LENGTH]; volatile int twi_rxBufferIndex = 0; void (*twi_onSlaveTransmit)(void); void (*twi_onSlaveReceive)(uint8_t*, size_t); // ETS queue/timer interfaces - enum { EVENTTASK_QUEUE_SIZE = 1, EVENTTASK_QUEUE_PRIO = 2 }; - enum { TWI_SIG_RANGE = 0x00000100, TWI_SIG_RX = 0x00000101, TWI_SIG_TX = 0x00000102 }; + enum + { + EVENTTASK_QUEUE_SIZE = 1, + EVENTTASK_QUEUE_PRIO = 2 + }; + enum + { + TWI_SIG_RANGE = 0x00000100, + TWI_SIG_RX = 0x00000101, + TWI_SIG_TX = 0x00000102 + }; ETSEvent eventTaskQueue[EVENTTASK_QUEUE_SIZE]; ETSTimer timer; // Event/IRQ callbacks, so they can't use "this" and need to be static - static void ICACHE_RAM_ATTR onSclChange(void); - static void ICACHE_RAM_ATTR onSdaChange(void); - static void eventTask(ETSEvent *e); - static void ICACHE_RAM_ATTR onTimer(void *unused); + static void IRAM_ATTR onSclChange(void); + static void IRAM_ATTR onSdaChange(void); + static void eventTask(ETSEvent* e); + static void IRAM_ATTR onTimer(void* unused); // Allow not linking in the slave code if there is no call to setAddress bool _slaveEnabled = false; // Internal use functions - void ICACHE_RAM_ATTR busywait(unsigned int v); - bool write_start(void); - bool write_stop(void); - bool write_bit(bool bit); - bool read_bit(void); - bool write_byte(unsigned char byte); - unsigned char read_byte(bool nack); - void ICACHE_RAM_ATTR onTwipEvent(uint8_t status); + void IRAM_ATTR busywait(unsigned int v); + bool write_start(void); + bool write_stop(void); + bool write_bit(bool bit); + bool read_bit(void); + bool write_byte(unsigned char byte); + unsigned char read_byte(bool nack); + void IRAM_ATTR onTwipEvent(uint8_t status); // Handle the case where a slave needs to stretch the clock with a time-limited busy wait inline void WAIT_CLOCK_STRETCH() { - esp8266::polledTimeout::oneShotFastUs timeout(twi_clockStretchLimit); + esp8266::polledTimeout::oneShotFastUs timeout(twi_clockStretchLimit); esp8266::polledTimeout::periodicFastUs yieldTimeout(5000); - while (!timeout && !SCL_READ(twi_scl)) // outer loop is stretch duration up to stretch limit + while (!timeout + && !SCL_READ(twi_scl)) // outer loop is stretch duration up to stretch limit { - if (yieldTimeout) // inner loop yields every 5ms + if (yieldTimeout) // inner loop yields every 5ms { yield(); } @@ -139,19 +173,21 @@ class Twi void twi_scl_valley(void); public: - void setClock(unsigned int freq); - void setClockStretchLimit(uint32_t limit); - void init(unsigned char sda, unsigned char scl); - void setAddress(uint8_t address); - unsigned char writeTo(unsigned char address, unsigned char * buf, unsigned int len, unsigned char sendStop); - unsigned char readFrom(unsigned char address, unsigned char* buf, unsigned int len, unsigned char sendStop); - uint8_t status(); - uint8_t transmit(const uint8_t* data, uint8_t length); - void attachSlaveRxEvent(void (*function)(uint8_t*, size_t)); - void attachSlaveTxEvent(void (*function)(void)); - void ICACHE_RAM_ATTR reply(uint8_t ack); - void ICACHE_RAM_ATTR releaseBus(void); - void enableSlave(); + void setClock(unsigned int freq); + void setClockStretchLimit(uint32_t limit); + void init(unsigned char sda, unsigned char scl); + void setAddress(uint8_t address); + unsigned char writeTo(unsigned char address, unsigned char* buf, unsigned int len, + unsigned char sendStop); + unsigned char readFrom(unsigned char address, unsigned char* buf, unsigned int len, + unsigned char sendStop); + uint8_t status(); + uint8_t transmit(const uint8_t* data, uint8_t length); + void attachSlaveRxEvent(void (*function)(uint8_t*, size_t)); + void attachSlaveTxEvent(void (*function)(void)); + void IRAM_ATTR reply(uint8_t ack); + void IRAM_ATTR releaseBus(void); + void enableSlave(); }; static Twi twi; @@ -176,7 +212,8 @@ void Twi::setClock(unsigned int freq) freq = 400000; } twi_dcount = (500000000 / freq); // half-cycle period in ns - twi_dcount = (1000 * (twi_dcount - 1120)) / 62500; // (half cycle - overhead) / busywait loop time + twi_dcount + = (1000 * (twi_dcount - 1120)) / 62500; // (half cycle - overhead) / busywait loop time #else @@ -185,7 +222,8 @@ void Twi::setClock(unsigned int freq) freq = 800000; } twi_dcount = (500000000 / freq); // half-cycle period in ns - twi_dcount = (1000 * (twi_dcount - 560)) / 31250; // (half cycle - overhead) / busywait loop time + twi_dcount + = (1000 * (twi_dcount - 560)) / 31250; // (half cycle - overhead) / busywait loop time #endif } @@ -195,8 +233,6 @@ void Twi::setClockStretchLimit(uint32_t limit) twi_clockStretchLimit = limit; } - - void Twi::init(unsigned char sda, unsigned char scl) { // set timer function @@ -210,7 +246,7 @@ void Twi::init(unsigned char sda, unsigned char scl) pinMode(twi_sda, INPUT_PULLUP); pinMode(twi_scl, INPUT_PULLUP); twi_setClock(preferred_si2c_clock); - twi_setClockStretchLimit(150000L); // default value is 150 mS + twi_setClockStretchLimit(150000L); // default value is 150 mS } void Twi::setAddress(uint8_t address) @@ -229,12 +265,13 @@ void Twi::enableSlave() } } -void ICACHE_RAM_ATTR Twi::busywait(unsigned int v) +void IRAM_ATTR Twi::busywait(unsigned int v) { unsigned int i; for (i = 0; i < v; i++) // loop time is 5 machine cycles: 31.25ns @ 160MHz, 62.5ns @ 80MHz { - __asm__ __volatile__("nop"); // minimum element to keep GCC from optimizing this function out. + __asm__ __volatile__( + "nop"); // minimum element to keep GCC from optimizing this function out. } } @@ -247,8 +284,13 @@ bool Twi::write_start(void) return false; } busywait(twi_dcount); + // A high-to-low transition on the SDA line while the SCL is high defines a START condition. SDA_LOW(twi_sda); busywait(twi_dcount); + // An additional delay between the SCL line high-to-low transition and setting up the SDA line + // to prevent a STOP condition execute. + SCL_LOW(twi_scl); + busywait(twi_dcount); return true; } @@ -260,6 +302,7 @@ bool Twi::write_stop(void) SCL_HIGH(twi_scl); WAIT_CLOCK_STRETCH(); busywait(twi_dcount); + // A low-to-high transition on the SDA line while the SCL is high defines a STOP condition. SDA_HIGH(twi_sda); busywait(twi_dcount); return true; @@ -303,7 +346,7 @@ bool Twi::write_byte(unsigned char byte) write_bit(byte & 0x80); byte <<= 1; } - return !read_bit();//NACK/ACK + return !read_bit(); // NACK/ACK } unsigned char Twi::read_byte(bool nack) @@ -318,12 +361,13 @@ unsigned char Twi::read_byte(bool nack) return byte; } -unsigned char Twi::writeTo(unsigned char address, unsigned char * buf, unsigned int len, unsigned char sendStop) +unsigned char Twi::writeTo(unsigned char address, unsigned char* buf, unsigned int len, + unsigned char sendStop) { unsigned int i; if (!write_start()) { - return 4; //line busy + return 4; // line busy } if (!write_byte(((address << 1) | 0) & 0xFF)) { @@ -331,7 +375,7 @@ unsigned char Twi::writeTo(unsigned char address, unsigned char * buf, unsigned { write_stop(); } - return 2; //received NACK on transmit of address + return 2; // received NACK on transmit of address } for (i = 0; i < len; i++) { @@ -341,7 +385,7 @@ unsigned char Twi::writeTo(unsigned char address, unsigned char * buf, unsigned { write_stop(); } - return 3;//received NACK on transmit of data + return 3; // received NACK on transmit of data } } if (sendStop) @@ -363,12 +407,13 @@ unsigned char Twi::writeTo(unsigned char address, unsigned char * buf, unsigned return 0; } -unsigned char Twi::readFrom(unsigned char address, unsigned char* buf, unsigned int len, unsigned char sendStop) +unsigned char Twi::readFrom(unsigned char address, unsigned char* buf, unsigned int len, + unsigned char sendStop) { unsigned int i; if (!write_start()) { - return 4; //line busy + return 4; // line busy } if (!write_byte(((address << 1) | 1) & 0xFF)) { @@ -376,7 +421,7 @@ unsigned char Twi::readFrom(unsigned char address, unsigned char* buf, unsigned { write_stop(); } - return 2;//received NACK on transmit of address + return 2; // received NACK on transmit of address } for (i = 0; i < (len - 1); i++) { @@ -415,21 +460,25 @@ uint8_t Twi::status() WAIT_CLOCK_STRETCH(); // wait for a slow slave to finish if (!SCL_READ(twi_scl)) { - return I2C_SCL_HELD_LOW; // SCL held low by another device, no procedure available to recover + return I2C_SCL_HELD_LOW; // SCL held low by another device, no procedure available to + // recover } int clockCount = 20; - while (!SDA_READ(twi_sda) && clockCount-- > 0) // if SDA low, read the bits slaves have to sent to a max + while (!SDA_READ(twi_sda) + && clockCount-- > 0) // if SDA low, read the bits slaves have to sent to a max { read_bit(); if (!SCL_READ(twi_scl)) { - return I2C_SCL_HELD_LOW_AFTER_READ; // I2C bus error. SCL held low beyond slave clock stretch time + return I2C_SCL_HELD_LOW_AFTER_READ; // I2C bus error. SCL held low beyond slave clock + // stretch time } } if (!SDA_READ(twi_sda)) { - return I2C_SDA_HELD_LOW; // I2C bus error. SDA line held low by slave/another_master after n bits. + return I2C_SDA_HELD_LOW; // I2C bus error. SDA line held low by slave/another_master after + // n bits. } return I2C_OK; @@ -471,58 +520,57 @@ void Twi::attachSlaveTxEvent(void (*function)(void)) twi_onSlaveTransmit = function; } -// DO NOT INLINE, inlining reply() in combination with compiler optimizations causes function breakup into -// parts and the ICACHE_RAM_ATTR isn't propagated correctly to all parts, which of course causes crashes. +// DO NOT INLINE, inlining reply() in combination with compiler optimizations causes function +// breakup into parts and the IRAM_ATTR isn't propagated correctly to all parts, which of course +// causes crashes. // TODO: test with gcc 9.x and if it still fails, disable optimization with -fdisable-ipa-fnsplit -void ICACHE_RAM_ATTR Twi::reply(uint8_t ack) +void IRAM_ATTR Twi::reply(uint8_t ack) { // transmit master read ready signal, with or without ack if (ack) { - //TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA); - SCL_HIGH(twi.twi_scl); // _BV(TWINT) - twi_ack = 1; // _BV(TWEA) + // TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA); + SCL_HIGH(twi.twi_scl); // _BV(TWINT) + twi_ack = 1; // _BV(TWEA) } else { - //TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT); - SCL_HIGH(twi.twi_scl); // _BV(TWINT) - twi_ack = 0; // ~_BV(TWEA) + // TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT); + SCL_HIGH(twi.twi_scl); // _BV(TWINT) + twi_ack = 0; // ~_BV(TWEA) } } - -void ICACHE_RAM_ATTR Twi::releaseBus(void) +void IRAM_ATTR Twi::releaseBus(void) { // release bus - //TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT); - SCL_HIGH(twi.twi_scl); // _BV(TWINT) - twi_ack = 1; // _BV(TWEA) + // TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT); + SCL_HIGH(twi.twi_scl); // _BV(TWINT) + twi_ack = 1; // _BV(TWEA) SDA_HIGH(twi.twi_sda); // update twi state twi_state = TWI_READY; } - -void ICACHE_RAM_ATTR Twi::onTwipEvent(uint8_t status) +void IRAM_ATTR Twi::onTwipEvent(uint8_t status) { twip_status = status; switch (status) { // Slave Receiver - case TW_SR_SLA_ACK: // addressed, returned ack - case TW_SR_GCALL_ACK: // addressed generally, returned ack - case TW_SR_ARB_LOST_SLA_ACK: // lost arbitration, returned ack - case TW_SR_ARB_LOST_GCALL_ACK: // lost arbitration, returned ack + case TW_SR_SLA_ACK: // addressed, returned ack + case TW_SR_GCALL_ACK: // addressed generally, returned ack + case TW_SR_ARB_LOST_SLA_ACK: // lost arbitration, returned ack + case TW_SR_ARB_LOST_GCALL_ACK: // lost arbitration, returned ack // enter slave receiver mode twi_state = TWI_SRX; // indicate that rx buffer can be overwritten and ack twi_rxBufferIndex = 0; reply(1); break; - case TW_SR_DATA_ACK: // data received, returned ack - case TW_SR_GCALL_DATA_ACK: // data received generally, returned ack + case TW_SR_DATA_ACK: // data received, returned ack + case TW_SR_GCALL_DATA_ACK: // data received generally, returned ack // if there is still room in the rx buffer if (twi_rxBufferIndex < TWI_BUFFER_LENGTH) { @@ -536,29 +584,29 @@ void ICACHE_RAM_ATTR Twi::onTwipEvent(uint8_t status) reply(0); } break; - case TW_SR_STOP: // stop or repeated start condition received + case TW_SR_STOP: // stop or repeated start condition received // put a null char after data if there's room if (twi_rxBufferIndex < TWI_BUFFER_LENGTH) { twi_rxBuffer[twi_rxBufferIndex] = '\0'; } // callback to user-defined callback over event task to allow for non-RAM-residing code - //twi_rxBufferLock = true; // This may be necessary + // twi_rxBufferLock = true; // This may be necessary ets_post(EVENTTASK_QUEUE_PRIO, TWI_SIG_RX, twi_rxBufferIndex); // since we submit rx buffer to "wire" library, we can reset it twi_rxBufferIndex = 0; break; - case TW_SR_DATA_NACK: // data received, returned nack - case TW_SR_GCALL_DATA_NACK: // data received generally, returned nack + case TW_SR_DATA_NACK: // data received, returned nack + case TW_SR_GCALL_DATA_NACK: // data received generally, returned nack // nack back at master reply(0); break; // Slave Transmitter - case TW_ST_SLA_ACK: // addressed, returned ack - case TW_ST_ARB_LOST_SLA_ACK: // arbitration lost, returned ack + case TW_ST_SLA_ACK: // addressed, returned ack + case TW_ST_ARB_LOST_SLA_ACK: // arbitration lost, returned ack // enter slave transmitter mode twi_state = TWI_STX; // ready the tx buffer index for iteration @@ -571,7 +619,7 @@ void ICACHE_RAM_ATTR Twi::onTwipEvent(uint8_t status) ets_post(EVENTTASK_QUEUE_PRIO, TWI_SIG_TX, 0); break; - case TW_ST_DATA_ACK: // byte sent, ack returned + case TW_ST_DATA_ACK: // byte sent, ack returned // copy data to output register twi_data = twi_txBuffer[twi_txBufferIndex++]; @@ -597,33 +645,32 @@ void ICACHE_RAM_ATTR Twi::onTwipEvent(uint8_t status) reply(0); } break; - case TW_ST_DATA_NACK: // received nack, we are done - case TW_ST_LAST_DATA: // received ack, but we are done already! + case TW_ST_DATA_NACK: // received nack, we are done + case TW_ST_LAST_DATA: // received ack, but we are done already! // leave slave receiver state releaseBus(); break; // All - case TW_NO_INFO: // no state information + case TW_NO_INFO: // no state information break; - case TW_BUS_ERROR: // bus error, illegal stop/start + case TW_BUS_ERROR: // bus error, illegal stop/start twi_error = TW_BUS_ERROR; break; } } -void ICACHE_RAM_ATTR Twi::onTimer(void *unused) +void IRAM_ATTR Twi::onTimer(void* unused) { (void)unused; twi.releaseBus(); twi.onTwipEvent(TW_BUS_ERROR); - twi.twip_mode = TWIPM_WAIT; + twi.twip_mode = TWIPM_WAIT; twi.twip_state = TWIP_BUS_ERR; } -void Twi::eventTask(ETSEvent *e) +void Twi::eventTask(ETSEvent* e) { - if (e == NULL) { return; @@ -638,7 +685,7 @@ void Twi::eventTask(ETSEvent *e) if (twi.twi_txBufferLength == 0) { twi.twi_txBufferLength = 1; - twi.twi_txBuffer[0] = 0x00; + twi.twi_txBuffer[0] = 0x00; } // Initiate transmission @@ -658,11 +705,11 @@ void Twi::eventTask(ETSEvent *e) // compared to the logical-or of all states with the same branch. This removes the need // for a large series of straight-line compares. The biggest win is when multiple states // all have the same branch (onSdaChange), but for others there is some benefit, still. -#define S2M(x) (1<<(x)) +#define S2M(x) (1 << (x)) // Shorthand for if the state is any of the or'd bitmask x #define IFSTATE(x) if (twip_state_mask & (x)) -void ICACHE_RAM_ATTR Twi::onSclChange(void) +void IRAM_ATTR Twi::onSclChange(void) { unsigned int sda; unsigned int scl; @@ -672,7 +719,7 @@ void ICACHE_RAM_ATTR Twi::onSclChange(void) sda = SDA_READ(twi.twi_sda); scl = SCL_READ(twi.twi_scl); - twi.twip_status = 0xF8; // reset TWI status + twi.twip_status = 0xF8; // reset TWI status int twip_state_mask = S2M(twi.twip_state); IFSTATE(S2M(TWIP_START) | S2M(TWIP_REP_START) | S2M(TWIP_SLA_W) | S2M(TWIP_READ)) @@ -747,13 +794,13 @@ void ICACHE_RAM_ATTR Twi::onSclChange(void) } else { - SCL_LOW(twi.twi_scl); // clock stretching + SCL_LOW(twi.twi_scl); // clock stretching SDA_HIGH(twi.twi_sda); twi.twip_mode = TWIPM_ADDRESSED; if (!(twi.twi_data & 0x01)) { twi.onTwipEvent(TW_SR_SLA_ACK); - twi.bitCount = 8; + twi.bitCount = 8; twi.twip_state = TWIP_SLA_W; } else @@ -765,18 +812,18 @@ void ICACHE_RAM_ATTR Twi::onSclChange(void) } else { - SCL_LOW(twi.twi_scl); // clock stretching + SCL_LOW(twi.twi_scl); // clock stretching SDA_HIGH(twi.twi_sda); if (!twi.twi_ack) { twi.onTwipEvent(TW_SR_DATA_NACK); - twi.twip_mode = TWIPM_WAIT; + twi.twip_mode = TWIPM_WAIT; twi.twip_state = TWIP_WAIT_STOP; } else { twi.onTwipEvent(TW_SR_DATA_ACK); - twi.bitCount = 8; + twi.bitCount = 8; twi.twip_state = TWIP_READ; } } @@ -832,7 +879,7 @@ void ICACHE_RAM_ATTR Twi::onSclChange(void) else { twi.twi_ack_rec = !sda; - twi.twip_state = TWIP_RWAIT_ACK; + twi.twip_state = TWIP_RWAIT_ACK; } } else IFSTATE(S2M(TWIP_RWAIT_ACK)) @@ -843,7 +890,7 @@ void ICACHE_RAM_ATTR Twi::onSclChange(void) } else { - SCL_LOW(twi.twi_scl); // clock stretching + SCL_LOW(twi.twi_scl); // clock stretching if (twi.twi_ack && twi.twi_ack_rec) { twi.onTwipEvent(TW_ST_DATA_ACK); @@ -853,14 +900,14 @@ void ICACHE_RAM_ATTR Twi::onSclChange(void) { // we have no more data to send and/or the master doesn't want anymore twi.onTwipEvent(twi.twi_ack_rec ? TW_ST_LAST_DATA : TW_ST_DATA_NACK); - twi.twip_mode = TWIPM_WAIT; + twi.twip_mode = TWIPM_WAIT; twi.twip_state = TWIP_WAIT_STOP; } } } } -void ICACHE_RAM_ATTR Twi::onSdaChange(void) +void IRAM_ATTR Twi::onSdaChange(void) { unsigned int sda; unsigned int scl; @@ -870,7 +917,7 @@ void ICACHE_RAM_ATTR Twi::onSdaChange(void) scl = SCL_READ(twi.twi_scl); int twip_state_mask = S2M(twi.twip_state); - if (scl) /* !DATA */ + if (scl) /* !DATA */ { IFSTATE(S2M(TWIP_IDLE)) { @@ -881,17 +928,19 @@ void ICACHE_RAM_ATTR Twi::onSdaChange(void) else { // START - twi.bitCount = 8; + twi.bitCount = 8; twi.twip_state = TWIP_START; - ets_timer_arm_new(&twi.timer, twi.twi_timeout_ms, false, true); // Once, ms + ets_timer_arm_new(&twi.timer, twi.twi_timeout_ms, false, true); // Once, ms } } - else IFSTATE(S2M(TWIP_START) | S2M(TWIP_REP_START) | S2M(TWIP_SEND_ACK) | S2M(TWIP_WAIT_ACK) | S2M(TWIP_SLA_R) | S2M(TWIP_REC_ACK) | S2M(TWIP_READ_ACK) | S2M(TWIP_RWAIT_ACK) | S2M(TWIP_WRITE)) + else IFSTATE(S2M(TWIP_START) | S2M(TWIP_REP_START) | S2M(TWIP_SEND_ACK) | S2M(TWIP_WAIT_ACK) + | S2M(TWIP_SLA_R) | S2M(TWIP_REC_ACK) | S2M(TWIP_READ_ACK) + | S2M(TWIP_RWAIT_ACK) | S2M(TWIP_WRITE)) { // START or STOP - SDA_HIGH(twi.twi_sda); // Should not be necessary + SDA_HIGH(twi.twi_sda); // Should not be necessary twi.onTwipEvent(TW_BUS_ERROR); - twi.twip_mode = TWIPM_WAIT; + twi.twip_mode = TWIPM_WAIT; twi.twip_state = TWIP_BUS_ERR; } else IFSTATE(S2M(TWIP_WAIT_STOP) | S2M(TWIP_BUS_ERR)) @@ -899,10 +948,10 @@ void ICACHE_RAM_ATTR Twi::onSdaChange(void) if (sda) { // STOP - SCL_LOW(twi.twi_scl); // generates a low SCL pulse after STOP + SCL_LOW(twi.twi_scl); // generates a low SCL pulse after STOP ets_timer_disarm(&twi.timer); twi.twip_state = TWIP_IDLE; - twi.twip_mode = TWIPM_IDLE; + twi.twip_mode = TWIPM_IDLE; SCL_HIGH(twi.twi_scl); } else @@ -914,9 +963,9 @@ void ICACHE_RAM_ATTR Twi::onSdaChange(void) } else { - twi.bitCount = 8; + twi.bitCount = 8; twi.twip_state = TWIP_REP_START; - ets_timer_arm_new(&twi.timer, twi.twi_timeout_ms, false, true); // Once, ms + ets_timer_arm_new(&twi.timer, twi.twi_timeout_ms, false, true); // Once, ms } } } @@ -927,28 +976,28 @@ void ICACHE_RAM_ATTR Twi::onSdaChange(void) { // inside byte transfer - error twi.onTwipEvent(TW_BUS_ERROR); - twi.twip_mode = TWIPM_WAIT; + twi.twip_mode = TWIPM_WAIT; twi.twip_state = TWIP_BUS_ERR; } else { // during first bit in byte transfer - ok - SCL_LOW(twi.twi_scl); // clock stretching + SCL_LOW(twi.twi_scl); // clock stretching twi.onTwipEvent(TW_SR_STOP); if (sda) { // STOP ets_timer_disarm(&twi.timer); twi.twip_state = TWIP_IDLE; - twi.twip_mode = TWIPM_IDLE; + twi.twip_mode = TWIPM_IDLE; } else { // START twi.bitCount = 8; - ets_timer_arm_new(&twi.timer, twi.twi_timeout_ms, false, true); // Once, ms + ets_timer_arm_new(&twi.timer, twi.twi_timeout_ms, false, true); // Once, ms twi.twip_state = TWIP_REP_START; - twi.twip_mode = TWIPM_IDLE; + twi.twip_mode = TWIPM_IDLE; } } } @@ -956,8 +1005,8 @@ void ICACHE_RAM_ATTR Twi::onSdaChange(void) } // C wrappers for the object, since API is exposed only as C -extern "C" { - +extern "C" +{ void twi_init(unsigned char sda, unsigned char scl) { return twi.init(sda, scl); @@ -978,12 +1027,14 @@ extern "C" { twi.setClockStretchLimit(limit); } - uint8_t twi_writeTo(unsigned char address, unsigned char * buf, unsigned int len, unsigned char sendStop) + uint8_t twi_writeTo(unsigned char address, unsigned char* buf, unsigned int len, + unsigned char sendStop) { return twi.writeTo(address, buf, len, sendStop); } - uint8_t twi_readFrom(unsigned char address, unsigned char * buf, unsigned int len, unsigned char sendStop) + uint8_t twi_readFrom(unsigned char address, unsigned char* buf, unsigned int len, + unsigned char sendStop) { return twi.readFrom(address, buf, len, sendStop); } @@ -993,7 +1044,7 @@ extern "C" { return twi.status(); } - uint8_t twi_transmit(const uint8_t * buf, uint8_t len) + uint8_t twi_transmit(const uint8_t* buf, uint8_t len) { return twi.transmit(buf, len); } @@ -1022,5 +1073,4 @@ extern "C" { { twi.enableSlave(); } - }; diff --git a/cores/esp8266/core_esp8266_spi_utils.cpp b/cores/esp8266/core_esp8266_spi_utils.cpp index ccf800e7d9..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+offest. + // 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 busses. The ESP8266 has bus 0 and 1, + * 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_timer.cpp b/cores/esp8266/core_esp8266_timer.cpp index af15de7fa9..02bd4aa470 100644 --- a/cores/esp8266/core_esp8266_timer.cpp +++ b/cores/esp8266/core_esp8266_timer.cpp @@ -31,7 +31,7 @@ extern "C" { static volatile timercallback timer1_user_cb = NULL; -void ICACHE_RAM_ATTR timer1_isr_handler(void *para, void *frame) { +void IRAM_ATTR timer1_isr_handler(void *para, void *frame) { (void) para; (void) frame; if ((T1C & ((1 << TCAR) | (1 << TCIT))) == 0) TEIE &= ~TEIE1;//edge int disable @@ -45,32 +45,32 @@ void ICACHE_RAM_ATTR timer1_isr_handler(void *para, void *frame) { } } -void ICACHE_RAM_ATTR timer1_isr_init(){ +void IRAM_ATTR timer1_isr_init(){ ETS_FRC_TIMER1_INTR_ATTACH(timer1_isr_handler, NULL); } -void ICACHE_RAM_ATTR timer1_attachInterrupt(timercallback userFunc) { +void IRAM_ATTR timer1_attachInterrupt(timercallback userFunc) { timer1_user_cb = userFunc; ETS_FRC1_INTR_ENABLE(); } -void ICACHE_RAM_ATTR timer1_detachInterrupt() { +void IRAM_ATTR timer1_detachInterrupt() { timer1_user_cb = 0; TEIE &= ~TEIE1;//edge int disable ETS_FRC1_INTR_DISABLE(); } -void ICACHE_RAM_ATTR timer1_enable(uint8_t divider, uint8_t int_type, uint8_t reload){ +void IRAM_ATTR timer1_enable(uint8_t divider, uint8_t int_type, uint8_t reload){ T1C = (1 << TCTE) | ((divider & 3) << TCPD) | ((int_type & 1) << TCIT) | ((reload & 1) << TCAR); T1I = 0; } -void ICACHE_RAM_ATTR timer1_write(uint32_t ticks){ +void IRAM_ATTR timer1_write(uint32_t ticks){ T1L = ((ticks)& 0x7FFFFF); if ((T1C & (1 << TCIT)) == 0) TEIE |= TEIE1;//edge int enable } -void ICACHE_RAM_ATTR timer1_disable(){ +void IRAM_ATTR timer1_disable(){ T1C = 0; T1I = 0; } @@ -80,7 +80,7 @@ void ICACHE_RAM_ATTR timer1_disable(){ static volatile timercallback timer0_user_cb = NULL; -void ICACHE_RAM_ATTR timer0_isr_handler(void *para, void *frame) { +void IRAM_ATTR timer0_isr_handler(void *para, void *frame) { (void) para; (void) frame; if (timer0_user_cb) { @@ -92,16 +92,16 @@ void ICACHE_RAM_ATTR timer0_isr_handler(void *para, void *frame) { } } -void ICACHE_RAM_ATTR timer0_isr_init(){ +void IRAM_ATTR timer0_isr_init(){ ETS_CCOMPARE0_INTR_ATTACH(timer0_isr_handler, NULL); } -void ICACHE_RAM_ATTR timer0_attachInterrupt(timercallback userFunc) { +void IRAM_ATTR timer0_attachInterrupt(timercallback userFunc) { timer0_user_cb = userFunc; ETS_CCOMPARE0_ENABLE(); } -void ICACHE_RAM_ATTR timer0_detachInterrupt() { +void IRAM_ATTR timer0_detachInterrupt() { timer0_user_cb = NULL; ETS_CCOMPARE0_DISABLE(); } diff --git a/cores/esp8266/core_esp8266_version.h b/cores/esp8266/core_esp8266_version.h index 856d9f8f43..b294d589bc 100644 --- a/cores/esp8266/core_esp8266_version.h +++ b/cores/esp8266/core_esp8266_version.h @@ -1,4 +1,3 @@ - /* core_esp8266_version.h - parse "git describe" at compile time Copyright (c) 2018 david gauchard. All rights reserved. @@ -159,7 +158,7 @@ int coreVersionSubRevision () } /* - * unique revision indentifier (never decreases) + * unique revision identifier (never decreases) */ constexpr int coreVersionNumeric () diff --git a/cores/esp8266/core_esp8266_vm.cpp b/cores/esp8266/core_esp8266_vm.cpp new file mode 100644 index 0000000000..00f50e3981 --- /dev/null +++ b/cores/esp8266/core_esp8266_vm.cpp @@ -0,0 +1,407 @@ +/* + core_esp8266_vm - Implements logic to enable external SRAM/PSRAM to be used + as if it were on-chip memory by code. + + Copyright (c) 2020 Earle F. Philhower, III All rights reserved. + + 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 + + + The original exception handler idea was taken from @pvvx's public domain + misaligned-flash-read exception handler, available here: + https://github.com/pvvx/esp8266web/blob/master/app/sdklib/system/app_main.c + + + Theory of Operation: + + The Xtensa core generates a hardware exception (unrelated to C++ exceptions) + when an address that's defined as invalid for load or store. The XTOS ROM + routines capture the machine state and call a standard C exception handler + routine (or the default one which resets the system). + + We hook into this exception callback and decode the EXCVADDR (the address + being accessed) and use the exception PC to read out the faulting + instruction. We decode that instruction and simulate it's behavior + (i.e. either loading or storing some data to a register/external memory) + and then return to the calling application. + + We use the hardware SPI interface to talk to an external SRAM/PSRAM, and + implement a simple cache to minimize the amount of times we actually need + to go out over the (slow) SPI bus. The SPI is set up in a DIO mode which + uses no more pins than normal SPI, but provides for ~2X faster transfers. + + NOTE: This works fine for processor accesses, but cannot be used by any + of the peripherals' DMA. For that, we'd need a real MMU. + + Hardware Configuration (make sure you have 3.3V compatible SRAMs): + * SPI interfaced byte-addressible SRAM/PSRAM: 24LC1024 or smaller + CS -> GPIO15 + SCK -> GPIO14 + MOSI -> GPIO13 + MISO -> GPIO12 + (note these are GPIO numbers, not the Arduion Dxx ones. Refer to your + ESP8266 board schematic for the mapping of GPIO to pin.) + * Higher density PSRAM (ESP-PSRAM64H/etc.) works as well, but may be too + large to effectively use with UMM. Only 256K is available vial malloc, + but addresses above 256K do work and can be used for fixed buffers. + +*/ + +#ifdef MMU_EXTERNAL_HEAP + +#include +#include +#include "esp8266_peri.h" +#include "core_esp8266_vm.h" +#include "core_esp8266_non32xfer.h" +#include "umm_malloc/umm_malloc.h" + + +extern "C" { + +#define VM_OFFSET_MASK 0x007fffffu + +#define SHORT_MASK 0x000008u +#define LOAD_MASK 0x00f00fu +#define L8UI_MATCH 0x000002u +#define L16UI_MATCH 0x001002u +#define L16SI_MATCH 0x009002u +#define L16_MASK 0x001000u +#define SIGNED_MASK 0x008000u +#define L32IN_MATCH 0x000008u +#define L32I_MATCH 0x002002u +#define L32R_MATCH 0x000001u +#define L32_MASK 0x002009u + +#define STORE_MASK 0x00f00fu +#define S8I_MATCH 0x004002u +#define S16I_MATCH 0x005002u +#define S16_MASK 0x001000u +#define S32I_MATCH 0x006002u +#define S32IN_MATCH 0x000009u +#define S32_MASK 0x002001u + +#define EXCCAUSE_LOAD_PROHIBITED 28 // Cache Attribute does not allow Load +#define EXCCAUSE_STORE_PROHIBITED 29 // Cache Attribute does not allow Store +#define EXCCAUSE_STORE_MASK 1 // Fast way of deciding if it's a ld or s that faulted + +// MINI SPI implementation inlined to have max performance and minimum code +// bloat. Can't include a library (SPI) in the core, anyway. + +// Place in a struct so hopefully compiler will generate smaller, base+offset +// based code to access it +typedef struct { + volatile uint32_t spi_cmd; // The SPI can change this behind our backs, so volatile! + uint32_t spi_addr; + uint32_t spi_ctrl; + uint32_t spi_ctrl1; // undocumented? Not shown in the reg map + uint32_t spi_rd_status; + uint32_t spi_ctrl2; + uint32_t spi_clock; + uint32_t spi_user; + uint32_t spi_user1; + uint32_t spi_user2; + uint32_t spi_wr_status; + uint32_t spi_pin; + uint32_t spi_slave; + uint32_t spi_slave1; + uint32_t spi_slave2; + uint32_t spi_slave3; + uint32_t spi_w[16]; // NOTE: You need a memory barrier before reading these after a read xaction + uint32_t spi_ext3; +} spi_regs; + +// The standard HSPI bus pins are used +constexpr uint8_t cs = 15; +constexpr uint8_t miso = 12; +constexpr uint8_t mosi = 13; +constexpr uint8_t sck = 14; + +#define DECLARE_SPI1 spi_regs *spi1 = (spi_regs*)&SPI1CMD + +typedef enum { spi_5mhz = 0x001c1001, spi_10mhz = 0x000c1001, spi_20mhz = 0x00041001, spi_30mhz = 0x00002001, spi_40mhz = 0x00001001 } spi_clocking; +typedef enum { sio = 0, dio = 1 } iotype; + +#if MMU_EXTERNAL_HEAP > 128 + constexpr uint32_t spi_clkval = spi_40mhz; + constexpr iotype hspi_mode = sio; +#else + constexpr uint32_t spi_clkval = spi_20mhz; + constexpr iotype hspi_mode = dio; +#endif + +constexpr int read_delay = (hspi_mode == dio) ? 4-1 : 0; + +constexpr int cache_ways = 4; // N-way, fully associative cache +constexpr int cache_words = 16; // Must be 16 words or smaller to fit in SPI buffer + +static struct cache_line { + int32_t addr; // Address, lower bits masked off + int dirty; // Needs writeback + struct cache_line *next; // We'll keep linked list in MRU order + union { + uint32_t w[cache_words]; + uint16_t s[cache_words * 2]; + uint8_t b[cache_words * 4]; + }; +} __vm_cache_line[cache_ways]; +static struct cache_line *__vm_cache; // Always points to MRU (hence the line being read/written) + +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); + // 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_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 + +// The SPI hardware cannot make the "command" portion dual or quad, only the addr and data +// So using the command portion of the cycle will not work. Comcatenate the address +// and command into a single 32-bit chunk "address" which will be sent across both bits. + +inline IRAM_ATTR void spi_writetransaction(spi_regs *spi1, int addr, int addr_bits, int dummy_bits, int data_bits, iotype dual) +{ + // Ensure no writes are still ongoing + while (spi1->spi_cmd & SPIBUSY) { /* busywait */ } + + spi1->spi_addr = addr; + spi1->spi_user = (addr_bits? SPIUADDR : 0) | (dummy_bits ? SPIUDUMMY : 0) | (data_bits ? SPIUMOSI : 0) | (dual ? SPIUFWDIO : 0); + spi1->spi_user1 = (addr_bits << 26) | (data_bits << 17) | dummy_bits; + // No need to set spi_user2, insn field never used + __asm ( "" ::: "memory" ); + spi1->spi_cmd = SPIBUSY; + // The write may continue on in the background, letting core do useful work instead of waiting, unless we're in cacheless mode + if (cache_ways == 0) { + while (spi1->spi_cmd & SPIBUSY) { /* busywait */ } + } +} + +inline IRAM_ATTR uint32_t spi_readtransaction(spi_regs *spi1, int addr, int addr_bits, int dummy_bits, int data_bits, iotype dual) +{ + // Ensure no writes are still ongoing + while (spi1->spi_cmd & SPIBUSY) { /* busywait */ } + + spi1->spi_addr = addr; + spi1->spi_user = (addr_bits? SPIUADDR : 0) | (dummy_bits ? SPIUDUMMY : 0) | SPIUMISO | (dual ? SPIUFWDIO : 0); + spi1->spi_user1 = (addr_bits << 26) | (data_bits << 8) | dummy_bits; + // No need to set spi_user2, insn field never used + __asm ( "" ::: "memory" ); + spi1->spi_cmd = SPIBUSY; + while (spi1->spi_cmd & SPIBUSY) { /* busywait */ } + __asm ( "" ::: "memory" ); + return spi1->spi_w[0]; +} + +static inline IRAM_ATTR void cache_flushrefill(spi_regs *spi1, int addr) +{ + addr &= addrmask; + struct cache_line *way = __vm_cache; + + if (__vm_cache->addr == addr) return; // Fast case, it already is the MRU + struct cache_line *last = way; + way = way->next; + + for (auto i = 1; i < cache_ways; i++) { + if (way->addr == addr) { + last->next = way->next; + way->next = __vm_cache; + __vm_cache = way; + return; + } else { + last = way; + way = way->next; + } + } + + // At this point we know the line is not in the cache and way points to the LRU. + + // We allow reads to go before writes since the write can happen in the background. + // We need to keep the data to be written back since it will be overwritten with read data + uint32_t wb[cache_words]; + if (last->dirty) { + memcpy(wb, last->w, sizeof(last->w)); + } + + // Update MRU info, list + last->next = __vm_cache; + __vm_cache = last; + + // Do the actual read + spi_readtransaction(spi1, (0x03 << 24) | addr, 32-1, read_delay, sizeof(last->w) * 8 - 1, hspi_mode); + memcpy(last->w, spi1->spi_w, sizeof(last->w)); + + // We fire a background writeback now, if needed + if (last->dirty) { + memcpy(spi1->spi_w, wb, sizeof(wb)); + spi_writetransaction(spi1, (0x02 << 24) | last->addr, 32-1, 0, sizeof(last->w) * 8 - 1, hspi_mode); + last->dirty = 0; + } + + // Update the addr at this point since we no longer need the old one + last->addr = addr; +} + +static inline IRAM_ATTR void spi_ramwrite(spi_regs *spi1, int addr, int data_bits, uint32_t val) +{ + if (cache_ways == 0) { + spi1->spi_w[0] = val; + spi_writetransaction(spi1, (0x02<<24) | addr, 32-1, 0, data_bits, hspi_mode); + } else { + cache_flushrefill(spi1, addr); + __vm_cache->dirty = 1; + addr -= __vm_cache->addr; + switch (data_bits) { + case 31: __vm_cache->w[addr >> 2] = val; break; + case 7: __vm_cache->b[addr] = val; break; + default: __vm_cache->s[addr >> 1] = val; break; + } + } +} + +static inline IRAM_ATTR uint32_t spi_ramread(spi_regs *spi1, int addr, int data_bits) +{ + if (cache_ways == 0) { + spi1->spi_w[0] = 0; + return spi_readtransaction(spi1, (0x03 << 24) | addr, 32-1, read_delay, data_bits, hspi_mode); + } else { + cache_flushrefill(spi1, addr); + addr -= __vm_cache->addr; + switch (data_bits) { + case 31: return __vm_cache->w[addr >> 2]; + case 7: return __vm_cache->b[addr]; + default: return __vm_cache->s[addr >> 1]; + } + } +} + +static void (*__old_handler)(struct __exception_frame *ef, int cause); + +static IRAM_ATTR void loadstore_exception_handler(struct __exception_frame *ef, int cause) +{ + uint32_t excvaddr; + uint32_t insn; + + /* Extract instruction and faulting data address */ + __EXCEPTION_HANDLER_PREAMBLE(ef, excvaddr, insn); + + // Check that we're really accessing VM and not some other illegal range + if ((excvaddr >> 28) != 1) { + // Reinstall the old handler, and retry the instruction to keep us out of the stack dump + _xtos_set_exception_handler(EXCCAUSE_LOAD_PROHIBITED, __old_handler); + _xtos_set_exception_handler(EXCCAUSE_STORE_PROHIBITED, __old_handler); + return; + } + + DECLARE_SPI1; + ef->epc += (insn & SHORT_MASK) ? 2 : 3; // resume at following instruction + + int regno = (insn & 0x0000f0u) >> 4; + if (regno != 0) --regno; // account for skipped a1 in exception_frame + + if (cause & EXCCAUSE_STORE_MASK) { + uint32_t val = ef->a_reg[regno]; + uint32_t what = insn & STORE_MASK; + if (what == S8I_MATCH) { + spi_ramwrite(spi1, excvaddr & VM_OFFSET_MASK, 8-1, val); + } else if (what == S16I_MATCH) { + spi_ramwrite(spi1, excvaddr & VM_OFFSET_MASK, 16-1, val); + } else { + spi_ramwrite(spi1, excvaddr & VM_OFFSET_MASK, 32-1, val); + } + } else { + if (insn & L32_MASK) { + ef->a_reg[regno] = spi_ramread(spi1, excvaddr & VM_OFFSET_MASK, 32-1); + } else if (insn & L16_MASK) { + ef->a_reg[regno] = spi_ramread(spi1, excvaddr & VM_OFFSET_MASK, 16-1); + if ((insn & SIGNED_MASK ) && (ef->a_reg[regno] & 0x8000)) + ef->a_reg[regno] |= 0xffff0000; + } else { + ef->a_reg[regno] = spi_ramread(spi1, excvaddr & VM_OFFSET_MASK, 8-1); + } + } +} + +void install_vm_exception_handler() +{ + __old_handler = _xtos_set_exception_handler(EXCCAUSE_LOAD_PROHIBITED, loadstore_exception_handler); + _xtos_set_exception_handler(EXCCAUSE_STORE_PROHIBITED, loadstore_exception_handler); + + DECLARE_SPI1; + + // Manually reset chip from DIO to SIO mode (HW SPI has issues with <8 bits/clocks total output) + digitalWrite(cs, HIGH); + digitalWrite(mosi, HIGH); + digitalWrite(miso, HIGH); + digitalWrite(sck, LOW); + pinMode(cs, OUTPUT); + pinMode(miso, OUTPUT); + pinMode(mosi, OUTPUT); + pinMode(sck, OUTPUT); + digitalWrite(cs, LOW); + for (int i = 0; i < 4; i++) { + digitalWrite(sck, HIGH); + digitalWrite(sck, LOW); + } + digitalWrite(cs, HIGH); + + // Set up the SPI regs + spi_init(spi1); + + // Enable streaming read/write mode + spi1->spi_w[0] = 0x40; + spi_writetransaction(spi1, 0x01<<24, 8-1, 0, 8-1, sio); + + if (hspi_mode == dio) { + // Ramp up to DIO mode + spi_writetransaction(spi1, 0x3b<<24, 8-1, 0, 0, sio); + spi1->spi_ctrl |= SPICDIO | SPICFASTRD; + } + + // Bring cache structures to baseline + if (cache_ways > 0) { + for (auto i = 0; i < cache_ways; i++) { + __vm_cache_line[i].addr = -1; // Invalid, bits set in lower region so will never match + __vm_cache_line[i].next = &__vm_cache_line[i+1]; + } + __vm_cache = &__vm_cache_line[0]; + __vm_cache_line[cache_ways - 1].next = NULL; + } + + // Our umm_malloc configuration can only support a maximum of 256K RAM. A + // change would affect the block size of all heaps, and a larger block size + // would result in wasted space in the smaller heaps. + static_assert(MMU_EXTERNAL_HEAP <= 256, "Heap size must not exceed 256K"); + + // Hook into memory manager + umm_init_vm( (void *)0x10000000, MMU_EXTERNAL_HEAP * 1024); +} + + +}; + +#endif diff --git a/cores/esp8266/core_esp8266_vm.h b/cores/esp8266/core_esp8266_vm.h new file mode 100644 index 0000000000..94681a7eeb --- /dev/null +++ b/cores/esp8266/core_esp8266_vm.h @@ -0,0 +1,11 @@ +#ifdef __cplusplus +extern "C" { +#endif + +extern void install_vm_exception_handler(); + + +#ifdef __cplusplus +}; +#endif + diff --git a/cores/esp8266/core_esp8266_waveform.h b/cores/esp8266/core_esp8266_waveform.h index 4c5ce4fedb..d3d303f99f 100644 --- a/cores/esp8266/core_esp8266_waveform.h +++ b/cores/esp8266/core_esp8266_waveform.h @@ -1,7 +1,123 @@ -// Wrapper to include both versions of the waveform generator +/* + esp8266_waveform - General purpose waveform generation and control, + supporting outputs on all pins in parallel. + + -- Default, PWM locked version -- + Copyright (c) 2018 Earle F. Philhower, III. All rights reserved. + + The core idea is to have a programmable waveform generator with a unique + high and low period (defined in microseconds or CPU clock cycles). TIMER1 is + set to 1-shot mode and is always loaded with the time until the next edge + of any live waveforms. + + Up to one waveform generator per pin supported. + + Each waveform generator is synchronized to the ESP clock cycle counter, not the + timer. This allows for removing interrupt jitter and delay as the counter + always increments once per 80MHz clock. Changes to a waveform are + contiguous and only take effect on the next waveform transition, + allowing for smooth transitions. + + This replaces older tone(), analogWrite(), and the Servo classes. + + Everywhere in the code where "cycles" is used, it means ESP.getCycleCount() + clock cycle count, or an interval measured in CPU clock cycles, but not TIMER1 + cycles (which may be 2 CPU clock cycles @ 160MHz). + ---------- + + -- Phase locked version -- + Copyright (c) 2020 Dirk O. Kaar. + + The core idea is to have a programmable waveform generator with a unique + high and low period (defined in microseconds or CPU clock cycles). TIMER1 is + set to 1-shot mode and is always loaded with the time until the next edge + of any live waveforms. + + Up to one waveform generator per pin supported. + + Each waveform generator is synchronized to the ESP clock cycle counter, not the + timer. This allows for removing interrupt jitter and delay as the counter + always increments once per 80MHz clock. Changes to a waveform are + contiguous and only take effect on the next waveform transition, + allowing for smooth transitions. + + This replaces older tone(), analogWrite(), and the Servo classes. + + Everywhere in the code where "ccy" or "ccys" is used, it means ESP.getCycleCount() + clock cycle count, or an interval measured in CPU clock cycles, but not TIMER1 + cycles (which may be 2 CPU clock cycles @ 160MHz). + ---------- + + 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 + +#ifndef __ESP8266_WAVEFORM_H +#define __ESP8266_WAVEFORM_H + +#ifdef __cplusplus +extern "C" { +#endif + +// Start or change a waveform of the specified high and low times on specific pin. +// If runtimeUS > 0 then automatically stop it after that many usecs, relative to the next +// full period. +// If waveform is not yet started on pin, and on pin == alignPhase a waveform is running, +// the new waveform is started at phaseOffsetUS phase offset, in microseconds, to that. +// Setting autoPwm to true allows the wave generator to maintain PWM duty to idle cycle ratio +// under load, for applications where frequency or duty cycle must not change, leave false. +// Returns true or false on success or failure. +int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t runTimeUS = 0, + // Following parameters are ignored unless in PhaseLocked mode + int8_t alignPhase = -1, uint32_t phaseOffsetUS = 0, bool autoPwm = false); + +// Start or change a waveform of the specified high and low CPU clock cycles on specific pin. +// If runtimeCycles > 0 then automatically stop it after that many CPU clock cycles, relative to the next +// full period. +// If waveform is not yet started on pin, and on pin == alignPhase a waveform is running, +// the new waveform is started at phaseOffsetCcys phase offset, in CPU clock cycles, to that. +// Setting autoPwm to true allows the wave generator to maintain PWM duty to idle cycle ratio +// under load, for applications where frequency or duty cycle must not change, leave false. +// Returns true or false on success or failure. +int startWaveformClockCycles(uint8_t pin, uint32_t timeHighCcys, uint32_t timeLowCcys, uint32_t runTimeCcys = 0, + // Following parameters are ignored unless in PhaseLocked mode + int8_t alignPhase = -1, uint32_t phaseOffsetCcys = 0, bool autoPwm = false); + +// Stop a waveform, if any, on the specified pin. +// Returns true or false on success or failure. +int stopWaveform(uint8_t pin); + +// Add a callback function to be called on *EVERY* timer1 trigger. The +// callback must return the number of CPU clock cycles until the next desired call. +// However, since it is called every timer1 interrupt, it may be called +// again before this period. It should therefore use the ESP Cycle Counter +// to determine whether or not to perform an operation. +// Pass in NULL to disable the callback and, if no other waveforms being +// generated, stop the timer as well. +// Make sure the CB function has the IRAM_ATTR decorator. +void setTimer1Callback(uint32_t (*fn)()); + + +// Internal-only calls, not for applications +extern void _setPWMFreq(uint32_t freq); +extern bool _stopPWM(uint8_t pin); +extern bool _setPWM(int pin, uint32_t val, uint32_t range); + +#ifdef __cplusplus +} +#endif -#ifdef WAVEFORM_LOCKED_PHASE - #include "core_esp8266_waveform_phase.h" -#else - #include "core_esp8266_waveform_pwm.h" #endif diff --git a/cores/esp8266/core_esp8266_waveform_phase.cpp b/cores/esp8266/core_esp8266_waveform_phase.cpp index 3d8eb1a89a..4240ccb3c1 100644 --- a/cores/esp8266/core_esp8266_waveform_phase.cpp +++ b/cores/esp8266/core_esp8266_waveform_phase.cpp @@ -39,13 +39,26 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifdef WAVEFORM_LOCKED_PHASE - -#include "core_esp8266_waveform_phase.h" +#include "core_esp8266_waveform.h" #include +#include "debug.h" #include "ets_sys.h" #include + +extern "C" void enablePhaseLockedWaveform (void) +{ + // Does nothing, added to app to enable linking these versions + // of the waveform functions instead of the default. + DEBUGV("Enabling phase locked waveform generator\n"); +} + +// No-op calls to override the PWM implementation +extern "C" void _setPWMFreq_weak(uint32_t freq) { (void) freq; } +extern "C" IRAM_ATTR bool _stopPWM_weak(int pin) { (void) pin; return false; } +extern "C" bool _setPWM_weak(int pin, uint32_t val, uint32_t range) { (void) pin; (void) val; (void) range; return false; } + + // Timer is 80MHz fixed. 160MHz CPU frequency need scaling. constexpr bool ISCPUFREQ160MHZ = clockCyclesPerMicrosecond() == 160; // Maximum delay between IRQs, Timer1, <= 2^23 / 80MHz @@ -98,7 +111,7 @@ namespace { } // Interrupt on/off control -static ICACHE_RAM_ATTR void timer1Interrupt(); +static IRAM_ATTR void timer1Interrupt(); // Non-speed critical bits #pragma GCC optimize ("Os") @@ -112,7 +125,7 @@ static void initTimer() { timer1_write(IRQLATENCYCCYS); // Cause an interrupt post-haste } -static void ICACHE_RAM_ATTR deinitTimer() { +static void IRAM_ATTR deinitTimer() { ETS_FRC_TIMER1_NMI_INTR_ATTACH(NULL); timer1_disable(); timer1_isr_init(); @@ -122,7 +135,7 @@ static void ICACHE_RAM_ATTR deinitTimer() { extern "C" { // Set a callback. Pass in NULL to stop it -void setTimer1Callback(uint32_t (*fn)()) { +void setTimer1Callback_weak(uint32_t (*fn)()) { waveform.timer1CB = fn; std::atomic_thread_fence(std::memory_order_acq_rel); if (!waveform.timer1Running && fn) { @@ -132,17 +145,10 @@ void setTimer1Callback(uint32_t (*fn)()) { } } -int startWaveform(uint8_t pin, uint32_t highUS, uint32_t lowUS, - uint32_t runTimeUS, int8_t alignPhase, uint32_t phaseOffsetUS, bool autoPwm) { - return startWaveformClockCycles(pin, - microsecondsToClockCycles(highUS), microsecondsToClockCycles(lowUS), - microsecondsToClockCycles(runTimeUS), alignPhase, microsecondsToClockCycles(phaseOffsetUS), autoPwm); -} - // Start up a waveform on a pin, or change the current one. Will change to the new // waveform smoothly on next low->high transition. For immediate change, stopWaveform() // first, then it will immediately begin. -int startWaveformClockCycles(uint8_t pin, uint32_t highCcys, uint32_t lowCcys, +int startWaveformClockCycles_weak(uint8_t pin, uint32_t highCcys, uint32_t lowCcys, uint32_t runTimeCcys, int8_t alignPhase, uint32_t phaseOffsetCcys, bool autoPwm) { uint32_t periodCcys = highCcys + lowCcys; if (periodCcys < MAXIRQTICKSCCYS) { @@ -205,14 +211,14 @@ int startWaveformClockCycles(uint8_t pin, uint32_t highCcys, uint32_t lowCcys, } std::atomic_thread_fence(std::memory_order_acq_rel); while (waveform.toSetBits) { - delay(0); // Wait for waveform to update + esp_yield(); // Wait for waveform to update std::atomic_thread_fence(std::memory_order_acquire); } return true; } // Stops a waveform on a pin -int ICACHE_RAM_ATTR stopWaveform(uint8_t pin) { +IRAM_ATTR int stopWaveform_weak(uint8_t pin) { // Can't possibly need to stop anything if there is no timer active if (!waveform.timer1Running) { return false; @@ -246,7 +252,7 @@ int ICACHE_RAM_ATTR stopWaveform(uint8_t pin) { // For dynamic CPU clock frequency switch in loop the scaling logic would have to be adapted. // Using constexpr makes sure that the CPU clock frequency is compile-time fixed. -static inline ICACHE_RAM_ATTR int32_t scaleCcys(const int32_t ccys, const bool isCPU2X) { +static inline IRAM_ATTR int32_t scaleCcys(const int32_t ccys, const bool isCPU2X) { if (ISCPUFREQ160MHZ) { return isCPU2X ? ccys : (ccys >> 1); } @@ -255,7 +261,7 @@ static inline ICACHE_RAM_ATTR int32_t scaleCcys(const int32_t ccys, const bool i } } -static ICACHE_RAM_ATTR void timer1Interrupt() { +static IRAM_ATTR void timer1Interrupt() { const uint32_t isrStartCcy = ESP.getCycleCount(); int32_t clockDrift = isrStartCcy - waveform.nextEventCcy; const bool isCPU2X = CPU2X & 1; @@ -436,5 +442,3 @@ static ICACHE_RAM_ATTR void timer1Interrupt() { // Register access is fast and edge IRQ was configured before. T1L = nextEventCcys; } - -#endif // WAVEFORM_LOCKED_PHASE diff --git a/cores/esp8266/core_esp8266_waveform_phase.h b/cores/esp8266/core_esp8266_waveform_phase.h deleted file mode 100644 index dff8fe5020..0000000000 --- a/cores/esp8266/core_esp8266_waveform_phase.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - esp8266_waveform - General purpose waveform generation and control, - supporting outputs on all pins in parallel. - - Copyright (c) 2018 Earle F. Philhower, III. All rights reserved. - Copyright (c) 2020 Dirk O. Kaar. - - The core idea is to have a programmable waveform generator with a unique - high and low period (defined in microseconds or CPU clock cycles). TIMER1 is - set to 1-shot mode and is always loaded with the time until the next edge - of any live waveforms. - - Up to one waveform generator per pin supported. - - Each waveform generator is synchronized to the ESP clock cycle counter, not the - timer. This allows for removing interrupt jitter and delay as the counter - always increments once per 80MHz clock. Changes to a waveform are - contiguous and only take effect on the next waveform transition, - allowing for smooth transitions. - - This replaces older tone(), analogWrite(), and the Servo classes. - - Everywhere in the code where "ccy" or "ccys" is used, it means ESP.getCycleCount() - clock cycle count, or an interval measured in CPU clock cycles, but not TIMER1 - cycles (which may be 2 CPU clock cycles @ 160MHz). - - 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 -*/ - -#ifdef WAVEFORM_LOCKED_PHASE - -#include - -#ifndef __ESP8266_WAVEFORM_H -#define __ESP8266_WAVEFORM_H - -#ifdef __cplusplus -extern "C" { -#endif - -// Start or change a waveform of the specified high and low times on specific pin. -// If runtimeUS > 0 then automatically stop it after that many usecs, relative to the next -// full period. -// If waveform is not yet started on pin, and on pin == alignPhase a waveform is running, -// the new waveform is started at phaseOffsetUS phase offset, in microseconds, to that. -// Setting autoPwm to true allows the wave generator to maintain PWM duty to idle cycle ratio -// under load, for applications where frequency or duty cycle must not change, leave false. -// Returns true or false on success or failure. -int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, - uint32_t runTimeUS = 0, int8_t alignPhase = -1, uint32_t phaseOffsetUS = 0, bool autoPwm = false); -// Start or change a waveform of the specified high and low CPU clock cycles on specific pin. -// If runtimeCycles > 0 then automatically stop it after that many CPU clock cycles, relative to the next -// full period. -// If waveform is not yet started on pin, and on pin == alignPhase a waveform is running, -// the new waveform is started at phaseOffsetCcys phase offset, in CPU clock cycles, to that. -// Setting autoPwm to true allows the wave generator to maintain PWM duty to idle cycle ratio -// under load, for applications where frequency or duty cycle must not change, leave false. -// Returns true or false on success or failure. -int startWaveformClockCycles(uint8_t pin, uint32_t timeHighCcys, uint32_t timeLowCcys, - uint32_t runTimeCcys = 0, int8_t alignPhase = -1, uint32_t phaseOffsetCcys = 0, bool autoPwm = false); -// Stop a waveform, if any, on the specified pin. -// Returns true or false on success or failure. -int stopWaveform(uint8_t pin); - -// Add a callback function to be called on *EVERY* timer1 trigger. The -// callback must return the number of CPU clock cycles until the next desired call. -// However, since it is called every timer1 interrupt, it may be called -// again before this period. It should therefore use the ESP Cycle Counter -// to determine whether or not to perform an operation. -// Pass in NULL to disable the callback and, if no other waveforms being -// generated, stop the timer as well. -// Make sure the CB function has the ICACHE_RAM_ATTR decorator. -void setTimer1Callback(uint32_t (*fn)()); - -#ifdef __cplusplus -} -#endif - -#endif // __ESP8266_WAVEFORM_H - -#endif // WAVEFORM_LOCKED_PHASE diff --git a/cores/esp8266/core_esp8266_waveform_pwm.cpp b/cores/esp8266/core_esp8266_waveform_pwm.cpp index aea88e15b9..f85ccb76aa 100644 --- a/cores/esp8266/core_esp8266_waveform_pwm.cpp +++ b/cores/esp8266/core_esp8266_waveform_pwm.cpp @@ -38,12 +38,14 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef WAVEFORM_LOCKED_PHASE #include +#include #include "ets_sys.h" -#include "core_esp8266_waveform_pwm.h" +#include "core_esp8266_waveform.h" #include "user_interface.h" + + extern "C" { // Maximum delay between IRQs @@ -92,7 +94,7 @@ static WVFState wvfState; #pragma GCC optimize ("Os") // Interrupt on/off control -static ICACHE_RAM_ATTR void timer1Interrupt(); +static IRAM_ATTR void timer1Interrupt(); static bool timerRunning = false; static __attribute__((noinline)) void initTimer() { @@ -106,7 +108,7 @@ static __attribute__((noinline)) void initTimer() { } } -static ICACHE_RAM_ATTR void forceTimerInterrupt() { +static IRAM_ATTR void forceTimerInterrupt() { if (T1L > microsecondsToClockCycles(10)) { T1L = microsecondsToClockCycles(10); } @@ -143,7 +145,7 @@ static uint32_t _pwmPeriod = microsecondsToClockCycles(1000000UL) / _pwmFreq; // If there are no more scheduled activities, shut down Timer 1. // Otherwise, do nothing. -static ICACHE_RAM_ATTR void disableIdleTimer() { +static IRAM_ATTR void disableIdleTimer() { if (timerRunning && !wvfState.waveformEnabled && !pwmState.cnt && !wvfState.timer1CB) { ETS_FRC_TIMER1_NMI_INTR_ATTACH(NULL); timer1_disable(); @@ -154,14 +156,14 @@ static ICACHE_RAM_ATTR void disableIdleTimer() { // Notify the NMI that a new PWM state is available through the mailbox. // Wait for mailbox to be emptied (either busy or delay() as needed) -static ICACHE_RAM_ATTR void _notifyPWM(PWMState *p, bool idle) { +static IRAM_ATTR void _notifyPWM(PWMState *p, bool idle) { p->pwmUpdate = nullptr; pwmState.pwmUpdate = p; MEMBARRIER(); forceTimerInterrupt(); while (pwmState.pwmUpdate) { if (idle) { - delay(0); + esp_yield(); } MEMBARRIER(); } @@ -169,8 +171,10 @@ static ICACHE_RAM_ATTR void _notifyPWM(PWMState *p, bool idle) { static void _addPWMtoList(PWMState &p, int pin, uint32_t val, uint32_t range); + // Called when analogWriteFreq() changed to update the PWM total period -void _setPWMFreq(uint32_t freq) { +extern void _setPWMFreq_weak(uint32_t freq) __attribute__((weak)); +void _setPWMFreq_weak(uint32_t freq) { _pwmFreq = freq; // Convert frequency into clock cycles @@ -204,6 +208,11 @@ void _setPWMFreq(uint32_t freq) { disableIdleTimer(); } } +static void _setPWMFreq_bound(uint32_t freq) __attribute__((weakref("_setPWMFreq_weak"))); +void _setPWMFreq(uint32_t freq) { + _setPWMFreq_bound(freq); +} + // Helper routine to remove an entry from the state machine // and clean up any marked-off entries @@ -228,7 +237,8 @@ static void _cleanAndRemovePWM(PWMState *p, int pin) { // Disable PWM on a specific pin (i.e. when a digitalWrite or analogWrite(0%/100%)) -ICACHE_RAM_ATTR bool _stopPWM(int pin) { +extern bool _stopPWM_weak(uint8_t pin) __attribute__((weak)); +IRAM_ATTR bool _stopPWM_weak(uint8_t pin) { if (!((1<high transition. For immediate change, stopWaveform() // first, then it will immediately begin. -int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t runTimeUS) { - return startWaveformClockCycles(pin, microsecondsToClockCycles(timeHighUS), microsecondsToClockCycles(timeLowUS), microsecondsToClockCycles(runTimeUS)); -} - -int startWaveformClockCycles(uint8_t pin, uint32_t timeHighCycles, uint32_t timeLowCycles, uint32_t runTimeCycles) { - if ((pin > 16) || isFlashInterfacePin(pin)) { +extern int startWaveformClockCycles_weak(uint8_t pin, uint32_t timeHighCycles, uint32_t timeLowCycles, uint32_t runTimeCycles, int8_t alignPhase, uint32_t phaseOffsetUS, bool autoPwm) __attribute__((weak)); +int startWaveformClockCycles_weak(uint8_t pin, uint32_t timeHighCycles, uint32_t timeLowCycles, uint32_t runTimeCycles, + int8_t alignPhase, uint32_t phaseOffsetUS, bool autoPwm) { + (void) alignPhase; + (void) phaseOffsetUS; + (void) autoPwm; + + if ((pin > 16) || isFlashInterfacePin(pin) || (timeHighCycles == 0)) { return false; } Waveform *wave = &wvfState.waveform[pin]; @@ -352,8 +373,8 @@ int startWaveformClockCycles(uint8_t pin, uint32_t timeHighCycles, uint32_t time if (wvfState.waveformEnabled & mask) { // Make sure no waveform changes are waiting to be applied while (wvfState.waveformToChange) { - delay(0); // Wait for waveform to update - // No mem barrier here, the call to a global function implies global state updated + esp_yield(); // Wait for waveform to update + MEMBARRIER(); } wvfState.waveformNewHigh = timeHighCycles; wvfState.waveformNewLow = timeLowCycles; @@ -372,17 +393,30 @@ int startWaveformClockCycles(uint8_t pin, uint32_t timeHighCycles, uint32_t time initTimer(); forceTimerInterrupt(); while (wvfState.waveformToEnable) { - delay(0); // Wait for waveform to update - // No mem barrier here, the call to a global function implies global state updated + esp_yield(); // Wait for waveform to update + MEMBARRIER(); } } return true; } +static int startWaveformClockCycles_bound(uint8_t pin, uint32_t timeHighCycles, uint32_t timeLowCycles, uint32_t runTimeCycles, int8_t alignPhase, uint32_t phaseOffsetUS, bool autoPwm) __attribute__((weakref("startWaveformClockCycles_weak"))); +int startWaveformClockCycles(uint8_t pin, uint32_t timeHighCycles, uint32_t timeLowCycles, uint32_t runTimeCycles, int8_t alignPhase, uint32_t phaseOffsetUS, bool autoPwm) { + return startWaveformClockCycles_bound(pin, timeHighCycles, timeLowCycles, runTimeCycles, alignPhase, phaseOffsetUS, autoPwm); +} +// This version falls-thru to the proper startWaveformClockCycles call and is invariant across waveform generators +int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t runTimeUS, + int8_t alignPhase, uint32_t phaseOffsetUS, bool autoPwm) { + return startWaveformClockCycles_bound(pin, + microsecondsToClockCycles(timeHighUS), microsecondsToClockCycles(timeLowUS), + microsecondsToClockCycles(runTimeUS), alignPhase, microsecondsToClockCycles(phaseOffsetUS), autoPwm); +} + // Set a callback. Pass in NULL to stop it -void setTimer1Callback(uint32_t (*fn)()) { +extern void setTimer1Callback_weak(uint32_t (*fn)()) __attribute__((weak)); +void setTimer1Callback_weak(uint32_t (*fn)()) { wvfState.timer1CB = fn; if (fn) { initTimer(); @@ -390,9 +424,14 @@ void setTimer1Callback(uint32_t (*fn)()) { } disableIdleTimer(); } +static void setTimer1Callback_bound(uint32_t (*fn)()) __attribute__((weakref("setTimer1Callback_weak"))); +void setTimer1Callback(uint32_t (*fn)()) { + setTimer1Callback_bound(fn); +} // Stops a waveform on a pin -int ICACHE_RAM_ATTR stopWaveform(uint8_t pin) { +extern int stopWaveform_weak(uint8_t pin) __attribute__((weak)); +IRAM_ATTR int stopWaveform_weak(uint8_t pin) { // Can't possibly need to stop anything if there is no timer active if (!timerRunning) { return false; @@ -415,6 +454,10 @@ int ICACHE_RAM_ATTR stopWaveform(uint8_t pin) { disableIdleTimer(); return true; } +static int stopWaveform_bound(uint8_t pin) __attribute__((weakref("stopWaveform_weak"))); +IRAM_ATTR int stopWaveform(uint8_t pin) { + return stopWaveform_bound(pin); +} // Speed critical bits #pragma GCC optimize ("O2") @@ -422,14 +465,14 @@ int ICACHE_RAM_ATTR stopWaveform(uint8_t pin) { // Normally would not want two copies like this, but due to different // optimization levels the inline attribute gets lost if we try the // other version. -static inline ICACHE_RAM_ATTR uint32_t GetCycleCountIRQ() { +static inline IRAM_ATTR uint32_t GetCycleCountIRQ() { uint32_t ccount; __asm__ __volatile__("rsr %0,ccount":"=a"(ccount)); return ccount; } // Find the earliest cycle as compared to right now -static inline ICACHE_RAM_ATTR uint32_t earliest(uint32_t a, uint32_t b) { +static inline IRAM_ATTR uint32_t earliest(uint32_t a, uint32_t b) { uint32_t now = GetCycleCountIRQ(); int32_t da = a - now; int32_t db = b - now; @@ -454,7 +497,7 @@ static inline ICACHE_RAM_ATTR uint32_t earliest(uint32_t a, uint32_t b) { // When the time to the next edge is greater than this, RTI and set another IRQ to minimize CPU usage #define MINIRQTIME microsecondsToClockCycles(4) -static ICACHE_RAM_ATTR void timer1Interrupt() { +static IRAM_ATTR void timer1Interrupt() { // Flag if the core is at 160 MHz, for use by adjust() bool turbo = (*(uint32_t*)0x3FF00014) & 1 ? true : false; @@ -622,5 +665,3 @@ static ICACHE_RAM_ATTR void timer1Interrupt() { } }; - -#endif diff --git a/cores/esp8266/core_esp8266_waveform_pwm.h b/cores/esp8266/core_esp8266_waveform_pwm.h deleted file mode 100644 index 3d66bc1413..0000000000 --- a/cores/esp8266/core_esp8266_waveform_pwm.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - esp8266_waveform - General purpose waveform generation and control, - supporting outputs on all pins in parallel. - - Copyright (c) 2018 Earle F. Philhower, III. All rights reserved. - - The core idea is to have a programmable waveform generator with a unique - high and low period (defined in microseconds or CPU clock cycles). TIMER1 is - set to 1-shot mode and is always loaded with the time until the next edge - of any live waveforms. - - Up to one waveform generator per pin supported. - - Each waveform generator is synchronized to the ESP clock cycle counter, not the - timer. This allows for removing interrupt jitter and delay as the counter - always increments once per 80MHz clock. Changes to a waveform are - contiguous and only take effect on the next waveform transition, - allowing for smooth transitions. - - This replaces older tone(), analogWrite(), and the Servo classes. - - Everywhere in the code where "cycles" is used, it means ESP.getCycleCount() - clock cycle count, or an interval measured in CPU clock cycles, but not TIMER1 - cycles (which may be 2 CPU clock cycles @ 160MHz). - - 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 WAVEFORM_LOCKED_PHASE - -#include - -#ifndef __ESP8266_WAVEFORM_H -#define __ESP8266_WAVEFORM_H - -#ifdef __cplusplus -extern "C" { -#endif - -// Start or change a waveform of the specified high and low times on specific pin. -// If runtimeUS > 0 then automatically stop it after that many usecs. -// Returns true or false on success or failure. -int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t runTimeUS); -// Start or change a waveform of the specified high and low CPU clock cycles on specific pin. -// If runtimeCycles > 0 then automatically stop it after that many CPU clock cycles. -// Returns true or false on success or failure. -int startWaveformClockCycles(uint8_t pin, uint32_t timeHighCycles, uint32_t timeLowCycles, uint32_t runTimeCycles); -// Stop a waveform, if any, on the specified pin. -// Returns true or false on success or failure. -int stopWaveform(uint8_t pin); - -// Add a callback function to be called on *EVERY* timer1 trigger. The -// callback must return the number of CPU clock cycles until the next desired call. -// However, since it is called every timer1 interrupt, it may be called -// again before this period. It should therefore use the ESP Cycle Counter -// to determine whether or not to perform an operation. -// Pass in NULL to disable the callback and, if no other waveforms being -// generated, stop the timer as well. -// Make sure the CB function has the ICACHE_RAM_ATTR decorator. -void setTimer1Callback(uint32_t (*fn)()); - - - -// Internal-only calls, not for applications -extern void _setPWMFreq(uint32_t freq); -extern bool _stopPWM(int pin); -extern bool _setPWM(int pin, uint32_t val, uint32_t range); - -#ifdef __cplusplus -} -#endif - -#endif - -#endif diff --git a/cores/esp8266/core_esp8266_wiring.cpp b/cores/esp8266/core_esp8266_wiring.cpp index 64b80aed10..3054d39338 100644 --- a/cores/esp8266/core_esp8266_wiring.cpp +++ b/cores/esp8266/core_esp8266_wiring.cpp @@ -23,37 +23,20 @@ #include "ets_sys.h" #include "osapi.h" #include "user_interface.h" -#include "cont.h" +#include "coredecls.h" extern "C" { -extern void ets_delay_us(uint32_t us); -extern void esp_schedule(); -extern void esp_yield(); - -static os_timer_t delay_timer; static os_timer_t micros_overflow_timer; static uint32_t micros_at_last_overflow_tick = 0; static uint32_t micros_overflow_count = 0; #define ONCE 0 #define REPEAT 1 -void delay_end(void* arg) { - (void) arg; - esp_schedule(); -} - void __delay(unsigned long ms) { - if(ms) { - os_timer_setfn(&delay_timer, (os_timer_func_t*) &delay_end, 0); - os_timer_arm(&delay_timer, ms, ONCE); - } else { - esp_schedule(); - } - esp_yield(); - if(ms) { - os_timer_disarm(&delay_timer); - } + // 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"))); @@ -69,8 +52,8 @@ void micros_overflow_tick(void* arg) { //--------------------------------------------------------------------------- // millis() 'magic multiplier' approximation // -// This function corrects the cumlative (296us / usec overflow) drift -// seen in the orignal 'millis()' function. +// This function corrects the cumulative (296us / usec overflow) drift +// seen in the original 'millis()' function. // // Input: // 'm' - 32-bit usec counter, 0 <= m <= 0xFFFFFFFF @@ -149,7 +132,7 @@ void micros_overflow_tick(void* arg) { // // Reference function: corrected millis(), 64-bit arithmetic, // truncated to 32-bits by return -// unsigned long ICACHE_RAM_ATTR millis_corr_DEBUG( void ) +// unsigned long IRAM_ATTR millis_corr_DEBUG( void ) // { // // Get usec system time, usec overflow conter // ...... @@ -163,7 +146,7 @@ void micros_overflow_tick(void* arg) { #define MAGIC_1E3_wLO 0x4bc6a7f0 // LS part #define MAGIC_1E3_wHI 0x00418937 // MS part, magic multiplier -unsigned long ICACHE_RAM_ATTR millis() +unsigned long IRAM_ATTR millis() { union { uint64_t q; // Accumulator, 64-bit, little endian @@ -194,18 +177,18 @@ unsigned long ICACHE_RAM_ATTR millis() } //millis -unsigned long ICACHE_RAM_ATTR micros() { +unsigned long IRAM_ATTR micros() { return system_get_time(); } -uint64_t ICACHE_RAM_ATTR micros64() { +uint64_t IRAM_ATTR micros64() { uint32_t low32_us = system_get_time(); uint32_t high32_us = micros_overflow_count + ((low32_us < micros_at_last_overflow_tick) ? 1 : 0); uint64_t duration64_us = (uint64_t)high32_us << 32 | low32_us; return duration64_us; } -void ICACHE_RAM_ATTR delayMicroseconds(unsigned int us) { +void IRAM_ATTR delayMicroseconds(unsigned int us) { os_delay_us(us); } diff --git a/cores/esp8266/core_esp8266_wiring_analog.cpp b/cores/esp8266/core_esp8266_wiring_analog.cpp index 27c98ff3f0..fe8d67e66e 100644 --- a/cores/esp8266/core_esp8266_wiring_analog.cpp +++ b/cores/esp8266/core_esp8266_wiring_analog.cpp @@ -39,4 +39,16 @@ extern int __analogRead(uint8_t pin) extern int analogRead(uint8_t pin) __attribute__ ((weak, alias("__analogRead"))); + +void __analogReference(uint8_t mode) +{ + // Only DEFAULT is supported on the ESP8266 + if (mode != DEFAULT) { + DEBUGV("analogReference called with illegal mode"); + } +} + + +extern void analogReference(uint8_t mode) __attribute__ ((weak, alias("__analogReference"))); + }; diff --git a/cores/esp8266/core_esp8266_wiring_digital.cpp b/cores/esp8266/core_esp8266_wiring_digital.cpp index 539f5448b7..6b42c3ee36 100644 --- a/cores/esp8266/core_esp8266_wiring_digital.cpp +++ b/cores/esp8266/core_esp8266_wiring_digital.cpp @@ -81,11 +81,9 @@ extern void __pinMode(uint8_t pin, uint8_t mode) { } } -extern void ICACHE_RAM_ATTR __digitalWrite(uint8_t pin, uint8_t val) { - stopWaveform(pin); // Disable any tone -#ifndef WAVEFORM_LOCKED_PHASE - _stopPWM(pin); // ...and any analogWrite -#endif +extern void IRAM_ATTR __digitalWrite(uint8_t pin, uint8_t val) { + stopWaveform(pin); // Disable any Tone or startWaveform on this pin + _stopPWM(pin); // and any analogWrites (PWM) if(pin < 16){ if(val) GPOS = (1 << pin); else GPOC = (1 << pin); @@ -95,7 +93,7 @@ extern void ICACHE_RAM_ATTR __digitalWrite(uint8_t pin, uint8_t val) { } } -extern int ICACHE_RAM_ATTR __digitalRead(uint8_t pin) { +extern int IRAM_ATTR __digitalRead(uint8_t pin) { if(pin < 16){ return GPIP(pin); } else if(pin == 16){ @@ -133,7 +131,7 @@ typedef struct { static interrupt_handler_t interrupt_handlers[16] = { {0, 0, 0, 0}, }; static uint32_t interrupt_reg = 0; -void ICACHE_RAM_ATTR interrupt_handler(void *arg, void *frame) +void IRAM_ATTR interrupt_handler(void *arg, void *frame) { (void) arg; (void) frame; @@ -220,7 +218,7 @@ extern void __attachInterruptArg(uint8_t pin, voidFuncPtrArg userFunc, void* arg __attachInterruptFunctionalArg(pin, userFunc, arg, mode, false); } -extern void ICACHE_RAM_ATTR __detachInterrupt(uint8_t pin) { +extern void IRAM_ATTR __detachInterrupt(uint8_t pin) { if (pin < 16) { ETS_GPIO_INTR_DISABLE(); diff --git a/cores/esp8266/core_esp8266_wiring_pwm.cpp b/cores/esp8266/core_esp8266_wiring_pwm.cpp index a456c965c8..5bd5416d70 100644 --- a/cores/esp8266/core_esp8266_wiring_pwm.cpp +++ b/cores/esp8266/core_esp8266_wiring_pwm.cpp @@ -28,7 +28,6 @@ extern "C" { static int32_t analogScale = 255; // Match upstream default, breaking change from 2.x.x -#ifdef WAVEFORM_LOCKED_PHASE static uint32_t analogMap = 0; static uint16_t analogFreq = 1000; @@ -41,9 +40,14 @@ extern void __analogWriteFreq(uint32_t freq) { } else { analogFreq = freq; } + _setPWMFreq(freq); } extern void __analogWrite(uint8_t pin, int val) { + analogWriteMode(pin, val, false); +} + +extern void __analogWriteMode(uint8_t pin, int val, bool openDrain) { if (pin > 16) { return; } @@ -62,51 +66,23 @@ extern void __analogWrite(uint8_t pin, int val) { analogMap &= ~(1 << pin); } else { - pinMode(pin, OUTPUT); + if(openDrain) { + pinMode(pin, OUTPUT_OPEN_DRAIN); + } else { + pinMode(pin, OUTPUT); + } } uint32_t high = (analogPeriod * val) / analogScale; uint32_t low = analogPeriod - high; // Find the first GPIO being generated by checking GCC's find-first-set (returns 1 + the bit of the first 1 in an int32_t) int phaseReference = __builtin_ffs(analogMap) - 1; - if (startWaveformClockCycles(pin, high, low, 0, phaseReference, 0, true)) { + if (_setPWM(pin, val, analogScale)) { + analogMap |= (1 << pin); + } else if (startWaveformClockCycles(pin, high, low, 0, phaseReference, 0, true)) { analogMap |= (1 << pin); } } -#else // !WAVEFORM_LOCKED_PHASE - -extern void __analogWriteFreq(uint32_t freq) { - if (freq < 100) { - freq = 100; - } else if (freq > 60000) { - freq = 60000; - } else { - freq = freq; - } - _setPWMFreq(freq); -} - -extern void __analogWrite(uint8_t pin, int val) { - if (pin > 16) { - return; - } - - if (val < 0) { - val = 0; - } else if (val > analogScale) { - val = analogScale; - } - - // Per the Arduino docs at https://www.arduino.cc/reference/en/language/functions/analog-io/analogwrite/ - // val: the duty cycle: between 0 (always off) and 255 (always on). - // So if val = 0 we have digitalWrite(LOW), if we have val==range we have digitalWrite(HIGH) - pinMode(pin, OUTPUT); - _setPWM(pin, val, analogScale); -} - -#endif // WAVEFORM_LOCKED_PHASE - - extern void __analogWriteRange(uint32_t range) { if ((range >= 15) && (range <= 65535)) { analogScale = range; @@ -120,6 +96,7 @@ extern void __analogWriteResolution(int res) { } extern void analogWrite(uint8_t pin, int val) __attribute__((weak, alias("__analogWrite"))); +extern void analogWriteMode(uint8_t pin, int val, bool openDrain) __attribute__((weak, alias("__analogWriteMode"))); extern void analogWriteFreq(uint32_t freq) __attribute__((weak, alias("__analogWriteFreq"))); extern void analogWriteRange(uint32_t range) __attribute__((weak, alias("__analogWriteRange"))); extern void analogWriteResolution(int res) __attribute__((weak, alias("__analogWriteResolution"))); diff --git a/cores/esp8266/core_version.h b/cores/esp8266/core_version.h index 04c95c80c5..3c9d4d54b8 100644 --- a/cores/esp8266/core_version.h +++ b/cores/esp8266/core_version.h @@ -6,6 +6,18 @@ #define ARDUINO_ESP8266_GIT_DESC unspecified #endif +#ifndef ARDUINO_ESP8266_MAJOR +#define ARDUINO_ESP8266_MAJOR 0 +#endif + +#ifndef ARDUINO_ESP8266_MINOR +#define ARDUINO_ESP8266_MINOR 0 +#endif + +#ifndef ARDUINO_ESP8266_REVISION +#define ARDUINO_ESP8266_REVISION 0 +#endif + // ARDUINO_ESP8266_RELEASE is defined for released versions as a string containing the version name, i.e. "2_3_0_RC1" // ARDUINO_ESP8266_RELEASE is used in the core internally. Please use ESP.getCoreVersion() function instead. diff --git a/cores/esp8266/coredecls.h b/cores/esp8266/coredecls.h index 7186662f0f..73af6d8cf1 100644 --- a/cores/esp8266/coredecls.h +++ b/cores/esp8266/coredecls.h @@ -1,6 +1,7 @@ -#ifndef __COREDECLS_H -#define __COREDECLS_H +#pragma once + +#include "core_esp8266_features.h" #ifdef __cplusplus extern "C" { @@ -13,27 +14,68 @@ extern "C" { #include // g_pcont declaration bool can_yield(); -void esp_yield(); +void esp_suspend(); +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; using TrivialCB = std::function; +void settimeofday_cb (BoolCB&& cb); void settimeofday_cb (const BoolCB& cb); void settimeofday_cb (const TrivialCB& cb); -#endif +// This overload of esp_suspend() performs the blocked callback whenever it is resumed, +// and if that returns true, it immediately suspends again. +template +inline void esp_suspend(T&& blocked) { + do { + esp_suspend(); + } while (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 +// 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 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(); + while (!esp_try_delay(start_ms, timeout_ms, intvl_ms) && blocked()) { + } +} + +// This overload of esp_delay() delays for a duration of at most timeout_ms milliseconds. +// Whenever it is resumed, 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) { + esp_delay(timeout_ms, std::forward(blocked), timeout_ms); +} -#endif // __COREDECLS_H +#endif // __cplusplus 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.cpp b/cores/esp8266/debug.cpp index afaf5553e8..327aace64e 100644 --- a/cores/esp8266/debug.cpp +++ b/cores/esp8266/debug.cpp @@ -1,37 +1,60 @@ /* - debug.cpp - debug helper functions - Copyright (c) 2015 Markus Sattler. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + debug.cpp - debug helper functions + Copyright (c) 2015 Markus Sattler. 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 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. + 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 - */ + 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 "Arduino.h" #include "debug.h" #include "osapi.h" -void ICACHE_RAM_ATTR hexdump(const void *mem, uint32_t len, uint8_t cols) { - const uint8_t* src = (const uint8_t*) mem; - os_printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len); - for(uint32_t i = 0; i < len; i++) { - if(i % cols == 0) { - os_printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i); - yield(); +#ifdef DEBUG_ESP_CORE +void __iamslow(const char* what) +{ + DEBUGV("%s should be overridden for better efficiency\r\n", what); +} +#endif + +IRAM_ATTR +void hexdump(const void* mem, uint32_t len, uint8_t cols) +{ + const char* src = (const char*)mem; + os_printf("\n[HEXDUMP] Address: %p len: 0x%X (%d)", src, len, len); + while (len > 0) + { + uint32_t linesize = cols > len ? len : cols; + os_printf("\n[%p] 0x%04x: ", src, (int)(src - (const char*)mem)); + for (uint32_t i = 0; i < linesize; i++) + { + os_printf("%02x ", *(src + i)); + } + os_printf(" "); + for (uint32_t i = linesize; i < cols; i++) + { + os_printf(" "); + } + for (uint32_t i = 0; i < linesize; i++) + { + unsigned char c = *(src + i); + os_putc(isprint(c) ? c : '.'); } - os_printf("%02X ", *src); - src++; + src += linesize; + len -= linesize; + optimistic_yield(10000); } os_printf("\n"); } diff --git a/cores/esp8266/debug.h b/cores/esp8266/debug.h index c48b79a14c..437479748c 100644 --- a/cores/esp8266/debug.h +++ b/cores/esp8266/debug.h @@ -4,31 +4,63 @@ #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 DEBUGV(fmt, ...) ::printf((PGM_P)PSTR(fmt), ##__VA_ARGS__) +#define DEBUG_LEAF_FUNCTION _DEBUG_LEAF_FUNCTION +#else +#define DEBUG_LEAF_FUNCTION(...) #endif #ifndef DEBUGV -#define DEBUGV(...) do { (void)0; } while (0) +#define DEBUGV(...) \ + do \ + { \ + (void)0; \ + } while (0) #endif #ifdef __cplusplus -void hexdump(const void *mem, uint32_t len, uint8_t cols = 16); +extern "C" void hexdump(const void* mem, uint32_t len, uint8_t cols = 16); #else -void hexdump(const void *mem, uint32_t len, uint8_t cols); +void hexdump(const void* mem, uint32_t len, uint8_t cols); #endif #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif - -void __unhandled_exception(const char *str) __attribute__((noreturn)); -void __panic_func(const char* file, int line, const char* func) __attribute__((noreturn)); + 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__) +#ifdef DEBUG_ESP_CORE + extern void __iamslow(const char* what); +#define IAMSLOW() \ + do \ + { \ + static bool once = false; \ + if (!once) \ + { \ + once = true; \ + __iamslow((PGM_P)FPSTR(__FUNCTION__)); \ + } \ + } while (0) +#else +#define IAMSLOW() \ + do \ + { \ + (void)0; \ + } while (0) +#endif + #ifdef __cplusplus } #endif - -#endif//ARD_DEBUG_H +#endif // ARD_DEBUG_H diff --git a/cores/esp8266/esp8266_peri.h b/cores/esp8266/esp8266_peri.h index 6ed20dfffd..29a15744b0 100644 --- a/cores/esp8266/esp8266_peri.h +++ b/cores/esp8266/esp8266_peri.h @@ -163,7 +163,7 @@ extern volatile uint32_t* const esp8266_gpioToFn[16]; #define TCIS 8 //Interrupt Status #define TCTE 7 //Timer Enable #define TCAR 6 //AutoReload (restart timer when condition is reached) -#define TCPD 2 //Prescale Devider (2bit) 0:1(12.5ns/tick), 1:16(0.2us/tick), 2/3:256(3.2us/tick) +#define TCPD 2 //Prescale Divider (2bit) 0:1(12.5ns/tick), 1:16(0.2us/tick), 2/3:256(3.2us/tick) #define TCIT 0 //Interrupt Type 0:edge, 1:level //RTC Registers @@ -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< +#include +#include + +#define PERIPHS_DPORT_18 (PERIPHS_DPORT_BASEADDR + 0x018) +#define PERIPHS_DPORT_ICACHE_ENABLE (PERIPHS_DPORT_BASEADDR + 0x024) +/* When enabled 16K IRAM starting at 0x4010C000 is unmapped */ +#define ICACHE_ENABLE_FIRST_16K BIT3 +/* When enabled 16K IRAM starting at 0x40108000 is unmapped */ +#define ICACHE_ENABLE_SECOND_16K BIT4 +#define PERIPHS_HW_WDT (0x60000900) +#define PERIPHS_I2C_48 (0x60000a00 + 0x348) + + +extern void (*user_start_fptr)(); #ifndef XCHAL_EXCCAUSE_NUM // from tools/xtensa-lx106-elf/include/xtensa/config/core.h:629:#define XCHAL_EXCCAUSE_NUM 64 @@ -19,6 +34,12 @@ extern int rom_i2c_readReg_Mask(int, int, int, int, int); extern int uart_baudrate_detect(int, int); +/* SDK/Flash contains also an implementation of this function + * but for reboot into UART download mode the version from ROM + * has to be used because flash is not accessible. + */ +extern void rom_uart_div_modify(uint8 uart_no, uint32 DivLatchValue); + /* ROM function, uart_buff_switch(), is used to switch printing between UART0 and UART1. It updates a structure that only controls a select group of print @@ -31,15 +52,29 @@ calls to ets_install_putc1(). */ extern void uart_buff_switch(uint8_t); +/* + ROM function, ets_install_uart_printf, is used to installs the internal ROM + putc1 driver used to print on UART0 or UART1. The installed driver is use by ets_printf. + Side note, ets_install_uart_printf just happens to return the address of the + internal putc1 driver installed. +*/ +extern void ets_install_uart_printf(void); + /* ROM function, ets_uart_printf(), prints on the UART selected by uart_buff_switch(). Supported format options are the same as vprintf(). Also has cooked newline behavior. No flash format/string support; however, ISR safe. - Also, uses a static function in ROM to print characters which is only - controlled by uart_buff_switch(). + It also uses a static function in ROM to print characters. The UART selection + is handled by a prior call to uart_buff_switch(). An advantage over ets_printf, + this call is not affected by calls made to ets_install_putc1 or + ets_install_putc2. */ extern int ets_uart_printf(const char *format, ...) __attribute__ ((format (printf, 1, 2))); +extern void user_uart_wait_tx_fifo_empty(uint32_t ch, uint32_t arg2); +extern void uartAttach(); +extern void Uart_Init(uint32_t uart_no); + extern void ets_delay_us(uint32_t us); #ifndef GDBSTUB_H @@ -160,7 +195,7 @@ typedef void (*fn_c_exception_handler_t)(struct __exception_frame *ef, int cause _xtos_c_handler_table[]. It is present when an exception handler has not been registered. It simply consist of a single instruction, `ret`. It is also internally used by `_xtos_set_exception_handler(cause, NULL)` to - reset a "C" exception handler back to the unhandled state. The coresponding + reset a "C" exception handler back to the unhandled state. The corresponding `_xtos_exc_handler_table` entry will be set to `_xtos_unhandled_exception`. Note, if nesting handlers is desired this must be implemented in the new "C" exception handler(s) being registered. @@ -206,6 +241,26 @@ extern fn_c_exception_handler_t _xtos_c_handler_table[XCHAL_EXCCAUSE_NUM]; extern fn_c_exception_handler_t _xtos_set_exception_handler(int cause, fn_c_exception_handler_t fn); #endif +/* + BootROM function that sends the SPI Flash "Write Enable" command, 0x06. + The function internally calls Wait_SPI_Idle before enabling. + Polls status register forever waiting for WEL bit to set. + This function always returns 0; however, most examples test for 0. + + Every function I find that needs WEL set, call this function. I suspect the + waiting for the WEL bit to set is a Flash chip anomaly workaround. +*/ +extern SpiFlashOpResult SPI_write_enable(SpiFlashChip *fc); + +extern uint32_t Wait_SPI_Idle(SpiFlashChip *fc); +extern void Cache_Read_Disable(); +extern int32_t system_func1(uint32_t); +extern void clockgate_watchdog(uint32_t); +extern void pm_open_rf(); +extern void UartDwnLdProc(uint8_t* ram_addr, uint32_t size, void (**user_start_ptr)()); +extern int boot_from_flash(); +extern void ets_run() __attribute__((noreturn)); + #ifdef __cplusplus }; #endif diff --git a/cores/esp8266/esp_priv.h b/cores/esp8266/esp_priv.h new file mode 100644 index 0000000000..6e11208bd1 --- /dev/null +++ b/cores/esp8266/esp_priv.h @@ -0,0 +1,43 @@ +/* + esp_priv.h - private esp8266 helpers + Copyright (c) 2020 esp8266/Arduino community. All right reserved. + + 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 __ESP_PRIV +#define __ESP_PRIV + +#if defined(CORE_MOCK) + +constexpr bool __byteAddressable(const void*) +{ + return true; +} + +#else // on hardware + +#include + +// returns true when addr can be used without "pgm_" functions or non32xfer service +inline bool __byteAddressable(const void* addr) +{ + return addr < (const void*)(XCHAL_DATARAM0_VADDR + XCHAL_DATARAM0_SIZE); +} + +#endif // on hardware + +#endif // __ESP_PRIV diff --git a/cores/esp8266/exc-c-wrapper-handler.S b/cores/esp8266/exc-c-wrapper-handler.S index f1ec1c391e..e1c3f1e15a 100644 --- a/cores/esp8266/exc-c-wrapper-handler.S +++ b/cores/esp8266/exc-c-wrapper-handler.S @@ -168,9 +168,12 @@ _xtos_c_wrapper_handler: rsync // wait for WSR to PS to complete rsr a12, SAR -// @mhightower83 - I think after the next instruction we have the potential of -// loosing UEXC_excvaddr Which the earlier comment said we need to preserve for -// the exception handler. +// @mhightower83 - I think, after the next instruction, we have the potential of +// losing UEXC_excvaddr. Which the earlier comment said we need to preserve for +// the exception handler. We keep interrupts off when calling the "C" exception +// handler. For the use cases that I am looking at, this is a must. If there are +// future use cases that need interrupts enabled, those "C" exception handlers +// can turn them on. // // rsil a13, 0 @@ -189,8 +192,11 @@ _xtos_c_wrapper_handler: // Restore special registers l32i a14, a1, UEXC_sar -// For compatibility with Arduino interrupt architecture, we keep interrupts 100% -// disabled. + // load early - saves two cycles - @mhightower83 + movi a0, _xtos_return_from_exc + +// @mhightower83 - For compatibility with Arduino interrupt architecture, we +// keep interrupts 100% disabled. // /* // * Disable interrupts while returning from the pseudo-CALL setup above, // * for the same reason they were disabled while doing the pseudo-CALL: @@ -201,7 +207,6 @@ _xtos_c_wrapper_handler: // rsil a12, 1 // XCHAL_EXCM_LEVEL rsil a12, 15 // All levels blocked. wsr a14, SAR - movi a0, _xtos_return_from_exc jx a0 /* FIXME: what about _GeneralException ? */ diff --git a/cores/esp8266/exc-sethandler.cpp b/cores/esp8266/exc-sethandler.cpp new file mode 100644 index 0000000000..64a45b0307 --- /dev/null +++ b/cores/esp8266/exc-sethandler.cpp @@ -0,0 +1,115 @@ +/* + * Adaptation of _xtos_set_exception_handler for Arduino ESP8266 core + * + * This replacement for the Boot ROM `_xtos_set_exception_handler` is used to + * install our replacement `_xtos_c_wrapper_handler`. This change protects the + * value of `excvaddr` from corruption. + * + * + * Details + * + * The issue, the Boot ROM "C" wrapper for exception handlers, + * `_xtos_c_wrapper_handler`, turns interrupts back on. This leaves `excvaddr` + * exposed to possible overwrite before it is read. For example, if an interrupt + * is taken during the exception handler processing and the ISR handler + * generates a new exception, the original value of `excvaddr` is lost. To + * address this issue we have a replacement `_xtos_c_wrapper_handler` in file + * `exc-c-wrapper-handler.S`. + * + * An overview, of an exception at entry: New interrupts are blocked by EXCM + * being set. Once cleared, interrupts above the current INTLEVEL and exceptions + * (w/o creating a DoubleException) can occur. + * + * Using our replacement for `_xtos_c_wrapper_handler`, INTLEVEL is raised to 15 + * with EXCM cleared. + * + * The original Boot ROM `_xtos_c_wrapper_handler` at entry would set INTLEVEL + * to 3 with EXCM cleared, save registers, then do a `rsil 0` (interrupts fully + * enabled!) just before calling the registered "C" Exception handler. Our + * replacement keeps INTLEVEL at 15. This is needed to support the Arduino model + * of interrupts disabled while an ISR runs. + * + * And we also need it for umm_malloc to work safely with an IRAM heap from an + * ISR call. While malloc() will supply DRAM for all allocation from an ISR, we + * want free() to safely operate from an ISR to avoid a leak potential. + * + * If an exception handler needs interrupts enabled, it would be done after it + * has consumed the value of `excvaddr`. Whether such action is safe is left to + * the exception handler writer to determine. However, with our current + * 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) || (NONOSDK >= (0x30000)) + +/* + * The original module source code came from: + * https://github.com/qca/open-ath9k-htc-firmware/blob/master/sboot/magpie_1_1/sboot/athos/src/xtos/exc-sethandler.c + * + * It has been revised to use Arduino ESP8266 core includes, types, and + * formatting. +*/ + +/* exc-sethandler.c - register an exception handler in XTOS */ + +/* + * Copyright (c) 1999-2006 Tensilica Inc. + * + * 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. + * + * 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. + */ + +#include +#include "esp8266_undocumented.h" + +extern "C" { + +/* + * Register a C handler for the specified general exception + * (specified EXCCAUSE value). + */ +fn_c_exception_handler_t _xtos_set_exception_handler(int cause, fn_c_exception_handler_t fn) +{ + fn_c_exception_handler_t ret; + + if( (unsigned) cause >= XCHAL_EXCCAUSE_NUM ) + return 0; + + if( fn == 0 ) + fn = &_xtos_p_none; + + ret = _xtos_c_handler_table[cause]; + + _xtos_exc_handler_table[cause] = ( (fn == &_xtos_p_none) + ? &_xtos_unhandled_exception + : &_xtos_c_wrapper_handler ); + + _xtos_c_handler_table[cause] = fn; + + if( ret == &_xtos_p_none ) + ret = 0; + + return ret; +} + +}; + +#endif diff --git a/cores/esp8266/flash_hal.cpp b/cores/esp8266/flash_hal.cpp index 986d3745d6..87b34830fa 100644 --- a/cores/esp8266/flash_hal.cpp +++ b/cores/esp8266/flash_hal.cpp @@ -68,3 +68,13 @@ int32_t flash_hal_erase(uint32_t addr, uint32_t size) { } return FLASH_HAL_OK; } + +#if FLASH_MAP_SUPPORT + +// default weak configuration: +FLASH_MAP_SETUP_CONFIG_ATTR(__attribute__((weak)), FLASH_MAP_OTA_FS) + +// can be overridden by user with: +//FLASH_MAP_SETUP_CONFIG(FLASH_MAP_some_configuration) + +#endif diff --git a/cores/esp8266/flash_hal.h b/cores/esp8266/flash_hal.h index 13219bb18f..061b1d0b08 100644 --- a/cores/esp8266/flash_hal.h +++ b/cores/esp8266/flash_hal.h @@ -24,18 +24,63 @@ License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifdef ARDUINO -extern "C" uint32_t _FS_start; -extern "C" uint32_t _FS_end; -extern "C" uint32_t _FS_page; -extern "C" uint32_t _FS_block; - -#define FS_PHYS_ADDR ((uint32_t) (&_FS_start) - 0x40200000) -#define FS_PHYS_SIZE ((uint32_t) (&_FS_end) - (uint32_t) (&_FS_start)) -#define FS_PHYS_PAGE ((uint32_t) &_FS_page) -#define FS_PHYS_BLOCK ((uint32_t) &_FS_block) + +#ifdef __cplusplus +extern "C" { #endif +#if FLASH_MAP_SUPPORT +#include + +extern uint32_t spi_flash_get_id (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...) \ + 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 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) +#define FS_start (__flashdesc[__flashindex].fs_start) +#define FS_end (__flashdesc[__flashindex].fs_end) +#define FS_block (__flashdesc[__flashindex].fs_block_size) +#define FS_page (__flashdesc[__flashindex].fs_page_size) + +#else // !FLASH_MAP_SUPPORT + +extern uint32_t _FS_start; +extern uint32_t _FS_end; +extern uint32_t _FS_page; +extern uint32_t _FS_block; +extern uint32_t _EEPROM_start; +#define EEPROM_start ((uint32_t)&_EEPROM_start) +#define FS_start ((uint32_t)&_FS_start) +#define FS_end ((uint32_t)&_FS_end) +#define FS_page ((uint32_t)&_FS_page) +#define FS_block ((uint32_t)&_FS_block) + +#endif // FLASH_MAP_SUPPORT + +#define FS_PHYS_ADDR ((uint32_t)FS_start - 0x40200000) +#define FS_PHYS_SIZE ((uint32_t)(FS_end - FS_start)) +#define FS_PHYS_PAGE ((uint32_t)FS_page) +#define FS_PHYS_BLOCK ((uint32_t)FS_block) + // Return values of the following functions #define FLASH_HAL_OK (0) #define FLASH_HAL_READ_ERROR (-1) @@ -46,4 +91,8 @@ extern int32_t flash_hal_write(uint32_t addr, uint32_t size, const uint8_t *src) extern int32_t flash_hal_erase(uint32_t addr, uint32_t size); extern int32_t flash_hal_read(uint32_t addr, uint32_t size, uint8_t *dst); +#ifdef __cplusplus +} // extern "C" +#endif + #endif // !defined(flash_hal_h) diff --git a/cores/esp8266/gdb_hooks.cpp b/cores/esp8266/gdb_hooks.cpp index 8864f757fb..94ca78e807 100644 --- a/cores/esp8266/gdb_hooks.cpp +++ b/cores/esp8266/gdb_hooks.cpp @@ -26,7 +26,7 @@ same stub can be used for gdb_present. */ extern "C" { -static bool ICACHE_RAM_ATTR __gdb_no_op() +static bool IRAM_ATTR __gdb_no_op() { return false; } diff --git a/cores/esp8266/gdb_hooks.h b/cores/esp8266/gdb_hooks.h index 4953216a1a..81a117cc82 100644 --- a/cores/esp8266/gdb_hooks.h +++ b/cores/esp8266/gdb_hooks.h @@ -27,7 +27,7 @@ extern "C" { * @brief Initialize GDB stub, if present * * By default, this function is a no-op. When GDBStub library is linked, - * this function is overriden and does necessary initialization of that library. + * this function is overridden and does necessary initialization of that library. * Called early at startup. */ void gdb_init(void); @@ -36,7 +36,7 @@ void gdb_init(void); * @brief Break into GDB, if present * * By default, this function is a no-op. When GDBStub library is linked, - * this function is overriden and triggers entry into the debugger, which + * this function is overridden and triggers entry into the debugger, which * looks like a breakpoint hit. */ void gdb_do_break(void); @@ -45,7 +45,7 @@ void gdb_do_break(void); * @brief Check if GDB stub is present. * * By default, this function returns false. When GDBStub library is linked, - * this function is overriden and returns true. Can be used to check whether + * this function is overridden and returns true. Can be used to check whether * GDB is used. * * @return true if GDB stub is present @@ -58,7 +58,7 @@ bool gdb_present(void); * @brief Check if GDB is installing a putc1 callback. * * By default, this function returns false. When GDBStub library is linked, - * this function is overriden and returns true. + * this function is overridden and returns true. * * @return true if GDB is installing a putc1 callback */ @@ -69,7 +69,7 @@ bool gdbstub_has_putc1_control(void); * @param func function GDB will proxy putc1 data to * * By default, this function is a no-op. When GDBStub library is linked, - * this function is overriden and sets GDB stub's secondary putc1 callback to + * this function is overridden and sets GDB stub's secondary putc1 callback to * func. When GDB stub is linked, but a GDB session is not current attached, * then GDB stub will pass putc1 chars directly to this function. */ @@ -79,7 +79,7 @@ void gdbstub_set_putc1_callback(void (*func)(char)); * @brief Check if GDB is installing a uart0 isr callback. * * By default, this function returns false. When GDBStub library is linked, - * this function is overriden and returns true. + * this function is overridden and returns true. * * @return true if GDB is installing a uart0 isr callback */ @@ -90,7 +90,7 @@ bool gdbstub_has_uart_isr_control(void); * @param func function GDB will proxy uart0 isr data to * * By default, this function is a no-op. When GDBStub library is linked, - * this function is overriden and sets GDB stub's secondary uart0 isr callback + * this function is overridden and sets GDB stub's secondary uart0 isr callback * to func. When GDB stub is linked, but a GDB session is not current attached, * then GDB stub will pass uart0 isr data back to this function. */ @@ -101,7 +101,7 @@ void gdbstub_set_uart_isr_callback(void (*func)(void*, uint8_t), void* arg); * @param c character to write * * By default, this function is a no-op. When GDBStub library is linked, - * this function is overriden and writes a char to either the GDB session on + * this function is overridden and writes a char to either the GDB session on * uart0 or directly to uart0 if not GDB session is attached. */ void gdbstub_write_char(char c); @@ -112,7 +112,7 @@ void gdbstub_write_char(char c); * @param size length of buffer * * By default, this function is a no-op. When GDBStub library is linked, - * this function is overriden and writes a buffer to either the GDB session on + * this function is overridden and writes a buffer to either the GDB session on * uart0 or directly to uart0 if not GDB session is attached. */ void gdbstub_write(const char* buf, size_t size); 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 3f549716f4..54db1ede4a 100644 --- a/cores/esp8266/heap.cpp +++ b/cores/esp8266/heap.cpp @@ -5,6 +5,18 @@ #include #include "umm_malloc/umm_malloc.h" +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 +#include "umm_malloc/umm_heap_select.h" + #include #include #include @@ -16,6 +28,7 @@ extern "C" { #define UMM_CALLOC(n,s) umm_poison_calloc(n,s) #define UMM_REALLOC_FL(p,s,f,l) umm_poison_realloc_fl(p,s,f,l) #define UMM_FREE_FL(p,f,l) umm_poison_free_fl(p,f,l) +#define STATIC_ALWAYS_INLINE #undef realloc #undef free @@ -25,6 +38,7 @@ extern "C" { #define UMM_CALLOC(n,s) umm_calloc(n,s) #define UMM_REALLOC_FL(p,s,f,l) umm_realloc(p,s) #define UMM_FREE_FL(p,f,l) umm_free(p) +#define STATIC_ALWAYS_INLINE #undef realloc #undef free @@ -34,6 +48,10 @@ extern "C" { #define UMM_CALLOC(n,s) calloc(n,s) #define UMM_REALLOC_FL(p,s,f,l) realloc(p,s) #define UMM_FREE_FL(p,f,l) free(p) + +// STATIC_ALWAYS_INLINE only applies to the non-debug build path, +// it must not be enabled on the debug build path. +#define STATIC_ALWAYS_INLINE static ALWAYS_INLINE #endif @@ -142,7 +160,7 @@ void* _calloc_r(struct _reent* unused, size_t count, size_t size) { (void) unused; void *ret = calloc(count, size); - PTR_CHECK__LOG_LAST_FAIL(ret, count * size); + PTR_CHECK__LOG_LAST_FAIL(ret, umm_umul_sat(count, size)); return ret; } @@ -153,7 +171,7 @@ void* _calloc_r(struct _reent* unused, size_t count, size_t size) #define DEBUG_HEAP_PRINTF ets_uart_printf -void ICACHE_RAM_ATTR print_loc(size_t size, const char* file, int line) +void IRAM_ATTR print_loc(size_t size, const char* file, int line) { (void)size; (void)line; @@ -161,11 +179,11 @@ void ICACHE_RAM_ATTR print_loc(size_t size, const char* file, int line) DEBUG_HEAP_PRINTF(":oom(%d)@", (int)size); bool inISR = ETS_INTR_WITHINISR(); - if (inISR && (uint32_t)file >= 0x40200000) { + if (NULL == file || (inISR && (uint32_t)file >= 0x40200000)) { DEBUG_HEAP_PRINTF("File: %p", file); } else if (!inISR && (uint32_t)file >= 0x40200000) { - char buf[ets_strlen(file) + 1] __attribute__((aligned(4))); - ets_strcpy(buf, file); + char buf[strlen_P(file) + 1]; + strcpy_P(buf, file); DEBUG_HEAP_PRINTF(buf); } else { DEBUG_HEAP_PRINTF(file); @@ -175,7 +193,7 @@ void ICACHE_RAM_ATTR print_loc(size_t size, const char* file, int line) } } -void ICACHE_RAM_ATTR print_oom_size(size_t size) +void IRAM_ATTR print_oom_size(size_t size) { (void)size; if (system_get_os_print()) { @@ -183,8 +201,8 @@ void ICACHE_RAM_ATTR print_oom_size(size_t size) } } -#define OOM_CHECK__PRINT_OOM(p, s) if (!p) print_oom_size(s) -#define OOM_CHECK__PRINT_LOC(p, s, f, l) if (!p) print_loc(s, f, l) +#define OOM_CHECK__PRINT_OOM(p, s) if ((s) && !(p)) print_oom_size(s) +#define OOM_CHECK__PRINT_LOC(p, s, f, l) if ((s) && !(p)) print_loc(s, f, l) #else // ! DEBUG_ESP_OOM @@ -215,13 +233,13 @@ void ICACHE_RAM_ATTR print_oom_size(size_t size) b. Before, when the *alloc function creates a new, not modified, allocation. In a free() or realloc() call, the focus is on their allocation. It is - checked 1st and reported on 1ST if an error exists. Full Posion Check is + checked 1st and reported on 1ST if an error exists. Full Poison Check is done after. - For malloc(), calloc(), and zalloc() Full Posion Check is done 1st since + For malloc(), calloc(), and zalloc() Full Poison Check is done 1st since these functions do not modify an existing allocation. */ -void* ICACHE_RAM_ATTR malloc(size_t size) +void* IRAM_ATTR malloc(size_t size) { INTEGRITY_CHECK__ABORT(); POISON_CHECK__ABORT(); @@ -231,17 +249,20 @@ void* ICACHE_RAM_ATTR malloc(size_t size) return ret; } -void* ICACHE_RAM_ATTR calloc(size_t count, size_t size) +void* IRAM_ATTR calloc(size_t count, size_t size) { INTEGRITY_CHECK__ABORT(); POISON_CHECK__ABORT(); void* ret = UMM_CALLOC(count, size); - PTR_CHECK__LOG_LAST_FAIL(ret, count * size); - OOM_CHECK__PRINT_OOM(ret, size); + #if defined(DEBUG_ESP_OOM) + size_t total_size = umm_umul_sat(count, size);// For logging purposes + #endif + PTR_CHECK__LOG_LAST_FAIL(ret, total_size); + OOM_CHECK__PRINT_OOM(ret, total_size); return ret; } -void* ICACHE_RAM_ATTR realloc(void* ptr, size_t size) +void* IRAM_ATTR realloc(void* ptr, size_t size) { INTEGRITY_CHECK__ABORT(); void* ret = UMM_REALLOC_FL(ptr, size, NULL, 0); @@ -251,7 +272,7 @@ void* ICACHE_RAM_ATTR realloc(void* ptr, size_t size) return ret; } -void ICACHE_RAM_ATTR free(void* p) +void IRAM_ATTR free(void* p) { INTEGRITY_CHECK__ABORT(); UMM_FREE_FL(p, NULL, 0); @@ -259,8 +280,8 @@ void ICACHE_RAM_ATTR free(void* p) } #endif - -void* ICACHE_RAM_ATTR pvPortMalloc(size_t size, const char* file, int line) +STATIC_ALWAYS_INLINE +void* IRAM_ATTR heap_pvPortMalloc(size_t size, const char* file, int line) { INTEGRITY_CHECK__PANIC_FL(file, line); POISON_CHECK__PANIC_FL(file, line); @@ -270,17 +291,22 @@ void* ICACHE_RAM_ATTR pvPortMalloc(size_t size, const char* file, int line) return ret; } -void* ICACHE_RAM_ATTR pvPortCalloc(size_t count, size_t size, const char* file, int line) +STATIC_ALWAYS_INLINE +void* IRAM_ATTR heap_pvPortCalloc(size_t count, size_t size, const char* file, int line) { INTEGRITY_CHECK__PANIC_FL(file, line); POISON_CHECK__PANIC_FL(file, line); void* ret = UMM_CALLOC(count, size); - PTR_CHECK__LOG_LAST_FAIL_FL(ret, count * size, file, line); - OOM_CHECK__PRINT_LOC(ret, size, file, line); + #if defined(DEBUG_ESP_OOM) + size_t total_size = umm_umul_sat(count, size); + #endif + PTR_CHECK__LOG_LAST_FAIL_FL(ret, total_size, file, line); + OOM_CHECK__PRINT_LOC(ret, total_size, file, line); return ret; } -void* ICACHE_RAM_ATTR pvPortRealloc(void *ptr, size_t size, const char* file, int line) +STATIC_ALWAYS_INLINE +void* IRAM_ATTR heap_pvPortRealloc(void *ptr, size_t size, const char* file, int line) { INTEGRITY_CHECK__PANIC_FL(file, line); void* ret = UMM_REALLOC_FL(ptr, size, file, line); @@ -290,7 +316,8 @@ void* ICACHE_RAM_ATTR pvPortRealloc(void *ptr, size_t size, const char* file, in return ret; } -void* ICACHE_RAM_ATTR pvPortZalloc(size_t size, const char* file, int line) +STATIC_ALWAYS_INLINE +void* IRAM_ATTR heap_pvPortZalloc(size_t size, const char* file, int line) { INTEGRITY_CHECK__PANIC_FL(file, line); POISON_CHECK__PANIC_FL(file, line); @@ -300,21 +327,155 @@ void* ICACHE_RAM_ATTR pvPortZalloc(size_t size, const char* file, int line) return ret; } -void ICACHE_RAM_ATTR vPortFree(void *ptr, const char* file, int line) +STATIC_ALWAYS_INLINE +void IRAM_ATTR heap_vPortFree(void *ptr, const char* file, int line) { INTEGRITY_CHECK__PANIC_FL(file, line); UMM_FREE_FL(ptr, file, line); POISON_CHECK__PANIC_FL(file, line); } -size_t ICACHE_RAM_ATTR xPortWantedSizeAlign(size_t size) +size_t IRAM_ATTR xPortWantedSizeAlign(size_t size) { return (size + 3) & ~((size_t) 3); } void system_show_malloc(void) { +#ifdef UMM_INFO + HeapSelectDram ephemeral; umm_info(NULL, true); +#endif +} + +/* + NONOS SDK and lwIP do not handle IRAM heap well. Since they also use portable + malloc calls pvPortMalloc, ... we can leverage that for this solution. + Force pvPortMalloc, ... APIs to serve DRAM only. +*/ +void* IRAM_ATTR pvPortMalloc(size_t size, const char* file, int line) +{ + HeapSelectDram ephemeral; + 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); +} + +void* IRAM_ATTR pvPortRealloc(void *ptr, size_t size, const char* file, int line) +{ + HeapSelectDram ephemeral; + 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); +} + +void IRAM_ATTR vPortFree(void *ptr, const char* file, int line) +{ +#if defined(DEBUG_ESP_OOM) || defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) || defined(UMM_INTEGRITY_CHECK) + // This is only needed for debug checks to ensure they are performed in + // correct context. umm_malloc free internally determines the correct heap. + HeapSelectDram ephemeral; +#endif + 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 new file mode 100644 index 0000000000..0f0bee8d4e --- /dev/null +++ b/cores/esp8266/hwdt_app_entry.cpp @@ -0,0 +1,1207 @@ +/* + * Copyright 2020 Michael 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. + */ + +/* + * As far as I know, there is no way to get called for a Hardware WDT. I assume + * it generates a form of reset that occurs at a low level that cannot be + * trapped. Debugging an HWDT can be quite challenging. + * + * This module writes a stack dump to the serial port after a Hardware Watchdog + * Timer has struck, and a new boot cycle has begun. By making adjustments to the + * stack, we can avoid crash stack data being overwritten by this tool, + * the Boot ROM, and the bootloader. + * + * We are using the method defined for `core_esp8266_app_entry_noextra4k.cpp` to + * load an alternate `app_entry_redefinable()`. For details on this method, see + * comments in `core_esp8266_main.cpp's app_entry()`. + * + * Using this tool's alternate `app_entry_redefinable()`, we can gain control + * before the SDK is started. And dump the contents of the "sys" and "cont" + * stacks. + * + * By making some adjustments to start of the stack pointer, at the entry to + * `app_entry_redefinable()`, and also to the stack pointer passed to the SDK, + * we can preserve the stack during an HWDT event. + * + * To use this tool, select HWDT or HWDT_NOEXTRA4K from the Arduino IDE menu + * "Tools->Debug Level" before building your sketch. Note, 'Tools->Debug port' + * selection is not needed or referenced for printing the HWDT stack dump. + * + * To enable in other build environments, add DEBUG_ESP_HWDT_NOEXTRA4K or + * DEBUG_ESP_HWDT global defines to your build. + * + * This tool prints to the serial port at the default serial port speed set by + * the Boot ROM. On a Hardware WDT reset that port speed is 115200 bps. If your + * needs differ, see the DEBUG_ESP_HWDT_UART_SPEED option below. + * + * More on crystal differences and data rates: + * When the ESP8266 restarts because of a Hardware WDT reset, the port speed + * defaults to 115200. This will be the speed, even if you have a 26MHz or + * 40MHz Crystal. If you would like to use a different data rate, use the + * option DEBUG_ESP_HWDT_UART_SPEED described below. + * + * The Boot ROM initially sets the UART clock divisor to support a data rate + * of 115200 bps with the assumption that it has a 40MHz crystal. When a + * 26MHz crystal is used instead, the resulting error gives us a real rate + * of 74880 bps and printing is garbled at first, until the CPU clock's PLL + * is adjusted by the NONOS SDK. While CH_PD and EST_RST bring the CPU back + * to this state of underclocking with a 26MHz crystal, the Hardware WDT + * reset does not appear to do the same. The UART continues to be clocked at + * a rate to support a device at 115200 bps. Thus, allowing us to see the + * old cryptic WDT message along with our new stack dump. + * + * + * When you get a stack dump, copy-paste it into the "ESP Exception Decoder". + * Since we don't have an SP, we see a lot more stuff in the report, compared to + * what you would see with a postmortem report. Start at the bottom and work + * your way up. At this time, I have not had a lot of practice using this tool. + * TODO: Update description with more details when available. + * + * SYS Stack Issue with Extra 4K Heap option: + * During WiFi Connect, Reconnect, and about every hour a block of memory + * 0x3FFFEA80 - 0x3FFFEB30 (176 bytes) is zeroed by the Boot ROM function + * aes_decrypt_init. All other painted memory in the area was untouched + * after starting WiFi. See `core/esp8266/aes_unwrap.cpp` for more details. + * + * + * Possible Issues/Thoughts/Improvements: + * + * On reboot after an OTA download, eboot requires a lot of stack and DRAM + * space. On the other hand, for routine loads from flash, the stack and DRAM + * usage is small, leaving us valid data to print a stack dump. + * + * If a problem should arise with some data elements being corrupted during + * reboot, would it be possible to move their DRAM location higher in memory? + * + * Also, DRAM being valid across reset events other than power-on and deep + * sleep, suggest that a variable in the .noinit section could be used instead + * of the more limited RTC Memory for sketches that don't do deep sleep. + * However, DRAM should be considered invalid after an upload serial or OTA. + * These operations use a lot of DRAM. + * + * With this module active, postmortem stack dumps will be a little longer than + * they need to be. The sys stack now ends at 0x3FFFFC00 instead of 0x3FFFFB0. + * + * Maybe an in/out ref count would be nice for bearssl and cont stacks. + */ + +/*____________________________________________________________________________*/ +/* */ +/* Configuration Options */ +/*____________________________________________________________________________*/ + + +/* + * DEBUG_ESP_HWDT + * + * Enables this debug tool for printing a Hardware WDT stack dump at restart. + * + * This option is now managed from the Arduino IDE menu 'Tools->Debug Level' + #define DEBUG_ESP_HWDT + */ + + +/* + * DEBUG_ESP_HWDT_NOEXTRA4K + * + * This option will leave more of the system stack available for the stack dump. + * A problem we have with the "4K extra" option, is it pushes the system stack + * up into the ROM's BSS area which gets zeroed at reboot by the Boot ROM. + * + * Using this option has the effect of taking 4K of DRAM away from the heap, + * which gets used for the "cont" stack. Leaving an extra 4K on the "sys" stack, + * that is clear of the ROM's BSS area. This allows for a more complete "sys" + * stack dump. The choice here can depend on where you are crashing. + * + * Because we don't know where the crash occurs, this option prints two stack + * dumps, one for "cont" (user stack) and one for "sys" (NONOS SDK). + * + * In contrast, if the hang is happening on the "cont" stack, you don't need a + * complete stack dump of the "sys" stack. You can omit this define and have an + * extra 4K in the heap. + * + * This option is now managed from the Arduinoo IDE menu 'Tools->Debug Level' + #define DEBUG_ESP_HWDT_NOEXTRA4K + */ + + +/* + * DEBUG_ESP_HWDT_UART_SPEED + * + * This option alters 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 and save on the IRAM space. Note this option only + * changes the speed while this module is printing. + * + * For more confusion on the serial port speed, see "More on crystal differences + * and data rates" in the comments at the top. + * + */ + // #define DEBUG_ESP_HWDT_UART_SPEED (19200) + // #define DEBUG_ESP_HWDT_UART_SPEED (74880) + // #define DEBUG_ESP_HWDT_UART_SPEED (115200) + // #define DEBUG_ESP_HWDT_UART_SPEED (230400) + + +/* + * DEBUG_ESP_HWDT_PRINT_GREETING + * + * Prints a simple introduction to let you know this tool is active and in the + * build. At power-on, this may not be viewable on some devices. The crystal has + * to be 40Mhz for this to work w/o using the DEBUG_ESP_HWDT_UART_SPEED option + * above. May not be worth the cost in IRAM. + * + * EDIT: There is something different in the UART setup after a flash upload. I + * am unable to print using the same code that works for Power-on and an EXT_RST + * at any other time. After the SDK has run a 2nd EXT_RST will show the greeting + * message. + * + * EDIT2: Seems to work better now. Placed delays around calls to + * uart_div_modify(). Leave these comments until I have more experience with + * this change. + * + * EDIT3: The delay before the uart_div_modify() has been replaced with a wait + * till FIFO empty loop. I now believe the lost greeting message after an + * esptool firmware update, has to do with the transition period between the + * tool performing hardware reset and exiting, then the serial monitor + * re-engaging. This is not an issue that needs to be addressed here. + */ + #ifndef DEBUG_ESP_HWDT_PRINT_GREETING + #define DEBUG_ESP_HWDT_PRINT_GREETING (1) + #endif + + +/* + * DEBUG_ESP_HWDT_ROM_STACK_SIZE + * + * There are four sections of code starting just before 0x40000000, that share + * the same stack space. + * 1) The Boot ROM (uses around 640 bytes) + * 2) The Bootloader, eboot.elf (uses around 720 bytes.) + * 3) `app_entry_redefinable()` just before it starts the SDK. + * 4) The NONOS SDK and optionally the Core when the extra 4K option is selected. + * + * To preserve the sketch stack data for a stack dump, I define three separate + * stacks: + * 1) Boot ROM and eboot + * 2) this stack dump code + * 3) SDK, Core, and Sketch + * + * For the "NO extra 4K Heap" case, we use a ROM stack size of 1024. However, + * without `aes_unwrap.cpp`, 1024 is not safe for the "extra 4K of heap" case. + * Bad crashes happen with the 1024 and the "extra 4K of Heap". For now, leave + * this case with 720 bytes for ROM Stack since it also gives more SYS stack for + * dumping. See comment in aes_unwrap.cpp for AES buffer clash with SYS stack + * space description. + * + * If or when eboot.elf uses more than 720 there will be a little over-writing + * of the cont stack that we report. Then 720 can be increased as long as the + * replacement aes_unwrap is used. + * + * If possible, use the no-extra 4K heap option. This is the optimum choice for + * debugging HWDT crashes. It has the content of SYS stack fully exposed. + * + */ +#ifndef DEBUG_ESP_HWDT_ROM_STACK_SIZE + #ifdef DEBUG_ESP_HWDT_NOEXTRA4K + #define DEBUG_ESP_HWDT_ROM_STACK_SIZE (1024UL) + #else + #define DEBUG_ESP_HWDT_ROM_STACK_SIZE (720UL) + #endif +#endif + + +/* + * DEBUG_ESP_HWDT_INFO + * + * Gather some information on ROM and bootloader combined, sys, and cont stack + * usage. If you are missing the include file for this structure, you can + * copy-paste from the embedded version of the .h below. + * + */ + // #define DEBUG_ESP_HWDT_INFO + + +/* + * ROM_STACK_DUMP + * + * Dump the stack contents of the ROM Stack area. This gives us a visual of the + * stack usage. Probably not of value, beyond developing this tool. + * + * To see printing, you may need to use this option with DEBUG_ESP_HWDT_UART_SPEED. + */ + // #define ROM_STACK_DUMP + + +/* + * HWDT_IF_METHOD_RESET_REASON + * + * "If" statement or "switch" method to implement, the reset reason logic. Both + * can be made smaller by removing confirmation checks. + * + * Checks are performed when DEBUG_ESP_HWDT_DEV_DEBUG_RESET_REASON has been defined. + * + * EDIT: I should settle on one or the other; however, new issues continue to + * pop up on determining reset reason. I'll wait until later and pick one. + * + #define DEBUG_ESP_HWDT_DEV_DEBUG_RESET_REASON + */ + #define HWDT_IF_METHOD_RESET_REASON + +/*____________________________________________________________________________*/ +/* */ +/* End of Configuration Options */ +/*____________________________________________________________________________*/ + +#if defined(DEBUG_ESP_HWDT) || defined(DEBUG_ESP_HWDT_NOEXTRA4K) + +#include +#include "cont.h" +#include "coredecls.h" +#include +#include +#include +#include +#include +#include "umm_malloc/umm_malloc.h" +#include "mmu_iram.h" + +extern "C" { +#include +extern void call_user_start(); +extern uint32_t rtc_get_reset_reason(void); + +uint32_t __zero_return() { + return 0; +} +// extern void stack_thunk_dump_stack(); +extern uint32_t stack_thunk_get_refcnt() __attribute__((weak, alias("__zero_return"))); +extern uint32_t stack_thunk_get_stack_top() __attribute__((weak, alias("__zero_return"))); +extern uint32_t stack_thunk_get_stack_bot() __attribute__((weak, alias("__zero_return"))); + +} + +// #define DEBUG_ESP_HWDT_DEV_DEBUG +// #define USE_IRAM + +//C To the maintainers: Since the Cache_Read_Enable logic seems to work, do you +//C see a need or would you like to keep the IRAM build path? +//C If you see no need for it the USE_IRAM build path could be deleted. +#ifdef USE_IRAM +#undef USE_IRAM +#define USE_IRAM 1 +#define IRAM_MAYBE IRAM_ATTR + +#else +#undef USE_IRAM +#define IRAM_MAYBE +#endif + +#define STATIC static __attribute__((noinline)) +// #define STATIC + +#ifdef DEBUG_ESP_HWDT_DEV_DEBUG +/* + * We have two copies of hwdt_info_t. Verify internal and external structures + * match. + * + * This duplication is done so that in most cases, a simple/quick add one file + * to a sketch folder is enough to debug. + * + * Only if additional internal information is needed, would this include be + * added. Since we have two copies, a static_assert is used to verify that at + * least the the size of the two structures are the same. + */ +#include "hwdt_app_entry.h" +#endif + +/* + * Verify that the internal and external structure definitions match. + */ +#ifdef HWDT_STACK_DUMP_H +#define hwdt_info_t LOCAL_HWDT_INFO_T +#define hwdt_info_ LOCAL_HWDT_INFO_ +#define hwdt_info LOCAL_HWDT_INFO +#define HWDT_VERIFY_HWDT_INFO +#endif + +/* + * If you are using the hwdt_info_t structure, and are missing the include file. + * Copy-paste the include block below into its respective filename. + */ + +/*____________________________________________________________________________*/ +/* */ +/* Start of copy-paste block to create "hwdt_app_entry.h" */ +/*____________________________________________________________________________*/ +#if !defined(HWDT_STACK_DUMP_H) || defined(HWDT_VERIFY_HWDT_INFO) +#define HWDT_STACK_DUMP_H + +typedef struct hwdt_info_ { + uint32_t rom; + uint32_t sys; + uint32_t cont; + uint32_t bearssl; + uint32_t rom_api_reason; + uint32_t rtc_sys_reason; + uint32_t reset_reason; + uint32_t cont_integrity; + bool g_pcont_valid; +} hwdt_info_t; + +extern "C" void debug_hwdt_init(void); + +extern uint32_t *g_rom_stack; +extern hwdt_info_t hwdt_info; + +#endif +/*____________________________________________________________________________*/ +/* */ +/* End of copy-paste block for creating "hwdt_app_entry.h" */ +/*____________________________________________________________________________*/ + + +#ifdef HWDT_VERIFY_HWDT_INFO +#undef hwdt_info_t +#undef hwdt_info_ +#undef hwdt_info +#undef HWDT_VERIFY_HWDT_INFO +static_assert(sizeof(hwdt_info_t) == sizeof(LOCAL_HWDT_INFO_T), "Local and include version of hwdt_info_t do not match."); +#endif + + +#define MK_ALIGN16_SZ(a) (((a) + 0x0FUL) & ~0x0FUL) +#define ALIGN_UP(a, s) ((decltype(a))((((uintptr_t)(a)) + (s-1)) & ~(s-1))) +#define ALIGN_DOWN(a, s) ((decltype(a))(((uintptr_t)(a)) & ~(s-1))) + +#ifndef CONT_STACKGUARD +#define CONT_STACKGUARD 0xfeefeffe +#endif +#define RTC_SYS ((volatile uint32_t*)0x60001100UL) + +/* + * + #define DRAM_START ((uint32_t *)0x3FFE8000UL) + #define DRAM_END ((uint32_t *)0x40000000UL) + * + * The space between 0x3fffe000 up to 0x3fffeb30 is a ROM BSS area that is later + * claimed by the SDK for stack space. This is a problem area for this tool, + * because the ROM BSS gets zeroed as part of ROM init on reboot. Any part of + * the "sys" stack residing there is lost. On the other hand, it becomes a prime + * candidate for DRAM address space to handle the needs of this tool. + * + #define SYS_STACK_E000 ((uint32_t *)0x3fffe000UL) + */ + +#define ROM_STACK_FIRST ((uint32_t *)0x40000000UL) +#define SYS_STACK ((uint32_t *)0x3fffeb30UL) + +// Map out who will live where. +#define ROM_STACK_A16_SZ (MK_ALIGN16_SZ(DEBUG_ESP_HWDT_ROM_STACK_SIZE)) +#define CONT_STACK_A16_SZ (MK_ALIGN16_SZ(sizeof(cont_t))) +/* + * For WPS support, cont stack comes out of the user's heap address space. + * The NONOS-SDK stack address is initialized before the reserved ROM stack + * space. In this configuration there is no extra 4K in the heap. + * Memory map: 0x3FFE8000, ..., (CONT_STACK), ..., (SYS), (ROM_STACK), 0x4000000 + * + * sys_stack_first <= ROM_STACK + */ +#define ROM_STACK ((uint32_t *) ((uintptr_t)ROM_STACK_FIRST - ROM_STACK_A16_SZ)) + + +#define CONT_STACK_FIRST ROM_STACK // only for computation +/* + * For extra 4K of heap space, the continuation stack (user's stack) is created + * in the SYS stack address space. The NONOS-SDK stack starts before the cont + * stack. + * Memory map: 0x3FFE8000, ..., (SYS), (CONT_STACK), (ROM_STACK), 0x4000000 + * + * sys_stack_first <= CONT_STACK + */ +#define CONT_STACK ((cont_t *)((uintptr_t)CONT_STACK_FIRST - CONT_STACK_A16_SZ)) + + +uint32_t *g_rom_stack __attribute__((section(".noinit"))); +uint32_t *sys_stack_first __attribute__((section(".noinit"))); +size_t g_rom_stack_A16_sz __attribute__((section(".noinit"))); +hwdt_info_t hwdt_info __attribute__((section(".noinit"))); + +extern "C" { + +extern cont_t * get_noextra4k_g_pcont(void); + +cont_t * IRAM_ATTR get_noextra4k_g_pcont(void) __attribute__((weak)); +cont_t * IRAM_ATTR get_noextra4k_g_pcont(void) { + return NULL; +} + +static void IRAM_MAYBE set__sys_stack_first(void) { + if (get_noextra4k_g_pcont()) { + sys_stack_first = ROM_STACK; + } else { + sys_stack_first = (uint32_t *)CONT_STACK; + } +} + +#if USE_IRAM +#define ETS_PRINTF ets_uart_printf + +#else +/* + * This function is already in umm_malloc for some debug options. + * Define here in case they are not enabled. + */ +int ICACHE_FLASH_ATTR umm_info_safe_printf_P(const char *fmt, ...) __attribute__((weak)); +int ICACHE_FLASH_ATTR umm_info_safe_printf_P(const char *fmt, ...) { + char ram_buf[strlen_P(fmt) + 1]; + strcpy_P(ram_buf, fmt); + va_list argPtr; + va_start(argPtr, fmt); + int result = ets_vprintf(ets_uart_putc1, ram_buf, argPtr); + va_end(argPtr); + return result; +} + +#define ETS_PRINTF(fmt, ...) umm_info_safe_printf_P(PSTR(fmt), ##__VA_ARGS__) +#endif + +#define ETS_FLUSH(uart_no) while((USS(uart_no) >> USTXC) & 0xff) {} + +enum PRINT_STACK { + CONT = 1, + SYS = 2, + ROM = 4, + BEARSSL = 8 +}; + + +STATIC void IRAM_MAYBE print_stack(const uintptr_t start, const uintptr_t end, const uint32_t chunk) { + ETS_PRINTF("\n\n>>>stack>>>\n\nctx: "); + + if (chunk & PRINT_STACK::CONT) { + ETS_PRINTF("cont"); + } else + if (chunk & PRINT_STACK::SYS) { + ETS_PRINTF("sys"); + } else + if (chunk & PRINT_STACK::ROM) { + ETS_PRINTF("ROM"); + } else + if (chunk & PRINT_STACK::BEARSSL) { + ETS_PRINTF("bearssl"); + } + + ETS_PRINTF("\nsp: %08x end: %08x offset: %04x\n", start, end, 0); + + const size_t this_mutch = end - start; + if (this_mutch >= 0x10) { + for (size_t pos = 0; pos < this_mutch; pos += 0x10) { + const uint32_t *value = (uint32_t *)(start + pos); + + /* rough indicator: stack frames usually have SP saved as the second word */ + bool looksLikeStackFrame = (value[2] == (start + pos + 0x10)); + ETS_PRINTF("%08x: %08x %08x %08x %08x %c\n", (uint32_t)&value[0], + value[0], value[1], value[2], value[3], + (looksLikeStackFrame)?'<':' '); + } + } + + ETS_PRINTF("<< new_uart_divisor || + new_uart_divisor == uart_divisor) { + uart_divisor = 0; // used to indicate no change + + } else { + adjust_uart_speed(new_uart_divisor); + } + +#if defined(DEBUG_ESP_HWDT_DEV_DEBUG) + ETS_PRINTF("\n\nreal_uart_div_modify(0, %u / %u);\n", master_freq, new_speed); + ETS_PRINTF("F_CRYSTAL = %u\n", crystal_freq); + ETS_PRINTF("old uart_divisor = %u\n", uart_divisor); + ETS_PRINTF("new uart_divisor = %u\n", new_uart_divisor); + ETS_PRINTF("master_freq = %u\n", master_freq); +#endif + + return uart_divisor; +} +#endif + +/* + * When g_pcont is valid, we expect these checks to be valid. I am not sure + * what to do when they are not. An error that could lead to a crash is + * corrected. We currently continue and print the stack dump. This assumes + * something is better than nothing. + * + * Make global so postmortem can take advange of this check. + */ +uint32_t IRAM_MAYBE hwdt_cont_integrity_check() { + uint32_t cont_integrity = 0; + if (g_pcont->stack_guard1 != CONT_STACKGUARD) { + cont_integrity |= 0x0001; + } + if (g_pcont->stack_guard2 != CONT_STACKGUARD) { + cont_integrity |= 0x0020; + } + if (g_pcont->stack_end != (g_pcont->stack + (sizeof(g_pcont->stack) / 4))) { + cont_integrity |= 0x0300; + // Fix ending so we don't crash + g_pcont->stack_end = (g_pcont->stack + (sizeof(g_pcont->stack) / 4)); + } + if (g_pcont->struct_start != (unsigned*)g_pcont) { + cont_integrity |= 0x4000; + g_pcont->struct_start = (unsigned*)g_pcont; + } + hwdt_info.cont_integrity = cont_integrity; + return cont_integrity; +} +/* + * Determine if we have a HWDT reboot and dump stack traces if so. + */ +STATIC void IRAM_MAYBE handle_hwdt(void) __attribute__((used)); +STATIC void IRAM_MAYBE handle_hwdt(void) { +#ifdef DEBUG_ESP_HWDT_NOEXTRA4K + disable_extra4k_at_link_time(); +#endif + set__sys_stack_first(); + + ets_memset(&hwdt_info, 0, sizeof(hwdt_info)); + hwdt_check_g_pcont_validity(); + + bool power_on = false; + bool hwdt_reset = false; + get_reset_reason(&power_on, &hwdt_reset); + +#ifdef DEBUG_ESP_HWDT_UART_SPEED + const uint32_t uart_divisor = set_uart_speed(0, DEBUG_ESP_HWDT_UART_SPEED); +#endif +#if defined(DEBUG_ESP_HWDT_DEV_DEBUG) + ETS_PRINTF("Basic boot reason: %s\n", (power_on) ? "Power-on" : "Reboot"); + ETS_PRINTF("RTC_SYS Reset Reason = %u\n", hwdt_info.rtc_sys_reason); + ETS_PRINTF("ROM API Reset Reason = %u\n", hwdt_info.rom_api_reason); + ETS_PRINTF("HWDT Reset Reason = %u\n\n", hwdt_info.reset_reason); +#endif +#if defined(DEBUG_ESP_HWDT_DEV_DEBUG_RESET_REASON) + if (REASON_EXT_SYS_RST < hwdt_info.reset_reason) { + ETS_PRINTF("Reset reason confirmation failed!\n"); + ETS_PRINTF(" RTC_SYS Reset Reason = %u\n", hwdt_info.rtc_sys_reason); + ETS_PRINTF(" ROM API Reset Reason = %u\n", hwdt_info.rom_api_reason); + } +#endif + /* + * With a few exceptions, DRAM data remains valid after a reset. + * + * Check for "cont" stack consistency. + * The contents of DRAM are not expected to be valid after a: + * 1) flash update (OTA or serial) + * 2) power-on + * 3) deep sleep + * Additionally, g_pcont is expected to be invalid after these events. + * + */ + if (!power_on && hwdt_info.g_pcont_valid) { + // Checks and fixes incorrect cont_t structure values that might + // otherwise cause us to crash. + hwdt_cont_integrity_check(); + + const uint32_t *ctx_cont_ptr = NULL; +#if !defined(DEBUG_ESP_HWDT_INFO) + if (get_noextra4k_g_pcont()) +#endif + { + ctx_cont_ptr = skip_stackguard(g_pcont->stack, g_pcont->stack_end, CONT_STACKGUARD); + hwdt_info.cont = (uintptr_t)g_pcont->stack_end - (uintptr_t)ctx_cont_ptr; + } + + const uint32_t *ctx_sys_ptr = skip_stackguard(SYS_STACK, ROM_STACK, CONT_STACKGUARD); + hwdt_info.sys = (uintptr_t)ROM_STACK - (uintptr_t)ctx_sys_ptr; + +#ifndef USE_IRAM + const uint32_t *bearssl_stack_top = NULL; + const uint32_t *ctx_bearssl_ptr = NULL; + if (stack_thunk_get_refcnt()) { + bearssl_stack_top = (const uint32_t *)stack_thunk_get_stack_top(); + ctx_bearssl_ptr = skip_stackguard((const uint32_t *)stack_thunk_get_stack_bot(), bearssl_stack_top, 0xdeadbeef); + hwdt_info.bearssl = (uintptr_t)bearssl_stack_top - (uintptr_t)ctx_bearssl_ptr; + } +#endif + + if (hwdt_reset) { + ETS_PRINTF("\n\nHardware WDT reset\n"); +#ifndef USE_IRAM + if (bearssl_stack_top) { + /* Print context bearssl */ + print_stack((uintptr_t)ctx_bearssl_ptr, (uintptr_t)bearssl_stack_top, PRINT_STACK::BEARSSL); + } +#endif + /* Print context SYS */ + print_stack((uintptr_t)ctx_sys_ptr, (uintptr_t)ROM_STACK, PRINT_STACK::SYS); + if (get_noextra4k_g_pcont()) { + /* Print separate ctx: cont stack */ + + /* Check if cont stack is yielding to SYS */ + if (0 == hwdt_info.cont_integrity && 0 != g_pcont->pc_suspend) { + ctx_cont_ptr = (const uint32_t *)((uintptr_t)g_pcont->sp_suspend - 8u); + } + print_stack((uintptr_t)ctx_cont_ptr, (uintptr_t)g_pcont->stack_end, PRINT_STACK::CONT); + } else { + if (0 == hwdt_info.cont_integrity && 0 != g_pcont->pc_suspend) { + ETS_PRINTF("\nCont stack is yielding. Active stack starts at 0x%08X.\n", (uint32_t)g_pcont->sp_suspend - 8u); + } + } + + if (hwdt_info.cont_integrity) { + ETS_PRINTF("\nCaution, the stack is possibly corrupt integrity checks did not pass.\n\n"); + } + } + } + + /* + * Fill the SDK stack area with CONT_STACKGUARD so we can detect and + * skip the unused section of the stack when printing a Stack Dump. + */ + { + size_t this_mutch = (uintptr_t)ROM_STACK - (uintptr_t)SYS_STACK; + this_mutch /= sizeof(uint32_t); + for (size_t i = 0; i < this_mutch; i++) { + SYS_STACK[i] = CONT_STACKGUARD; + } + } + +#if defined(DEBUG_ESP_HWDT_INFO) || defined(ROM_STACK_DUMP) + /* + * Reports on ROM_STACK usage by ROM and eboot. + * Used to confirm DEBUG_ESP_HWDT_ROM_STACK_SIZE is large enough. + */ + { + const uint32_t *ctx_rom_ptr = skip_stackguard(ROM_STACK, ROM_STACK_FIRST, CONT_STACKGUARD); + hwdt_info.rom = (uintptr_t)ROM_STACK_FIRST - (uintptr_t)ctx_rom_ptr; +#if defined(ROM_STACK_DUMP) + print_stack((uintptr_t)ctx_rom_ptr, (uintptr_t)ROM_STACK_FIRST, PRINT_STACK::ROM); +#endif + } +#endif + +#if DEBUG_ESP_HWDT_PRINT_GREETING + ETS_PRINTF("\n\nHardware WDT Stack Dump - enabled\n\n"); +#else + ETS_PRINTF("\n\n"); +#endif + +#ifdef DEBUG_ESP_HWDT_UART_SPEED + if (uart_divisor) { + adjust_uart_speed(uart_divisor); + } +#endif +} + +#if defined(DEBUG_ESP_HWDT_DEV_DEBUG) && !defined(USE_IRAM) +static void printSanityCheck() { + ETS_PRINTF("\n\nsys_stack_first: %p\n", sys_stack_first); + ETS_PRINTF( "CONT_STACK: %p\n", CONT_STACK); + ETS_PRINTF( "g_pcont: %p\n", g_pcont); + ETS_PRINTF( "ROM_STACK: %p\n", ROM_STACK); + ETS_PRINTF( "get_noextra4k_g_pcont(): %p\n", get_noextra4k_g_pcont()); + ETS_PRINTF( "g_rom_stack: %p\n", g_rom_stack); + ETS_PRINTF( "g_rom_stack_A16_sz: 0x%08X\n\n", g_rom_stack_A16_sz); +} +#endif //DEBUG_ESP_HWDT_DEV_DEBUG + +/* + * Using Cache_Read_Enable/Cache_Read_Disable to reduce IRAM usage. Moved + * strings and most functions to flash. At this phase of the startup, "C++" has + * not initialized. So, we needed a local "C" function to handle printing from + * flash. For this, I grabbed a copy of umm_info_safe_printf_P. + * + * This reduced IRAM usage by ~1K and DRAM ~200 bytes. + * + * Inspiration for using Cache_Read_Enable came from reviewing rboot, zboot, and + * https://richard.burtons.org/2015/06/12/esp8266-cache_read_enable/. + * Additional insight can be gleemed from reviewing the ESP8266_RTOS_SDK. + * (eg. ../components/bootloader_support/src/bootloader_utility.c) + * + * The logic to use Cache_Read_Enable and Cache_Read_Disable has been + * generalized into a wrapper function, mmu_wrap_irom_fn, and moved to + * mmu_iram.cpp. + */ + +/* + hwdt_pre_sdk_init() is the result of a hook for development diagnotics which + evolved and was generlized to run any optional diagnostic code supplied at + link time. + + Summary of the hwdt_pre_sdk_init() runtime environment: + * The code can run from flash and use PROGMEM strings. + * All functions must be extern "C" type + * C/C++ runtime has not started. Structures have not been initialized and + should have the values prior to reboot. With the exception of hwdt_info, + which was updated before this call. + * You can reference hwdt_info.reset_reason to control the action of the diagnostic. + * The stack is on the SYS stack. You have about 3K available before you + overwrite ROM Data area. + * Printing will work best with ets_uart_printf and umm_info_safe_printf_P. + */ +void hwdt_pre_sdk_init(void) __attribute__((weak)); +void hwdt_pre_sdk_init(void) { +#if defined(DEBUG_ESP_HWDT_DEV_DEBUG) && !defined(USE_IRAM) + printSanityCheck(); +#endif +} + +static void __attribute__((noinline)) hwdt_pre_sdk_init_icache(void) __attribute__((used)); +void hwdt_pre_sdk_init_icache(void) { +#ifdef DEBUG_ESP_HWDT_UART_SPEED + const uint32_t uart_divisor = set_uart_speed(0, DEBUG_ESP_HWDT_UART_SPEED); +#endif + + hwdt_pre_sdk_init(); + +#ifdef DEBUG_ESP_HWDT_UART_SPEED + if (uart_divisor) { + adjust_uart_speed(uart_divisor); + } +#endif +} + +/* + For app_entry_redefinable, use Basic ASM instead of "C" with Extended ASM. The + (inline) Extended ASM approach required constant inspection to verify that the + compiler's optimizer did not clobber needed registers or do something weird + after minor changes in code or compiler updates. Also, I think Basic ASM is + the safer route when changing the stack pointer multiple times. +*/ +cont_t *hwdt_app_entry__cont_stack __attribute__((used)) = CONT_STACK; + +asm ( + ".section .iram.text.hwdt_app_entry.cpp,\"ax\",@progbits\n\t" + ".literal_position\n\t" + ".literal .g_pcont, g_pcont\n\t" + ".literal .pcont_stack, hwdt_app_entry__cont_stack\n\t" + ".literal .sys_stack_first, sys_stack_first\n\t" + ".literal .umm_init, umm_init\n\t" + ".literal .call_user_start, call_user_start\n\t" + ".literal .get_noextra4k_g_pcont, get_noextra4k_g_pcont\n\t" + ".literal .mmu_wrap_irom_fn, mmu_wrap_irom_fn\n\t" + ".align 4\n\t" + ".global app_entry_redefinable\n\t" + ".type app_entry_redefinable, @function\n\t" + "\n" +"app_entry_redefinable:\n\t" + /* + * There are 4 sections of code that share the stack starting near + * 0x40000000. + * 1) The Boot ROM (uses around 640 bytes) + * 2) The Bootloader, eboot.elf (last seen using 720 bytes.) + * 3) `app_entry_redefinable()` just before it starts the SDK. + * 4) The NONOS SDK, optionally the Core when the extra 4K option is + * selected. + * + * Use the ROM BSS zeroed out memory as the home for our temporary stack. + * This way no additional information will be lost. That will remove this + * tool from the list of possible concerns for stack overwrite. + * + */ + "movi a1, 0x3fffeb30\n\t" +#ifdef USE_IRAM + "call0 handle_hwdt\n\t" +#else + "l32r a0, .mmu_wrap_irom_fn\n\t" + "movi a2, handle_hwdt\n\t" + "callx0 a0\n\t" +#endif + /* + * Use new calculated SYS stack from top. + * Call the entry point of the SDK code. + */ + "l32r a2, .sys_stack_first\n\t" + /* + * Stack cases: + * + * 1) Continuation context is in BSS. (noextra4k) + * g_pcont = get_noextra4k_g_pcont(); was &g_cont; + * + * 2) The continuation context is on the stack just after the reserved + * space for the ROM/eboot stack and before the SYS stack begins. + * All computations were done at top, save pointer to it now. + * g_pcont = CONT_STACK; + */ + "l32r a13, .pcont_stack\n\t" + "l32r a0, .get_noextra4k_g_pcont\n\t" + "l32r a14, .g_pcont\n\t" + // We now switch to the SYS stack the SDK will use + "l32i.n a1, a2, 0\n\t" // delayed load for pipeline + "l32i.n a13, a13, 0\n\t" + "callx0 a0\n\t" + "moveqz a2, a13, a2\n\t" + "s32i.n a2, a14, 0\n\t" + + /* + * Allow for running additional diagnotics supplied at link time. + */ + "l32r a0, .mmu_wrap_irom_fn\n\t" + "movi a2, hwdt_pre_sdk_init_icache\n\t" + "callx0 a0\n\t" + + // In case somebody cares, leave things as we found them + // - Restore ROM BSS zeros. + "movi a2, 0x3FFFE000\n\t" // ROM BSS Area + "movi a3, 0x0b30\n\t" // ROM BSS Size + "call0 ets_bzero\n\t" + + /* + * Up until this call, the heap at crash time has been available for + * analysis. This is needed for dumping the bearssl stack. Also, future + * improvements could possibly use hwdt_pre_sdk_init() to run other early + * diagnostic tools. + */ +#ifdef UMM_INIT_USE_IRAM + "l32r a0, .umm_init\n\t" +#else + "l32r a0, .mmu_wrap_irom_fn\n\t" + "l32r a2, .umm_init\n\t" +#endif + "callx0 a0\n\t" + + "l32r a3, .call_user_start\n\t" + "movi a0, 0x4000044c\n\t" + "jx a3\n\t" + ".size app_entry_redefinable, .-app_entry_redefinable\n\t" +); + +#if defined(DEBUG_ESP_HWDT_INFO) || defined(ROM_STACK_DUMP) +void debug_hwdt_init(void) { + /* + * Fill the ROM_STACK while it is not actively being used. + * + * I am thinking that during the time the sketch is running this block of + * memory could be used for a scratch buffer. + */ + for (size_t i = 0; i < g_rom_stack_A16_sz/sizeof(uint32_t); i++) { + g_rom_stack[i] = CONT_STACKGUARD; + } +} + +#else +void debug_hwdt_init(void) { +} +#endif + +}; + +#endif // end of #if defined(DEBUG_ESP_HWDT) || defined(DEBUG_ESP_HWDT_NOEXTRA4K) diff --git a/cores/esp8266/hwdt_app_entry.h b/cores/esp8266/hwdt_app_entry.h new file mode 100644 index 0000000000..12861527ea --- /dev/null +++ b/cores/esp8266/hwdt_app_entry.h @@ -0,0 +1,21 @@ +#if !defined(HWDT_STACK_DUMP_H) || defined(HWDT_VERIFY_HWDT_INFO) +#define HWDT_STACK_DUMP_H + +typedef struct hwdt_info_ { + uint32_t rom; + uint32_t sys; + uint32_t cont; + uint32_t bearssl; + uint32_t rom_api_reason; + uint32_t rtc_sys_reason; + uint32_t reset_reason; + uint32_t cont_integrity; + bool g_pcont_valid; +} hwdt_info_t; + +extern "C" void debug_hwdt_init(void); + +extern uint32_t *g_rom_stack; +extern hwdt_info_t hwdt_info; + +#endif diff --git a/cores/esp8266/interrupts.h b/cores/esp8266/interrupts.h index 0afcb161b7..300154bc2a 100644 --- a/cores/esp8266/interrupts.h +++ b/cores/esp8266/interrupts.h @@ -3,7 +3,7 @@ #include -// these auto classes wrap up xt_rsil so your code can be simplier, but can only be +// these auto classes wrap up xt_rsil so your code can be simpler, but can only be // used in an ino or cpp files. // InterruptLock is used when you want to completely disable interrupts diff --git a/cores/esp8266/libc_replacements.cpp b/cores/esp8266/libc_replacements.cpp index f9aa13d550..da03516b4f 100644 --- a/cores/esp8266/libc_replacements.cpp +++ b/cores/esp8266/libc_replacements.cpp @@ -47,27 +47,27 @@ extern "C" { -int ICACHE_RAM_ATTR _open_r (struct _reent* unused, const char *ptr, int mode) { +int IRAM_ATTR _open_r (struct _reent* unused, const char *ptr, int mode) { (void)unused; (void)ptr; (void)mode; return 0; } -int ICACHE_RAM_ATTR _close_r(struct _reent* unused, int file) { +int IRAM_ATTR _close_r(struct _reent* unused, int file) { (void)unused; (void)file; return 0; } -int ICACHE_RAM_ATTR _fstat_r(struct _reent* unused, int file, struct stat *st) { +int IRAM_ATTR _fstat_r(struct _reent* unused, int file, struct stat *st) { (void)unused; (void)file; st->st_mode = S_IFCHR; return 0; } -int ICACHE_RAM_ATTR _lseek_r(struct _reent* unused, int file, int ptr, int dir) { +int IRAM_ATTR _lseek_r(struct _reent* unused, int file, int ptr, int dir) { (void)unused; (void)file; (void)ptr; @@ -75,7 +75,7 @@ int ICACHE_RAM_ATTR _lseek_r(struct _reent* unused, int file, int ptr, int dir) return 0; } -int ICACHE_RAM_ATTR _read_r(struct _reent* unused, int file, char *ptr, int len) { +int IRAM_ATTR _read_r(struct _reent* unused, int file, char *ptr, int len) { (void)unused; (void)file; (void)ptr; @@ -83,7 +83,7 @@ int ICACHE_RAM_ATTR _read_r(struct _reent* unused, int file, char *ptr, int len) return 0; } -int ICACHE_RAM_ATTR _write_r(struct _reent* r, int file, char *ptr, int len) { +int IRAM_ATTR _write_r(struct _reent* r, int file, char *ptr, int len) { (void) r; int pos = len; if (file == STDOUT_FILENO) { @@ -95,9 +95,9 @@ int ICACHE_RAM_ATTR _write_r(struct _reent* r, int file, char *ptr, int len) { return len; } -int ICACHE_RAM_ATTR _putc_r(struct _reent* r, int c, FILE* file) __attribute__((weak)); +int IRAM_ATTR _putc_r(struct _reent* r, int c, FILE* file) __attribute__((weak)); -int ICACHE_RAM_ATTR _putc_r(struct _reent* r, int c, FILE* file) { +int IRAM_ATTR _putc_r(struct _reent* r, int c, FILE* file) { (void) r; if (file->_file == STDOUT_FILENO) { ets_putc(c); @@ -106,7 +106,7 @@ int ICACHE_RAM_ATTR _putc_r(struct _reent* r, int c, FILE* file) { return EOF; } -int ICACHE_RAM_ATTR puts(const char * str) { +int IRAM_ATTR puts(const char * str) { char c; while((c = *str) != 0) { ets_putc(c); @@ -117,7 +117,7 @@ int ICACHE_RAM_ATTR puts(const char * str) { } #undef putchar -int ICACHE_RAM_ATTR putchar(int c) { +int IRAM_ATTR putchar(int c) { ets_putc(c); return c; } diff --git a/cores/esp8266/mmu_iram.cpp b/cores/esp8266/mmu_iram.cpp index 488e596ccd..0193241890 100644 --- a/cores/esp8266/mmu_iram.cpp +++ b/cores/esp8266/mmu_iram.cpp @@ -19,13 +19,16 @@ #include "mmu_iram.h" #include +#define ICACHE_SIZE_32 1 +#define ICACHE_SIZE_16 0 + extern "C" { #if (MMU_ICACHE_SIZE == 0x4000) -#define SOC_CACHE_SIZE 0 // 16KB +#define SOC_CACHE_SIZE ICACHE_SIZE_16 #pragma message("ICACHE size 16K") #else -#define SOC_CACHE_SIZE 1 // 32KB +#define SOC_CACHE_SIZE ICACHE_SIZE_32 #endif #if (MMU_ICACHE_SIZE == 0x4000) @@ -80,7 +83,7 @@ extern "C" { * * "Cache_Read_Enable" is underdocumented. Main sources of information were from * rboot, zboot, https://richard.burtons.org/2015/06/12/esp8266-cache_read_enable/, - * and other places. And some additional expermentation. + * and other places. And some additional experimentation. * * Searching through the NONOS SDK shows nothing on this API; however, some * clues on what the NONOS SDK might be doing with ICACHE related calls can be @@ -119,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); } @@ -185,8 +160,82 @@ extern "C" void pinMode( uint8_t pin, uint8_t mode ) { __pinMode( pin, mode ); } +#else // #ifdef DEV_DEBUG_PRINT +extern void Cache_Read_Disable(void); #endif // #ifdef DEV_DEBUG_PRINT +#else // #if (MMU_ICACHE_SIZE == 0x4000) +extern void Cache_Read_Enable(uint8_t map, uint8_t p, uint8_t v); #endif // #if (MMU_ICACHE_SIZE == 0x4000) +/* + * 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 e372fa1047..e80c5ec348 100644 --- a/cores/esp8266/mmu_iram.h +++ b/cores/esp8266/mmu_iram.h @@ -26,13 +26,37 @@ extern "C" { #endif -//C This turns on range checking. Is this the value you want to trigger it? +// This turns on range checking. #ifdef DEBUG_ESP_CORE #define DEBUG_ESP_MMU #endif #if defined(CORE_MOCK) #define ets_uart_printf(...) do {} while(false) +#define XCHAL_INSTRAM0_VADDR 0x40000000 +#define XCHAL_INSTRAM1_VADDR 0x40100000 +#define XCHAL_INSTROM0_VADDR 0x40200000 +#else +#include // For config/core-isa.h +/* + Cautiously use XCHAL_..._VADDR values where possible. + While XCHAL_..._VADDR values in core-isa.h may define the Xtensa processor + CONFIG options, they are not always an indication of DRAM, IRAM, or ROM + size or position in the address space. +*/ +#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 /* @@ -45,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) @@ -69,34 +93,54 @@ DBG_MMU_FLUSH(0) #define DBG_MMU_PRINTF(...) do {} while(false) #endif // defined(DEV_DEBUG_PRINT) || defined(DEBUG_ESP_MMU) +/* + * This wrapper is for running code from IROM (flash) before the SDK starts. + * + * Wraps a `void fn(void)` call with calls to enable and disable iCACHE. + * Allows a function that resides in IROM to run before the SDK starts. + * + * Do not use once the SDK has started. + * + * Because the SDK initialization code has not run, nearly all the SDK functions + * are not safe to call. + * + * Note printing at this early stage is complicated. To gain more insight, + * review DEV_DEBUG_PRINT build path in mmu_iram.cpp. To handle strings stored + * in IROM, review printing method and comments in hwdt_app_entry.cpp. + * + */ +void IRAM_ATTR mmu_wrap_irom_fn(void (*fn)(void)); + static inline __attribute__((always_inline)) bool mmu_is_iram(const void *addr) { - #define IRAM_START 0x40100000UL + const uintptr_t iram_start = (uintptr_t)XCHAL_INSTRAM1_VADDR; #ifndef MMU_IRAM_SIZE #if defined(__GNUC__) && !defined(CORE_MOCK) #warning "MMU_IRAM_SIZE was undefined, setting to 0x8000UL!" #endif - #define MMU_IRAM_SIZE 0x8000UL + #define MMU_IRAM_SIZE 0x8000ul #endif - #define IRAM_END (IRAM_START + MMU_IRAM_SIZE) + const uintptr_t iram_end = iram_start + MMU_IRAM_SIZE; - return (IRAM_START <= (uintptr_t)addr && IRAM_END > (uintptr_t)addr); + return (iram_start <= (uintptr_t)addr && iram_end > (uintptr_t)addr); } static inline __attribute__((always_inline)) bool mmu_is_dram(const void *addr) { - #define DRAM_START 0x3FF80000UL - #define DRAM_END 0x40000000UL + const uintptr_t dram_start = 0x3FFE8000ul; + // The start of the Boot ROM sits at the end of DRAM. 0x40000000ul; + const uintptr_t dram_end = (uintptr_t)XCHAL_INSTRAM0_VADDR; - return (DRAM_START <= (uintptr_t)addr && DRAM_END > (uintptr_t)addr); + return (dram_start <= (uintptr_t)addr && dram_end > (uintptr_t)addr); } static inline __attribute__((always_inline)) bool mmu_is_icache(const void *addr) { - #define ICACHE_START 0x40200000UL - #define ICACHE_END (ICACHE_START + 0x100000UL) + extern void _irom0_text_end(void); + const uintptr_t icache_start = (uintptr_t)XCHAL_INSTROM0_VADDR; + const uintptr_t icache_end = (uintptr_t)_irom0_text_end; - return (ICACHE_START <= (uintptr_t)addr && ICACHE_END > (uintptr_t)addr); + return (icache_start <= (uintptr_t)addr && icache_end > (uintptr_t)addr); } #ifdef DEBUG_ESP_MMU @@ -122,13 +166,31 @@ bool mmu_is_icache(const void *addr) { /* * Some inlines to allow faster random access to non32bit access of iRAM or * iCACHE data elements. These remove the extra time and stack space that would - * have occured by relying on exception processing. + * have occurred by relying on exception processing. */ static inline __attribute__((always_inline)) uint8_t mmu_get_uint8(const void *p8) { ASSERT_RANGE_TEST_READ(p8); - uint32_t val = (*(uint32_t *)((uintptr_t)p8 & ~0x3)); - uint32_t pos = ((uintptr_t)p8 & 0x3) * 8; + // https://gist.github.com/shafik/848ae25ee209f698763cffee272a58f8#how-do-we-type-pun-correctly + // Comply with strict-aliasing rules. Using memcpy is a Standards suggested + // method for type punning. The compiler optimizer will replace the memcpy + // with an `l32i` instruction. Using __builtin_memcpy to ensure we get the + // effects of the compiler optimization and not some #define version of + // memcpy. + void *v32 = (void *)((uintptr_t)p8 & ~(uintptr_t)3u); + uint32_t val; + __builtin_memcpy(&val, v32, sizeof(uint32_t)); + // Use an empty ASM to reference the 32-bit value. This will block the + // compiler from immediately optimizing to an 8-bit or 16-bit load instruction + // against IRAM memory. (This approach was inspired by + // https://github.com/esp8266/Arduino/pull/7780#discussion_r548303374) + // This issue was seen when using a constant address with the GCC 10.3 + // compiler. + // As a general practice, I think referencing by way of Extended ASM R/W + // output register will stop the the compiler from reloading the value later + // as 8-bit load from IRAM. + asm volatile ("" :"+r"(val)); // inject 32-bit dependency + uint32_t pos = ((uintptr_t)p8 & 3u) * 8u; val >>= pos; return (uint8_t)val; } @@ -136,8 +198,11 @@ uint8_t mmu_get_uint8(const void *p8) { static inline __attribute__((always_inline)) uint16_t mmu_get_uint16(const uint16_t *p16) { ASSERT_RANGE_TEST_READ(p16); - uint32_t val = (*(uint32_t *)((uintptr_t)p16 & ~0x3)); - uint32_t pos = ((uintptr_t)p16 & 0x3) * 8; + void *v32 = (void *)((uintptr_t)p16 & ~(uintptr_t)0x3u); + uint32_t val; + __builtin_memcpy(&val, v32, sizeof(uint32_t)); + asm volatile ("" :"+r"(val)); + uint32_t pos = ((uintptr_t)p16 & 3u) * 8u; val >>= pos; return (uint16_t)val; } @@ -145,8 +210,11 @@ uint16_t mmu_get_uint16(const uint16_t *p16) { static inline __attribute__((always_inline)) int16_t mmu_get_int16(const int16_t *p16) { ASSERT_RANGE_TEST_READ(p16); - uint32_t val = (*(uint32_t *)((uintptr_t)p16 & ~0x3)); - uint32_t pos = ((uintptr_t)p16 & 0x3) * 8; + void *v32 = (void *)((uintptr_t)p16 & ~(uintptr_t)3u); + uint32_t val; + __builtin_memcpy(&val, v32, sizeof(uint32_t)); + asm volatile ("" :"+r"(val)); + uint32_t pos = ((uintptr_t)p16 & 3u) * 8u; val >>= pos; return (int16_t)val; } @@ -154,30 +222,43 @@ int16_t mmu_get_int16(const int16_t *p16) { static inline __attribute__((always_inline)) uint8_t mmu_set_uint8(void *p8, const uint8_t val) { ASSERT_RANGE_TEST_WRITE(p8); - uint32_t pos = ((uintptr_t)p8 & 0x3) * 8; + uint32_t pos = ((uintptr_t)p8 & 3u) * 8u; uint32_t sval = val << pos; - uint32_t valmask = 0x0FF << pos; + uint32_t valmask = 0x0FFu << pos; + + void *v32 = (void *)((uintptr_t)p8 & ~(uintptr_t)3u); + uint32_t ival; + __builtin_memcpy(&ival, v32, sizeof(uint32_t)); + asm volatile ("" :"+r"(ival)); - uint32_t *p32 = (uint32_t *)((uintptr_t)p8 & ~0x3); - uint32_t ival = *p32; ival &= (~valmask); ival |= sval; - *p32 = ival; + /* + This 32-bit dependency injection does not appear to be needed with the + current GCC 10.3; however, that could change in the future versions. Or, I + may not have the right test for it to fail. + */ + asm volatile ("" :"+r"(ival)); + __builtin_memcpy(v32, &ival, sizeof(uint32_t)); return val; } static inline __attribute__((always_inline)) uint16_t mmu_set_uint16(uint16_t *p16, const uint16_t val) { ASSERT_RANGE_TEST_WRITE(p16); - uint32_t pos = ((uintptr_t)p16 & 0x3) * 8; + uint32_t pos = ((uintptr_t)p16 & 3u) * 8u; uint32_t sval = val << pos; - uint32_t valmask = 0x0FFFF << pos; + uint32_t valmask = 0x0FFFFu << pos; + + void *v32 = (void *)((uintptr_t)p16 & ~(uintptr_t)3u); + uint32_t ival; + __builtin_memcpy(&ival, v32, sizeof(uint32_t)); + asm volatile ("" :"+r"(ival)); - uint32_t *p32 = (uint32_t *)((uintptr_t)p16 & ~0x3); - uint32_t ival = *p32; ival &= (~valmask); ival |= sval; - *p32 = ival; + asm volatile ("" :"+r"(ival)); + __builtin_memcpy(v32, &ival, sizeof(uint32_t)); return val; } @@ -185,32 +266,36 @@ static inline __attribute__((always_inline)) int16_t mmu_set_int16(int16_t *p16, const int16_t val) { ASSERT_RANGE_TEST_WRITE(p16); uint32_t sval = (uint16_t)val; - uint32_t pos = ((uintptr_t)p16 & 0x3) * 8; + uint32_t pos = ((uintptr_t)p16 & 3u) * 8u; sval <<= pos; - uint32_t valmask = 0x0FFFF << pos; + uint32_t valmask = 0x0FFFFu << pos; + + void *v32 = (void *)((uintptr_t)p16 & ~(uintptr_t)3u); + uint32_t ival; + __builtin_memcpy(&ival, v32, sizeof(uint32_t)); + asm volatile ("" :"+r"(ival)); - uint32_t *p32 = (uint32_t *)((uintptr_t)p16 & ~0x3); - uint32_t ival = *p32; ival &= (~valmask); ival |= sval; - *p32 = ival; + asm volatile ("" :"+r"(ival)); + __builtin_memcpy(v32, &ival, sizeof(uint32_t)); return val; } #if (MMU_IRAM_SIZE > 32*1024) && !defined(MMU_SEC_HEAP) -extern void _text_end(void); #define MMU_SEC_HEAP mmu_sec_heap() #define MMU_SEC_HEAP_SIZE mmu_sec_heap_size() static inline __attribute__((always_inline)) void *mmu_sec_heap(void) { - uint32_t sec_heap = (uint32_t)_text_end + 32; - return (void *)(sec_heap &= ~7); + extern void _text_end(void); + uintptr_t sec_heap = (uintptr_t)_text_end + (uintptr_t)32u; + return (void *)(sec_heap &= ~(uintptr_t)7u); } static inline __attribute__((always_inline)) size_t mmu_sec_heap_size(void) { - return (size_t)0xC000UL - ((size_t)mmu_sec_heap() - 0x40100000UL); + return (size_t)0xC000ul - ((uintptr_t)mmu_sec_heap() - (uintptr_t)XCHAL_INSTRAM1_VADDR); } #endif diff --git a/cores/esp8266/pgmspace.h b/cores/esp8266/pgmspace.h index af1a211a3f..23506a0959 100644 --- a/cores/esp8266/pgmspace.h +++ b/cores/esp8266/pgmspace.h @@ -3,6 +3,7 @@ // This file's contents have been moved to newlib. This file simply // includes the newlib pgmspace file as well as some ets headers // to preserve backwards compatibility +// current source: https://github.com/earlephilhower/newlib-xtensa/blob/xtensa-4_0_0-lock-arduino/newlib/libc/sys/xtensa/sys/pgmspace.h #include diff --git a/cores/esp8266/reboot_uart_dwnld.cpp b/cores/esp8266/reboot_uart_dwnld.cpp new file mode 100644 index 0000000000..d234c9c026 --- /dev/null +++ b/cores/esp8266/reboot_uart_dwnld.cpp @@ -0,0 +1,150 @@ +/* + ESP8266-specific implementation of the UART download mode + Copyright (c) 2021 Timo Wischer + 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 + + This implementation is based on the original implementation of the ROM. + It was shortened to reduce the memory usage. The complete version and the + development history can be found in: + https://github.com/twischer/Arduino/tree/reboot_uart_download_full + This might be useful in case of issues. + */ +#include "reboot_uart_dwnld.h" +#include +#include +#include + + +static inline uint32_t __rsil_1() { + uint32_t program_state; + asm volatile("rsil %0, 1" : "=r" (program_state)); + return program_state; +} + +static inline void __wsr_intenable(uint32_t interupt_enable) { + asm volatile("wsr.intenable %0" :: "r" (interupt_enable)); +} + +static inline void __wsr_litbase(uint32_t literal_base) { + asm volatile("wsr.litbase %0" :: "r" (literal_base)); +} + +static inline void __wsr_ps(uint32_t program_state) { + asm volatile("wsr.ps %0" :: "r" (program_state)); +} + +static inline void __wsr_vecbase(uint32_t vector_base) { + asm volatile("wsr.vecbase %0" :: "r" (vector_base)); +} + +[[noreturn]] void IRAM_ATTR esp8266UartDownloadMode() +{ + /* reverse engineered from system_restart_core() */ + /* Before disabling instruction cache and restoring instruction RAM to a + * power-on like state, SPI bus must be idle. + */ + Wait_SPI_Idle(flashchip); + + Cache_Read_Disable(); + /* This will disable the 32kB instruction cache and extend the IRAM by 32kB. + * Therefore the full 64kB of IRAM will be available for boot. + * Cache_Read_Enable() sets those bits but Cache_Read_Disable() does not clear + * them. On hardware reset those bits are cleared. Therefore clear them also + * for this reboot. + */ + CLEAR_PERI_REG_MASK(PERIPHS_DPORT_ICACHE_ENABLE, + ICACHE_ENABLE_FIRST_16K | ICACHE_ENABLE_SECOND_16K); + + /* reverse engineered from _ResetHandler() */ + /* disable all level 1 interrupts */ + __wsr_intenable(0); + /* Clear the literal base to use an offset of 0 for + * Load 32-bit PC-Relative(L32R) instructions + */ + __wsr_litbase(0); + asm volatile("rsync"); + + /* Set interrupt vector base address to system ROM */ + __wsr_vecbase(0x40000000); + /* Set interrupt level to 1. Therefore disable interrupts of level 1. + * Above levels like level 2,... might still be active if available + * on ESP8266. + */ + __rsil_1(); + + /* reverse engineered from _start() */ + /* Set stack pointer to upper end of data RAM */ + const uint32_t stack_pointer = 0x40000000; + asm volatile("mov a1, %0" :: "r" (stack_pointer)); + + /* Set the program state register + * Name Value Description + * Interrupt level disable 0 enable all interrupt levels + * Exception mode 0 normal operation + * User vector mode 1 user vector mode, exceptions need to switch stacks + * Privilege level 0 Set to Ring 0 + */ + __wsr_ps(0x20); + asm volatile("rsync"); + + /* reverse engineered from main() */ + const uint32_t uart_no = 0; + uartAttach(); + Uart_Init(uart_no); + ets_install_uart_printf(); + + /* reverse engineered from boot_from_something() */ + const uint16_t divlatch = uart_baudrate_detect(uart_no, 0); + rom_uart_div_modify(uart_no, divlatch); + UartDwnLdProc((uint8_t*)0x3fffa000, 0x2000, &user_start_fptr); + + /* reverse engineered from main() */ + if (user_start_fptr == NULL) { + if (boot_from_flash() != 0) { + ets_printf("boot_from_flash() failed\n"); + while (true); + } + } + + if (user_start_fptr) { + user_start_fptr(); + } + + ets_printf("user code done\n"); + ets_run(); +} + +[[noreturn]] void esp8266RebootIntoUartDownloadMode() +{ + /* reverse engineered from system_restart_local() */ + if (system_func1(0x4) == -1) { + clockgate_watchdog(0); + SET_PERI_REG_MASK(PERIPHS_DPORT_18, 0xffff00ff); + pm_open_rf(); + } + + user_uart_wait_tx_fifo_empty(0, 0x7a120); + user_uart_wait_tx_fifo_empty(1, 0x7a120); + ets_intr_lock(); + SET_PERI_REG_MASK(PERIPHS_DPORT_18, 0x7500); + CLEAR_PERI_REG_MASK(PERIPHS_DPORT_18, 0x7500); + SET_PERI_REG_MASK(PERIPHS_I2C_48, 0x2); + CLEAR_PERI_REG_MASK(PERIPHS_I2C_48, 0x2); + + esp8266UartDownloadMode(); +} diff --git a/cores/esp8266/reboot_uart_dwnld.h b/cores/esp8266/reboot_uart_dwnld.h new file mode 100644 index 0000000000..531471e2a0 --- /dev/null +++ b/cores/esp8266/reboot_uart_dwnld.h @@ -0,0 +1,23 @@ +/* + ESP8266-specific implementation of the UART download mode + Copyright (c) 2021 Timo Wischer + 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 + */ +#include + +[[noreturn]] void esp8266RebootIntoUartDownloadMode(); 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/spiffs/TECH_SPEC b/cores/esp8266/spiffs/TECH_SPEC index b4755a6dbc..300edb4912 100644 --- a/cores/esp8266/spiffs/TECH_SPEC +++ b/cores/esp8266/spiffs/TECH_SPEC @@ -113,7 +113,7 @@ Choosing the page size for the system involves many factors: - How fast must spiffs be - Other things impossible to find out -So, chosing the Optimal Page Size (tm) seems tricky, to say the least. Don't +So, choosing the Optimal Page Size (tm) seems tricky, to say the least. Don't fret - there is no optimal page size. This varies from how the target will use spiffs. Use the golden rule: diff --git a/cores/esp8266/spiffs/spiffs.h b/cores/esp8266/spiffs/spiffs.h index e659bf2f37..f9249b1eb4 100644 --- a/cores/esp8266/spiffs/spiffs.h +++ b/cores/esp8266/spiffs/spiffs.h @@ -695,7 +695,7 @@ s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh); * in this callback will mess things up for sure - do not do this. * This can be used to track where files are and move around during garbage * collection, which in turn can be used to build location tables in ram. - * Used in conjuction with SPIFFS_open_by_page this may improve performance + * Used in conjunction with SPIFFS_open_by_page this may improve performance * when opening a lot of files. * Must be invoked after mount. * @@ -710,7 +710,7 @@ s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func); * Maps the first level index lookup to a given memory map. * This will make reading big files faster, as the memory map will be used for * looking up data pages instead of searching for the indices on the physical - * medium. When mapping, all affected indicies are found and the information is + * medium. When mapping, all affected indices are found and the information is * copied to the array. * Whole file or only parts of it may be mapped. The index map will cover file * contents from argument offset until and including arguments (offset+len). diff --git a/cores/esp8266/spiffs/spiffs_nucleus.h b/cores/esp8266/spiffs/spiffs_nucleus.h index a8b4af062e..14ef32b926 100644 --- a/cores/esp8266/spiffs/spiffs_nucleus.h +++ b/cores/esp8266/spiffs/spiffs_nucleus.h @@ -353,7 +353,7 @@ extern "C" { if ((ph).span_ix != (spix)) return SPIFFS_ERR_DATA_SPAN_MISMATCH; -// check id, only visit matching objec ids +// check id, only visit matching object ids #define SPIFFS_VIS_CHECK_ID (1<<0) // report argument object id to visitor - else object lookup id is reported #define SPIFFS_VIS_CHECK_PH (1<<1) diff --git a/cores/esp8266/spiffs_api.h b/cores/esp8266/spiffs_api.h index 1ea04849e1..96b01358a6 100644 --- a/cores/esp8266/spiffs_api.h +++ b/cores/esp8266/spiffs_api.h @@ -26,8 +26,6 @@ */ #include #include "FS.h" -#undef max -#undef min #include "FSImpl.h" extern "C" { #include "spiffs/spiffs.h" @@ -390,10 +388,10 @@ class SPIFFSFileImpl : public FileImpl return result; } - size_t read(uint8_t* buf, size_t size) override + int read(uint8_t* buf, size_t size) override { CHECKFD(); - auto result = SPIFFS_read(_fs->getFs(), _fd, (void*) buf, size); + int result = SPIFFS_read(_fs->getFs(), _fd, (void*) buf, size); if (result < 0) { DEBUGV("SPIFFS_read rc=%d\r\n", result); return 0; diff --git a/cores/esp8266/stdlib_noniso.cpp b/cores/esp8266/stdlib_noniso.cpp new file mode 100644 index 0000000000..4fed9b615d --- /dev/null +++ b/cores/esp8266/stdlib_noniso.cpp @@ -0,0 +1,74 @@ +/* + stdlib_noniso.h - nonstandard (but useful) conversion functions + + Copyright (c) 2021 David Gauchard. 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 +*/ + +#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) noexcept +{ + str += --slen; + *str = 0; + do + { + auto mod = val % radix; + val /= radix; + *--str = mod + ((mod > 9) ? ('a' - 10) : '0'); + } while (--slen && val); + return val? nullptr: str; +} + +// lltoa fills str backwards and can return a pointer different from str +char* lltoa(long long val, char* str, int slen, unsigned int radix) noexcept +{ + bool neg; + if (val < 0) + { + val = -val; + neg = true; + } + else + { + neg = false; + } + char* ret = ulltoa(val, str, slen, radix); + if (neg) + { + if (ret == str || ret == nullptr) + return nullptr; + *--ret = '-'; + } + 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 053ea47948..0c8c488ed1 100644 --- a/cores/esp8266/stdlib_noniso.h +++ b/cores/esp8266/stdlib_noniso.h @@ -1,9 +1,9 @@ -/* - stdlib_noniso.h - nonstandard (but usefull) conversion functions +/* + stdlib_noniso.h - nonstandard (but useful) conversion functions Copyright (c) 2014 Ivan Grokhotkov. 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 @@ -22,34 +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); +#ifdef __cplusplus +#define __STDLIB_NONISO_NOEXCEPT noexcept +#else +#define __STDLIB_NONISO_NOEXCEPT +#endif -double atof(const char* s); +char* ltoa (long val, char *s, int radix) __STDLIB_NONISO_NOEXCEPT; -char* itoa (int val, char *s, int radix); +char* lltoa (long long val, char* str, int slen, unsigned int radix) __STDLIB_NONISO_NOEXCEPT; -char* ltoa (long val, char *s, int radix); +char* ultoa (unsigned long val, char *s, int radix) __STDLIB_NONISO_NOEXCEPT; -char* utoa (unsigned int val, char *s, int radix); +char* ulltoa (unsigned long long val, char* str, int slen, unsigned int radix) __STDLIB_NONISO_NOEXCEPT; -char* ultoa (unsigned long val, char *s, int radix); - -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 b9489c330f..970beada48 100644 --- a/cores/esp8266/time.cpp +++ b/cores/esp8266/time.cpp @@ -20,6 +20,28 @@ * synchronisation of the two through timeshift64 */ +#include + +// https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-time.c + +bool getLocalTime(struct tm * info, uint32_t ms) +{ + uint32_t start = millis(); + time_t now; + while((millis()-start) <= ms) { + time(&now); + localtime_r(&now, info); + if(info->tm_year > (2016 - 1900)){ + return true; + } + delay(10); + } + return false; +} + + +#if !defined(CORE_MOCK) + #include #include <../include/time.h> // See issue #6714 #include @@ -33,7 +55,6 @@ extern "C" { #include #include -#include // configTime() extern "C" { @@ -187,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]; @@ -207,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) @@ -214,6 +261,11 @@ void settimeofday_cb (const TrivialCB& cb) _settimeofday_cb = [cb](bool sntp) { (void)sntp; cb(); }; } +void settimeofday_cb (BoolCB&& cb) +{ + _settimeofday_cb = std::move(cb); +} + void settimeofday_cb (const BoolCB& cb) { _settimeofday_cb = cb; @@ -252,4 +304,6 @@ int settimeofday(const struct timeval* tv, const struct timezone* tz) return 0; } -}; +}; // extern "C" + +#endif // !defined(CORE_MOCK) diff --git a/cores/esp8266/uart.cpp b/cores/esp8266/uart.cpp index 47e7243abf..802072d438 100644 --- a/cores/esp8266/uart.cpp +++ b/cores/esp8266/uart.cpp @@ -41,6 +41,7 @@ * */ #include "Arduino.h" +#include "coredecls.h" #include #include "gdb_hooks.h" #include "uart.h" @@ -115,7 +116,7 @@ struct uart_ // called by ISR -inline size_t ICACHE_RAM_ATTR +inline size_t IRAM_ATTR uart_rx_fifo_available(const int uart_nr) { return (USS(uart_nr) >> USRXC) & 0xFF; @@ -144,7 +145,7 @@ uart_rx_available_unsafe(uart_t* uart) // Copy all the rx fifo bytes that fit into the rx buffer // called by ISR -inline void ICACHE_RAM_ATTR +inline void IRAM_ATTR uart_rx_copy_fifo_to_buffer_unsafe(uart_t* uart) { struct uart_rx_buffer_ *rx_buffer = uart->rx_buffer; @@ -240,6 +241,41 @@ uart_peek_char(uart_t* uart) return ret; } +// return number of byte accessible by uart_peek_buffer() +size_t uart_peek_available (uart_t* uart) +{ + // path for further optimization: + // - return already copied buffer pointer (= older data) + // - or return fifo when buffer is empty but then any move from fifo to + // buffer should be blocked until peek_consume is called + + ETS_UART_INTR_DISABLE(); + uart_rx_copy_fifo_to_buffer_unsafe(uart); + auto rpos = uart->rx_buffer->rpos; + auto wpos = uart->rx_buffer->wpos; + ETS_UART_INTR_ENABLE(); + if(wpos < rpos) + return uart->rx_buffer->size - rpos; + return wpos - rpos; +} + +// return a pointer to available data buffer (size = available()) +// semantic forbids any kind of read() between peekBuffer() and peekConsume() +const char* uart_peek_buffer (uart_t* uart) +{ + return (const char*)&uart->rx_buffer->buffer[uart->rx_buffer->rpos]; +} + +// consume bytes after use (see uart_peek_buffer) +void uart_peek_consume (uart_t* uart, size_t consume) +{ + ETS_UART_INTR_DISABLE(); + uart->rx_buffer->rpos += consume; + if (uart->rx_buffer->rpos >= uart->rx_buffer->size) + uart->rx_buffer->rpos -= uart->rx_buffer->size; + ETS_UART_INTR_ENABLE(); +} + int uart_read_char(uart_t* uart) { @@ -289,7 +325,7 @@ uart_read(uart_t* uart, char* userbuffer, size_t usersize) // instead of the uart_isr...uart_rx_copy_fifo_to_buffer_unsafe() // Since we've already read the bytes from the FIFO, can't use that // function directly and need to implement it bytewise here -static void ICACHE_RAM_ATTR uart_isr_handle_data(void* arg, uint8_t data) +static void IRAM_ATTR uart_isr_handle_data(void* arg, uint8_t data) { uart_t* uart = (uart_t*)arg; if(uart == NULL || !uart->rx_enabled) { @@ -370,7 +406,7 @@ uart_get_rx_buffer_size(uart_t* uart) } // The default ISR handler called when GDB is not enabled -void ICACHE_RAM_ATTR +void IRAM_ATTR uart_isr(void * arg, void * frame) { (void) frame; @@ -458,13 +494,13 @@ uart_stop_isr(uart_t* uart) -tools/sdk/uart_register.h -cores/esp8266/esp8266_peri.h */ -inline size_t +inline __attribute__((always_inline)) size_t uart_tx_fifo_available(const int uart_nr) { return (USS(uart_nr) >> USTXC) & 0xff; } -inline bool +inline __attribute__((always_inline)) bool uart_tx_fifo_full(const int uart_nr) { return uart_tx_fifo_available(uart_nr) >= 0x7f; @@ -531,7 +567,7 @@ uart_wait_tx_empty(uart_t* uart) return; while(uart_tx_fifo_available(uart->uart_nr) > 0) - delay(0); + esp_yield(); } @@ -660,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); } @@ -725,11 +761,11 @@ uart_uninit(uart_t* uart) free(uart); } -void +bool uart_swap(uart_t* uart, int tx_pin) { if(uart == NULL) - return; + return false; switch(uart->uart_nr) { @@ -740,19 +776,17 @@ uart_swap(uart_t* uart, int tx_pin) { pinMode(uart->tx_pin, INPUT); uart->tx_pin = 15; + pinMode(uart->tx_pin, FUNCTION_4); } if(uart->rx_enabled) //RX { pinMode(uart->rx_pin, INPUT); uart->rx_pin = 13; + pinMode(uart->rx_pin, FUNCTION_4); } - if(uart->tx_enabled) - pinMode(uart->tx_pin, FUNCTION_4); //TX - - if(uart->rx_enabled) - pinMode(uart->rx_pin, FUNCTION_4); //RX IOSWAP |= (1 << IOSWAPU0); + return true; } else { @@ -760,19 +794,17 @@ uart_swap(uart_t* uart, int tx_pin) { pinMode(uart->tx_pin, INPUT); uart->tx_pin = (tx_pin == 2)?2:1; + pinMode(uart->tx_pin, (tx_pin == 2)?FUNCTION_4:SPECIAL); } if(uart->rx_enabled) //RX { pinMode(uart->rx_pin, INPUT); uart->rx_pin = 3; + pinMode(3, SPECIAL); } - if(uart->tx_enabled) - pinMode(uart->tx_pin, (tx_pin == 2)?FUNCTION_4:SPECIAL); //TX - - if(uart->rx_enabled) - pinMode(3, SPECIAL); //RX IOSWAP &= ~(1 << IOSWAPU0); + return true; } break; case UART1: @@ -781,30 +813,30 @@ uart_swap(uart_t* uart, int tx_pin) default: break; } + return false; } -void +bool uart_set_tx(uart_t* uart, int tx_pin) { if(uart == NULL) - return; + return false; switch(uart->uart_nr) { case UART0: if(uart->tx_enabled) { - if (uart->tx_pin == 1 && tx_pin == 2) + if (uart->tx_pin == tx_pin) { - pinMode(uart->tx_pin, INPUT); - uart->tx_pin = 2; - pinMode(uart->tx_pin, FUNCTION_4); + return true; } - else if (uart->tx_pin == 2 && tx_pin != 2) + else if (tx_pin == 1 || tx_pin == 2) { pinMode(uart->tx_pin, INPUT); - uart->tx_pin = 1; - pinMode(uart->tx_pin, SPECIAL); + uart->tx_pin = tx_pin; + pinMode(uart->tx_pin, tx_pin == 1 ? SPECIAL : FUNCTION_4); + return true; } } @@ -815,33 +847,54 @@ uart_set_tx(uart_t* uart, int tx_pin) default: break; } + return false; } -void +bool uart_set_pins(uart_t* uart, int tx, int rx) { if(uart == NULL) - return; + return false; + + if(uart->uart_nr != UART0) // Only UART0 allows pin changes + return false; - if(uart->uart_nr == UART0) // Only UART0 allows pin changes + if(uart->tx_enabled && uart->tx_pin != tx) { - if(uart->tx_enabled && uart->tx_pin != tx) + if( rx == 13 && tx == 15) + { + if (!uart_swap(uart, 15)) + return false; + } + else if (rx == 3 && (tx == 1 || tx == 2)) { - if( rx == 13 && tx == 15) + if (uart->rx_pin != rx) { - uart_swap(uart, 15); + if (!uart_swap(uart, tx)) + return false; } - else if (rx == 3 && (tx == 1 || tx == 2)) + else { - if (uart->rx_pin != rx) - uart_swap(uart, tx); - else - uart_set_tx(uart, tx); + if (!uart_set_tx(uart, tx)) + return false; } } - if(uart->rx_enabled && uart->rx_pin != rx && rx == 13 && tx == 15) - uart_swap(uart, 15); + else + return false; + } + + if (uart->rx_enabled && uart->rx_pin != rx) + { + if (rx == 13 && tx == 15) + { + if (!uart_swap(uart, 15)) + return false; + } + else + return false; } + + return true; } @@ -891,23 +944,23 @@ uart_ignore_char(char c) (void) c; } -inline void +inline __attribute__((always_inline)) void uart_write_char_delay(const int uart_nr, char c) { while(uart_tx_fifo_full(uart_nr)) - delay(0); + esp_yield(); USF(uart_nr) = c; } -static void +static void IRAM_ATTR uart0_write_char(char c) { uart_write_char_delay(0, c); } -static void +static void IRAM_ATTR uart1_write_char(char c) { uart_write_char_delay(1, c); diff --git a/cores/esp8266/uart.h b/cores/esp8266/uart.h index 4871b5e93a..8e08da0219 100644 --- a/cores/esp8266/uart.h +++ b/cores/esp8266/uart.h @@ -116,9 +116,9 @@ typedef struct uart_ uart_t; uart_t* uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, size_t rx_size, bool invert); void uart_uninit(uart_t* uart); -void uart_swap(uart_t* uart, int tx_pin); -void uart_set_tx(uart_t* uart, int tx_pin); -void uart_set_pins(uart_t* uart, int tx, int rx); +bool uart_swap(uart_t* uart, int tx_pin); +bool uart_set_tx(uart_t* uart, int tx_pin); +bool uart_set_pins(uart_t* uart, int tx, int rx); bool uart_tx_enabled(uart_t* uart); bool uart_rx_enabled(uart_t* uart); @@ -147,6 +147,16 @@ int uart_get_debug(); void uart_start_detect_baudrate(int uart_nr); int uart_detect_baudrate(int uart_nr); +// return number of byte accessible by peekBuffer() +size_t uart_peek_available (uart_t* uart); + +// return a pointer to available data buffer (size = available()) +// semantic forbids any kind of read() before calling peekConsume() +const char* uart_peek_buffer (uart_t* uart); + +// consume bytes after use (see peekBuffer) +void uart_peek_consume (uart_t* uart, size_t consume); + uint8_t uart_get_bit_length(const int uart_nr); #if defined (__cplusplus) diff --git a/cores/esp8266/umm_malloc/Notes.h b/cores/esp8266/umm_malloc/Notes.h index e9d96b1e83..5d076c4b67 100644 --- a/cores/esp8266/umm_malloc/Notes.h +++ b/cores/esp8266/umm_malloc/Notes.h @@ -247,5 +247,110 @@ Enhancement ideas: 2. A build option to not have printing, from umm_info() compiled in. This can save on the execution time spent with interrupts disabled. +*/ + +/* + Dec 29, 2021 + Upstream umm_malloc at git hash id 4dac43c3be7a7470dd669323021ba238081da18e + processed all project files with the style program uncrustify. + + This PR updates our ported version of umm_malloc processed with "uncrustify". + This should make subsequent merges of upstream into this port easier. + + This also makes the style more consistant through umm_malloc. + + Some edits to source files was needed to get uncrustify to work. + 1) macros with "if"s need to be of the form "if ( blah ) { } " curley braces + are needed for it to parse correctly + 2) These "#ifdef __cplusplus" also had to be commented out while running to + avoid parser confusion. + ``` + #ifdef __cplusplus + extern "C" { + #endif + ``` + and + ``` + #ifdef __cplusplus + } + #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/README.md b/cores/esp8266/umm_malloc/README.md index f81aa09ccc..5bc5bbd348 100644 --- a/cores/esp8266/umm_malloc/README.md +++ b/cores/esp8266/umm_malloc/README.md @@ -126,7 +126,7 @@ later. The result is that a block of memory on the free list uses just 8 bytes instead of 16. -In fact, we go even one step futher when we realize that the free block +In fact, we go even one step further when we realize that the free block index values are available to store data when the block is allocated. The overhead of an allocated block is therefore just 4 bytes. @@ -205,7 +205,7 @@ described. allocated for use by the upper block. While we're talking about "upper" and "lower" blocks, we should make -a comment about adresses. In the diagrams, a block higher up in the +a comment about addresses. In the diagrams, a block higher up in the picture is at a lower address. And the blocks grow downwards their block index increases as does their physical address. @@ -347,7 +347,7 @@ nf |*?? | ?? | ?? | cf | nf | ?? | ?? | ?? | pf | ``` This one is prety easy too, except we don't need to mess with the -free list indexes at all becasue we'll allocate the new block at the +free list indexes at all because we'll allocate the new block at the end of the current free block. We do, however have to adjust the indexes in cf, c, and n. @@ -539,5 +539,5 @@ BEFORE AFTER +----+----+----+----+ +----+----+----+----+ ``` -Then we call free() with the adress of the data portion of the new +Then we call free() with the address of the data portion of the new block (s) which adds it to the free list. diff --git a/cores/esp8266/umm_malloc/dbglog/README.txt b/cores/esp8266/umm_malloc/dbglog/README.txt index 54f86e148c..90a81d382d 100644 --- a/cores/esp8266/umm_malloc/dbglog/README.txt +++ b/cores/esp8266/umm_malloc/dbglog/README.txt @@ -1 +1,2 @@ Downloaded from: https://github.com/rhempel/c-helper-macros/tree/develop +Applied uncrustify to be consistent with the rest of the umm_malloc files. diff --git a/cores/esp8266/umm_malloc/dbglog/dbglog.h b/cores/esp8266/umm_malloc/dbglog/dbglog.h index 575a7c6b13..21f10cfc9e 100644 --- a/cores/esp8266/umm_malloc/dbglog/dbglog.h +++ b/cores/esp8266/umm_malloc/dbglog/dbglog.h @@ -13,7 +13,7 @@ * to set the trace level #define DBGLOG_LEVEL x * * To update which of the DBGLOG macros are compiled in, you must redefine the - * DBGLOG_LEVEL macro and the inlcude the dbglog.h file again, like this: + * DBGLOG_LEVEL macro and the include the dbglog.h file again, like this: * * #undef DBGLOG_LEVEL * #define DBGLOG_LEVEL 6 @@ -50,11 +50,11 @@ #undef DBGLOG_FORCE #ifndef DBGLOG_LEVEL -# define DBGLOG_LEVEL 0 +#define DBGLOG_LEVEL 0 #endif #ifndef DBGLOG_FUNCTION -# define DBGLOG_FUNCTION printf +#define DBGLOG_FUNCTION printf #endif #define DBGLOG_32_BIT_PTR(x) ((uint32_t)(((uintptr_t)(x)) & 0xffffffff)) @@ -62,39 +62,39 @@ /* ------------------------------------------------------------------------- */ #if DBGLOG_LEVEL >= 6 -# define DBGLOG_TRACE(format, ...) DBGLOG_FUNCTION(format, ## __VA_ARGS__) +#define DBGLOG_TRACE(format, ...) DBGLOG_FUNCTION(format,##__VA_ARGS__) #else -# define DBGLOG_TRACE(format, ...) +#define DBGLOG_TRACE(format, ...) #endif #if DBGLOG_LEVEL >= 5 -# define DBGLOG_DEBUG(format, ...) DBGLOG_FUNCTION(format, ## __VA_ARGS__) +#define DBGLOG_DEBUG(format, ...) DBGLOG_FUNCTION(format,##__VA_ARGS__) #else -# define DBGLOG_DEBUG(format, ...) +#define DBGLOG_DEBUG(format, ...) #endif #if DBGLOG_LEVEL >= 4 -# define DBGLOG_CRITICAL(format, ...) DBGLOG_FUNCTION(format, ## __VA_ARGS__) +#define DBGLOG_CRITICAL(format, ...) DBGLOG_FUNCTION(format,##__VA_ARGS__) #else -# define DBGLOG_CRITICAL(format, ...) +#define DBGLOG_CRITICAL(format, ...) #endif #if DBGLOG_LEVEL >= 3 -# define DBGLOG_ERROR(format, ...) DBGLOG_FUNCTION(format, ## __VA_ARGS__) +#define DBGLOG_ERROR(format, ...) DBGLOG_FUNCTION(format,##__VA_ARGS__) #else -# define DBGLOG_ERROR(format, ...) +#define DBGLOG_ERROR(format, ...) #endif #if DBGLOG_LEVEL >= 2 -# define DBGLOG_WARNING(format, ...) DBGLOG_FUNCTION(format, ## __VA_ARGS__) +#define DBGLOG_WARNING(format, ...) DBGLOG_FUNCTION(format,##__VA_ARGS__) #else -# define DBGLOG_WARNING(format, ...) +#define DBGLOG_WARNING(format, ...) #endif #if DBGLOG_LEVEL >= 1 -# define DBGLOG_INFO(format, ...) DBGLOG_FUNCTION(format, ## __VA_ARGS__) +#define DBGLOG_INFO(format, ...) DBGLOG_FUNCTION(format,##__VA_ARGS__) #else -# define DBGLOG_INFO(format, ...) +#define DBGLOG_INFO(format, ...) #endif -#define DBGLOG_FORCE(force, format, ...) {if(force) {DBGLOG_FUNCTION(format, ## __VA_ARGS__);}} +#define DBGLOG_FORCE(force, format, ...) {if (force) {DBGLOG_FUNCTION(format,##__VA_ARGS__);}} diff --git a/cores/esp8266/umm_malloc/umm_heap_select.h b/cores/esp8266/umm_malloc/umm_heap_select.h index 4c9150819f..282e87b8ff 100644 --- a/cores/esp8266/umm_malloc/umm_heap_select.h +++ b/cores/esp8266/umm_malloc/umm_heap_select.h @@ -3,6 +3,18 @@ #include +#ifndef ALWAYS_INLINE +#define ALWAYS_INLINE inline __attribute__ ((always_inline)) +#endif + +// Use FORCE_ALWAYS_INLINE to ensure HeapSelect... constructor/deconstructor +// are placed in IRAM +#ifdef FORCE_ALWAYS_INLINE_HEAP_SELECT +#define MAYBE_ALWAYS_INLINE ALWAYS_INLINE +#else +#define MAYBE_ALWAYS_INLINE +#endif + /* This class is modeled after interrupts.h @@ -20,58 +32,77 @@ class HeapSelect { public: #if (UMM_NUM_HEAPS == 1) - HeapSelect(size_t id) { (void)id; } - ~HeapSelect() {} +MAYBE_ALWAYS_INLINE +HeapSelect(size_t id) { + (void)id; +} +MAYBE_ALWAYS_INLINE +~HeapSelect() { +} #else - HeapSelect(size_t id) : _heap_id(umm_get_current_heap_id()) { +MAYBE_ALWAYS_INLINE +HeapSelect(size_t id) : _heap_id(umm_get_current_heap_id()) { umm_set_heap_by_id(id); - } +} - ~HeapSelect() { +MAYBE_ALWAYS_INLINE +~HeapSelect() { umm_set_heap_by_id(_heap_id); - } +} protected: - size_t _heap_id; +size_t _heap_id; #endif }; class HeapSelectIram { public: #ifdef UMM_HEAP_IRAM - HeapSelectIram() : _heap_id(umm_get_current_heap_id()) { +MAYBE_ALWAYS_INLINE +HeapSelectIram() : _heap_id(umm_get_current_heap_id()) { umm_set_heap_by_id(UMM_HEAP_IRAM); - } +} - ~HeapSelectIram() { +MAYBE_ALWAYS_INLINE +~HeapSelectIram() { umm_set_heap_by_id(_heap_id); - } +} protected: - size_t _heap_id; +size_t _heap_id; #else - HeapSelectIram() {} - ~HeapSelectIram() {} +MAYBE_ALWAYS_INLINE +HeapSelectIram() { +} +MAYBE_ALWAYS_INLINE +~HeapSelectIram() { +} #endif }; class HeapSelectDram { public: #if (UMM_NUM_HEAPS == 1) - HeapSelectDram() {} - ~HeapSelectDram() {} +MAYBE_ALWAYS_INLINE +HeapSelectDram() { +} +MAYBE_ALWAYS_INLINE +~HeapSelectDram() { +} #else - HeapSelectDram() : _heap_id(umm_get_current_heap_id()) { +MAYBE_ALWAYS_INLINE +HeapSelectDram() : _heap_id(umm_get_current_heap_id()) { umm_set_heap_by_id(UMM_HEAP_DRAM); - } +} - ~HeapSelectDram() { +MAYBE_ALWAYS_INLINE +~HeapSelectDram() { umm_set_heap_by_id(_heap_id); - } +} protected: - size_t _heap_id; +size_t _heap_id; #endif }; diff --git a/cores/esp8266/umm_malloc/umm_info.c b/cores/esp8266/umm_malloc/umm_info.c index 0860fe79ea..c0ebb7948f 100644 --- a/cores/esp8266/umm_malloc/umm_info.c +++ b/cores/esp8266/umm_malloc/umm_info.c @@ -25,235 +25,244 @@ // UMM_HEAP_INFO ummHeapInfo; -void *umm_info( void *ptr, bool force ) { - UMM_CRITICAL_DECL(id_info); +void *umm_info(void *ptr, bool force) { + UMM_CRITICAL_DECL(id_info); - UMM_INIT_HEAP; + UMM_CHECK_INITIALIZED(); - uint16_t blockNo = 0; + uint16_t blockNo = 0; - /* Protect the critical section... */ - UMM_CRITICAL_ENTRY(id_info); + /* Protect the critical section... */ + UMM_CRITICAL_ENTRY(id_info); - umm_heap_context_t *_context = umm_get_current_heap(); + umm_heap_context_t *_context = umm_get_current_heap(); - /* - * Clear out all of the entries in the ummHeapInfo structure before doing - * any calculations.. - */ - memset( &_context->info, 0, sizeof( _context->info ) ); + /* + * Clear out all of the entries in the ummHeapInfo structure before doing + * any calculations.. + */ + memset(&_context->info, 0, sizeof(_context->info)); - DBGLOG_FORCE( force, "\n" ); - DBGLOG_FORCE( force, "+----------+-------+--------+--------+-------+--------+--------+\n" ); - DBGLOG_FORCE( force, "|0x%08lx|B %5d|NB %5d|PB %5d|Z %5d|NF %5d|PF %5d|\n", - DBGLOG_32_BIT_PTR(&UMM_BLOCK(blockNo)), - blockNo, - UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK, - UMM_PBLOCK(blockNo), - (UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK )-blockNo, - UMM_NFREE(blockNo), - UMM_PFREE(blockNo) ); + DBGLOG_FORCE(force, "\n"); + DBGLOG_FORCE(force, "+----------+-------+--------+--------+-------+--------+--------+\n"); + DBGLOG_FORCE(force, "|0x%08lx|B %5d|NB %5d|PB %5d|Z %5d|NF %5d|PF %5d|\n", + DBGLOG_32_BIT_PTR(&UMM_BLOCK(blockNo)), + blockNo, + UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK, + UMM_PBLOCK(blockNo), + (UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK) - blockNo, + UMM_NFREE(blockNo), + UMM_PFREE(blockNo)); - /* - * Now loop through the block lists, and keep track of the number and size - * of used and free blocks. The terminating condition is an nb pointer with - * a value of zero... - */ + /* + * Now loop through the block lists, and keep track of the number and size + * of used and free blocks. The terminating condition is an nb pointer with + * a value of zero... + */ - blockNo = UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK; + blockNo = UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK; - while( UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK ) { - size_t curBlocks = (UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK )-blockNo; + while (UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK) { + size_t curBlocks = (UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK) - blockNo; - ++_context->info.totalEntries; - _context->info.totalBlocks += curBlocks; + ++_context->info.totalEntries; + _context->info.totalBlocks += curBlocks; - /* Is this a free block? */ + /* Is this a free block? */ - if( UMM_NBLOCK(blockNo) & UMM_FREELIST_MASK ) { - ++_context->info.freeEntries; - _context->info.freeBlocks += curBlocks; - _context->info.freeBlocksSquared += (curBlocks * curBlocks); + if (UMM_NBLOCK(blockNo) & UMM_FREELIST_MASK) { + ++_context->info.freeEntries; + _context->info.freeBlocks += curBlocks; + _context->info.freeBlocksSquared += (curBlocks * curBlocks); - if (_context->info.maxFreeContiguousBlocks < curBlocks) { - _context->info.maxFreeContiguousBlocks = curBlocks; - } + if (_context->info.maxFreeContiguousBlocks < curBlocks) { + _context->info.maxFreeContiguousBlocks = curBlocks; + } - DBGLOG_FORCE( force, "|0x%08lx|B %5d|NB %5d|PB %5d|Z %5u|NF %5d|PF %5d|\n", - DBGLOG_32_BIT_PTR(&UMM_BLOCK(blockNo)), - blockNo, - UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK, - UMM_PBLOCK(blockNo), - (uint16_t)curBlocks, - UMM_NFREE(blockNo), - UMM_PFREE(blockNo) ); + DBGLOG_FORCE(force, "|0x%08lx|B %5d|NB %5d|PB %5d|Z %5u|NF %5d|PF %5d|\n", + DBGLOG_32_BIT_PTR(&UMM_BLOCK(blockNo)), + blockNo, + UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK, + UMM_PBLOCK(blockNo), + (uint16_t)curBlocks, + UMM_NFREE(blockNo), + UMM_PFREE(blockNo)); - /* Does this block address match the ptr we may be trying to free? */ + /* Does this block address match the ptr we may be trying to free? */ - if( ptr == &UMM_BLOCK(blockNo) ) { + if (ptr == &UMM_BLOCK(blockNo)) { - /* Release the critical section... */ - UMM_CRITICAL_EXIT(id_info); + /* Release the critical section... */ + UMM_CRITICAL_EXIT(id_info); - return( ptr ); - } - } else { - ++_context->info.usedEntries; - _context->info.usedBlocks += curBlocks; - - DBGLOG_FORCE( force, "|0x%08lx|B %5d|NB %5d|PB %5d|Z %5u|\n", - DBGLOG_32_BIT_PTR(&UMM_BLOCK(blockNo)), - blockNo, - UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK, - UMM_PBLOCK(blockNo), - (uint16_t)curBlocks ); + return ptr; + } + } else { + ++_context->info.usedEntries; + _context->info.usedBlocks += curBlocks; + + DBGLOG_FORCE(force, "|0x%08lx|B %5d|NB %5d|PB %5d|Z %5u|\n", + DBGLOG_32_BIT_PTR(&UMM_BLOCK(blockNo)), + blockNo, + UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK, + UMM_PBLOCK(blockNo), + (uint16_t)curBlocks); + } + + blockNo = UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK; } - blockNo = UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK; - } - - /* - * The very last block is used as a placeholder to indicate that - * there are no more blocks in the heap, so it cannot be used - * for anything - at the same time, the size of this block must - * ALWAYS be exactly 1 ! - */ - - DBGLOG_FORCE( force, "|0x%08lx|B %5d|NB %5d|PB %5d|Z %5d|NF %5d|PF %5d|\n", - DBGLOG_32_BIT_PTR(&UMM_BLOCK(blockNo)), - blockNo, - UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK, - UMM_PBLOCK(blockNo), - UMM_NUMBLOCKS-blockNo, - UMM_NFREE(blockNo), - UMM_PFREE(blockNo) ); - - DBGLOG_FORCE( force, "+----------+-------+--------+--------+-------+--------+--------+\n" ); - - DBGLOG_FORCE( force, "Total Entries %5d Used Entries %5d Free Entries %5d\n", - _context->info.totalEntries, - _context->info.usedEntries, - _context->info.freeEntries ); - - DBGLOG_FORCE( force, "Total Blocks %5d Used Blocks %5d Free Blocks %5d\n", - _context->info.totalBlocks, - _context->info.usedBlocks, - _context->info.freeBlocks ); - - DBGLOG_FORCE( force, "+--------------------------------------------------------------+\n" ); - - DBGLOG_FORCE( force, "Usage Metric: %5d\n", umm_usage_metric_core(_context)); - DBGLOG_FORCE( force, "Fragmentation Metric: %5d\n", umm_fragmentation_metric_core(_context)); - - DBGLOG_FORCE( force, "+--------------------------------------------------------------+\n" ); - -#if defined(UMM_STATS) || defined(UMM_STATS_FULL) -#if !defined(UMM_INLINE_METRICS) - if (_context->info.freeBlocks == _context->stats.free_blocks) { - DBGLOG_FORCE( force, "heap info Free blocks and heap statistics Free blocks match.\n"); - } else { - DBGLOG_FORCE( force, "\nheap info Free blocks %5d != heap statistics Free Blocks %5d\n\n", - _context->info.freeBlocks, - _context->stats.free_blocks ); - } - DBGLOG_FORCE( force, "+--------------------------------------------------------------+\n" ); -#endif + /* + * The very last block is used as a placeholder to indicate that + * there are no more blocks in the heap, so it cannot be used + * for anything - at the same time, the size of this block must + * ALWAYS be exactly 1 ! + */ + + DBGLOG_FORCE(force, "|0x%08lx|B %5d|NB %5d|PB %5d|Z %5d|NF %5d|PF %5d|\n", + DBGLOG_32_BIT_PTR(&UMM_BLOCK(blockNo)), + blockNo, + UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK, + UMM_PBLOCK(blockNo), + UMM_NUMBLOCKS - blockNo, + UMM_NFREE(blockNo), + UMM_PFREE(blockNo)); + + DBGLOG_FORCE(force, "+----------+-------+--------+--------+-------+--------+--------+\n"); + + DBGLOG_FORCE(force, "Total Entries %5d Used Entries %5d Free Entries %5d\n", + _context->info.totalEntries, + _context->info.usedEntries, + _context->info.freeEntries); + + DBGLOG_FORCE(force, "Total Blocks %5d Used Blocks %5d Free Blocks %5d\n", + _context->info.totalBlocks, + _context->info.usedBlocks, + _context->info.freeBlocks); + + DBGLOG_FORCE(force, "+--------------------------------------------------------------+\n"); + + DBGLOG_FORCE(force, "Usage Metric: %5d\n", umm_usage_metric_core(_context)); + DBGLOG_FORCE(force, "Fragmentation Metric: %5d\n", umm_fragmentation_metric_core(_context)); + + DBGLOG_FORCE(force, "+--------------------------------------------------------------+\n"); + + #if defined(UMM_STATS) || defined(UMM_STATS_FULL) + #if !defined(UMM_INLINE_METRICS) + if (_context->info.freeBlocks == _context->stats.free_blocks) { + DBGLOG_FORCE(force, "heap info Free blocks and heap statistics Free blocks match.\n"); + } else { + DBGLOG_FORCE(force, "\nheap info Free blocks %5d != heap statistics Free Blocks %5d\n\n", + _context->info.freeBlocks, + _context->stats.free_blocks); + } + DBGLOG_FORCE(force, "+--------------------------------------------------------------+\n"); + #endif - umm_print_stats(force); -#endif + umm_print_stats(force); + #endif - /* Release the critical section... */ - UMM_CRITICAL_EXIT(id_info); + /* Release the critical section... */ + UMM_CRITICAL_EXIT(id_info); - return( NULL ); + return NULL; } /* ------------------------------------------------------------------------ */ -size_t umm_free_heap_size_core( umm_heap_context_t *_context ) { - return (size_t)_context->info.freeBlocks * sizeof(umm_block); +size_t umm_free_heap_size_core(umm_heap_context_t *_context) { + return (size_t)_context->info.freeBlocks * sizeof(umm_block); } -size_t umm_free_heap_size( void ) { -#ifndef UMM_INLINE_METRICS - umm_info(NULL, false); -#endif +/* + 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); + #endif - return umm_free_heap_size_core(umm_get_current_heap()); + return umm_free_heap_size_core(umm_get_current_heap()); } -//C Breaking change in upstream umm_max_block_size() was changed to -//C umm_max_free_block_size() keeping old function name for (dot) releases. -//C TODO: update at next major release. -//C size_t umm_max_free_block_size( void ) { -size_t umm_max_block_size_core( umm_heap_context_t *_context ) { - return _context->info.maxFreeContiguousBlocks * sizeof(umm_block); +// C Breaking change in upstream umm_max_block_size() was changed to +// C umm_max_free_block_size() keeping old function name for (dot) releases. +// C TODO: update at next major release. +// C size_t umm_max_free_block_size( void ) { +size_t umm_max_block_size_core(umm_heap_context_t *_context) { + return _context->info.maxFreeContiguousBlocks * sizeof(umm_block); } -size_t umm_max_block_size( void ) { - umm_info(NULL, false); - return umm_max_block_size_core(umm_get_current_heap()); +size_t umm_max_block_size(void) { + umm_info(NULL, false); + return umm_max_block_size_core(umm_get_current_heap()); } /* Without build option UMM_INLINE_METRICS, calls to umm_usage_metric() or - umm_fragmentation_metric() must to be preceeded by a call to umm_info(NULL, false) + umm_fragmentation_metric() must to be preceded by a call to umm_info(NULL, false) for updated results. */ -int umm_usage_metric_core( umm_heap_context_t *_context ) { -//C Note, umm_metrics also appears in the upstrean w/o definition. I suspect it is suppose to be ummHeapInfo. - // DBGLOG_DEBUG( "usedBlocks %d totalBlocks %d\n", umm_metrics.usedBlocks, ummHeapInfo.totalBlocks); - DBGLOG_DEBUG( "usedBlocks %d totalBlocks %d\n", _context->info.usedBlocks, _context->info.totalBlocks); - if (_context->info.freeBlocks) - return (int)((_context->info.usedBlocks * 100)/(_context->info.freeBlocks)); - - return -1; // no freeBlocks +int umm_usage_metric_core(umm_heap_context_t *_context) { +// C Note, umm_metrics also appears in the upstrean w/o definition. I suspect it is suppose to be ummHeapInfo. + // DBGLOG_DEBUG( "usedBlocks %d totalBlocks %d\n", umm_metrics.usedBlocks, ummHeapInfo.totalBlocks); + DBGLOG_DEBUG("usedBlocks %d totalBlocks %d\n", _context->info.usedBlocks, _context->info.totalBlocks); + if (_context->info.freeBlocks) { + return (int)((_context->info.usedBlocks * 100) / (_context->info.freeBlocks)); + } + + return -1; // no freeBlocks } -int umm_usage_metric( void ) { -#ifndef UMM_INLINE_METRICS - umm_info(NULL, false); -#endif +int umm_usage_metric(void) { + #ifndef UMM_INLINE_METRICS + umm_info(NULL, false); + #endif - return umm_usage_metric_core(umm_get_current_heap()); + return umm_usage_metric_core(umm_get_current_heap()); } -uint32_t sqrt32 (uint32_t n); - -int umm_fragmentation_metric_core( umm_heap_context_t *_context ) { - // DBGLOG_DEBUG( "freeBlocks %d freeBlocksSquared %d\n", umm_metrics.freeBlocks, ummHeapInfo.freeBlocksSquared); - DBGLOG_DEBUG( "freeBlocks %d freeBlocksSquared %d\n", _context->info.freeBlocks, _context->info.freeBlocksSquared); - if (0 == _context->info.freeBlocks) { - return 0; - } else { - //upstream version: return (100 - (((uint32_t)(sqrtf(ummHeapInfo.freeBlocksSquared)) * 100)/(ummHeapInfo.freeBlocks))); - return (100 - (((uint32_t)(sqrt32(_context->info.freeBlocksSquared)) * 100)/(_context->info.freeBlocks))); - } +uint32_t sqrt32(uint32_t n); + +int umm_fragmentation_metric_core(umm_heap_context_t *_context) { + // DBGLOG_DEBUG( "freeBlocks %d freeBlocksSquared %d\n", umm_metrics.freeBlocks, ummHeapInfo.freeBlocksSquared); + DBGLOG_DEBUG("freeBlocks %d freeBlocksSquared %d\n", _context->info.freeBlocks, _context->info.freeBlocksSquared); + if (0 == _context->info.freeBlocks) { + return 0; + } else { + // upstream version: return (100 - (((uint32_t)(sqrtf(ummHeapInfo.freeBlocksSquared)) * 100)/(ummHeapInfo.freeBlocks))); + return 100 - (((uint32_t)(sqrt32(_context->info.freeBlocksSquared)) * 100) / (_context->info.freeBlocks)); + } } -int umm_fragmentation_metric( void ) { -#ifndef UMM_INLINE_METRICS - umm_info(NULL, false); -#endif +int umm_fragmentation_metric(void) { + #ifndef UMM_INLINE_METRICS + umm_info(NULL, false); + #endif - return umm_fragmentation_metric_core(umm_get_current_heap()); + return umm_fragmentation_metric_core(umm_get_current_heap()); } #ifdef UMM_INLINE_METRICS -static void umm_fragmentation_metric_init( umm_heap_context_t *_context ) { +static void umm_fragmentation_metric_init(umm_heap_context_t *_context) { _context->info.freeBlocks = UMM_NUMBLOCKS - 2; _context->info.freeBlocksSquared = _context->info.freeBlocks * _context->info.freeBlocks; } -static void umm_fragmentation_metric_add( umm_heap_context_t *_context, uint16_t c ) { +static void umm_fragmentation_metric_add(umm_heap_context_t *_context, uint16_t c) { uint16_t blocks = (UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) - c; - DBGLOG_DEBUG( "Add block %d size %d to free metric\n", c, blocks); + DBGLOG_DEBUG("Add block %d size %d to free metric\n", c, blocks); _context->info.freeBlocks += blocks; _context->info.freeBlocksSquared += (blocks * blocks); } -static void umm_fragmentation_metric_remove( umm_heap_context_t *_context, uint16_t c ) { +static void umm_fragmentation_metric_remove(umm_heap_context_t *_context, uint16_t c) { uint16_t blocks = (UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) - c; - DBGLOG_DEBUG( "Remove block %d size %d from free metric\n", c, blocks); + DBGLOG_DEBUG("Remove block %d size %d from free metric\n", c, blocks); _context->info.freeBlocks -= blocks; _context->info.freeBlocksSquared -= (blocks * blocks); } diff --git a/cores/esp8266/umm_malloc/umm_integrity.c b/cores/esp8266/umm_malloc/umm_integrity.c index bb68c52f4d..c652562f99 100644 --- a/cores/esp8266/umm_malloc/umm_integrity.c +++ b/cores/esp8266/umm_malloc/umm_integrity.c @@ -28,109 +28,108 @@ * chain. */ bool umm_integrity_check(void) { - UMM_CRITICAL_DECL(id_integrity); - bool ok = true; - uint16_t prev; - uint16_t cur; - - UMM_INIT_HEAP; - - /* Iterate through all free blocks */ - prev = 0; - UMM_CRITICAL_ENTRY(id_integrity); - - umm_heap_context_t *_context = umm_get_current_heap(); - - while(1) { - cur = UMM_NFREE(prev); - - /* Check that next free block number is valid */ - if (cur >= UMM_NUMBLOCKS) { - DBGLOG_FUNCTION("heap integrity broken: too large next free num: %d " - "(in block %d, addr 0x%08x)\n", cur, prev, - DBGLOG_32_BIT_PTR(&UMM_NBLOCK(prev))); - ok = false; - goto clean; + UMM_CRITICAL_DECL(id_integrity); + bool ok = true; + uint16_t prev; + uint16_t cur; + + UMM_CHECK_INITIALIZED(); + + /* Iterate through all free blocks */ + prev = 0; + UMM_CRITICAL_ENTRY(id_integrity); + + umm_heap_context_t *_context = umm_get_current_heap(); + + while (1) { + cur = UMM_NFREE(prev); + + /* Check that next free block number is valid */ + if (cur >= UMM_NUMBLOCKS) { + DBGLOG_FUNCTION("heap integrity broken: too large next free num: %d " + "(in block %d, addr 0x%08x)\n", cur, prev, + DBGLOG_32_BIT_PTR(&UMM_NBLOCK(prev))); + ok = false; + goto clean; + } + if (cur == 0) { + /* No more free blocks */ + break; + } + + /* Check if prev free block number matches */ + if (UMM_PFREE(cur) != prev) { + DBGLOG_FUNCTION("heap integrity broken: free links don't match: " + "%d -> %d, but %d -> %d\n", + prev, cur, cur, UMM_PFREE(cur)); + ok = false; + goto clean; + } + + UMM_PBLOCK(cur) |= UMM_FREELIST_MASK; + + prev = cur; } - if (cur == 0) { - /* No more free blocks */ - break; - } - - /* Check if prev free block number matches */ - if (UMM_PFREE(cur) != prev) { - DBGLOG_FUNCTION("heap integrity broken: free links don't match: " - "%d -> %d, but %d -> %d\n", - prev, cur, cur, UMM_PFREE(cur)); - ok = false; - goto clean; - } - - UMM_PBLOCK(cur) |= UMM_FREELIST_MASK; - - prev = cur; - } - /* Iterate through all blocks */ - prev = 0; - while(1) { - cur = UMM_NBLOCK(prev) & UMM_BLOCKNO_MASK; - - /* Check that next block number is valid */ - if (cur >= UMM_NUMBLOCKS) { - DBGLOG_FUNCTION("heap integrity broken: too large next block num: %d " - "(in block %d, addr 0x%08x)\n", cur, prev, - DBGLOG_32_BIT_PTR(&UMM_NBLOCK(prev))); - ok = false; - goto clean; - } - if (cur == 0) { - /* No more blocks */ - break; - } - - /* make sure the free mark is appropriate, and unmark it */ - if ((UMM_NBLOCK(cur) & UMM_FREELIST_MASK) - != (UMM_PBLOCK(cur) & UMM_FREELIST_MASK)) - { - DBGLOG_FUNCTION("heap integrity broken: mask wrong at addr 0x%08x: n=0x%x, p=0x%x\n", - DBGLOG_32_BIT_PTR(&UMM_NBLOCK(cur)), - (UMM_NBLOCK(cur) & UMM_FREELIST_MASK), - (UMM_PBLOCK(cur) & UMM_FREELIST_MASK)); - ok = false; - goto clean; - } - - /* make sure the block list is sequential */ - if (cur <= prev ) { - DBGLOG_FUNCTION("heap integrity broken: next block %d is before prev this one " - "(in block %d, addr 0x%08x)\n", cur, prev, - DBGLOG_32_BIT_PTR(&UMM_NBLOCK(prev))); - ok = false; - goto clean; - } + /* Iterate through all blocks */ + prev = 0; + while (1) { + cur = UMM_NBLOCK(prev) & UMM_BLOCKNO_MASK; + + /* Check that next block number is valid */ + if (cur >= UMM_NUMBLOCKS) { + DBGLOG_FUNCTION("heap integrity broken: too large next block num: %d " + "(in block %d, addr 0x%08x)\n", cur, prev, + DBGLOG_32_BIT_PTR(&UMM_NBLOCK(prev))); + ok = false; + goto clean; + } + if (cur == 0) { + /* No more blocks */ + break; + } + + /* make sure the free mark is appropriate, and unmark it */ + if ((UMM_NBLOCK(cur) & UMM_FREELIST_MASK) + != (UMM_PBLOCK(cur) & UMM_FREELIST_MASK)) { + DBGLOG_FUNCTION("heap integrity broken: mask wrong at addr 0x%08x: n=0x%x, p=0x%x\n", + DBGLOG_32_BIT_PTR(&UMM_NBLOCK(cur)), + (UMM_NBLOCK(cur) & UMM_FREELIST_MASK), + (UMM_PBLOCK(cur) & UMM_FREELIST_MASK)); + ok = false; + goto clean; + } + + /* make sure the block list is sequential */ + if (cur <= prev) { + DBGLOG_FUNCTION("heap integrity broken: next block %d is before prev this one " + "(in block %d, addr 0x%08x)\n", cur, prev, + DBGLOG_32_BIT_PTR(&UMM_NBLOCK(prev))); + ok = false; + goto clean; + } /* unmark */ - UMM_PBLOCK(cur) &= UMM_BLOCKNO_MASK; - - /* Check if prev block number matches */ - if (UMM_PBLOCK(cur) != prev) { - DBGLOG_FUNCTION("heap integrity broken: block links don't match: " - "%d -> %d, but %d -> %d\n", - prev, cur, cur, UMM_PBLOCK(cur)); - ok = false; - goto clean; + UMM_PBLOCK(cur) &= UMM_BLOCKNO_MASK; + + /* Check if prev block number matches */ + if (UMM_PBLOCK(cur) != prev) { + DBGLOG_FUNCTION("heap integrity broken: block links don't match: " + "%d -> %d, but %d -> %d\n", + prev, cur, cur, UMM_PBLOCK(cur)); + ok = false; + goto clean; + } + + prev = cur; } - prev = cur; - } - clean: - UMM_CRITICAL_EXIT(id_integrity); - if (!ok){ - UMM_HEAP_CORRUPTION_CB(); - } - return ok; + UMM_CRITICAL_EXIT(id_integrity); + if (!ok) { + UMM_HEAP_CORRUPTION_CB(); + } + return ok; } #endif diff --git a/cores/esp8266/umm_malloc/umm_local.c b/cores/esp8266/umm_malloc/umm_local.c index 3eca39c6d8..c08e2a27ca 100644 --- a/cores/esp8266/umm_malloc/umm_local.c +++ b/cores/esp8266/umm_malloc/umm_local.c @@ -6,28 +6,27 @@ #if defined(UMM_CRITICAL_METRICS) /* - * umm_malloc performance measurments for critical sections + * umm_malloc performance measurements for critical sections */ UMM_TIME_STATS time_stats = { {0xFFFFFFFF, 0U, 0U, 0U}, {0xFFFFFFFF, 0U, 0U, 0U}, {0xFFFFFFFF, 0U, 0U, 0U}, -#ifdef UMM_INFO + #ifdef UMM_INFO {0xFFFFFFFF, 0U, 0U, 0U}, -#endif -#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) + #endif + #if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) {0xFFFFFFFF, 0U, 0U, 0U}, -#endif -#ifdef UMM_INTEGRITY_CHECK + #endif + #ifdef UMM_INTEGRITY_CHECK {0xFFFFFFFF, 0U, 0U, 0U}, -#endif - {0xFFFFFFFF, 0U, 0U, 0U} }; + #endif + {0xFFFFFFFF, 0U, 0U, 0U} +}; -bool ICACHE_FLASH_ATTR get_umm_get_perf_data(UMM_TIME_STATS *p, size_t size) -{ +bool ICACHE_FLASH_ATTR get_umm_get_perf_data(UMM_TIME_STATS *p, size_t size) { UMM_CRITICAL_DECL(id_no_tag); - if (p && sizeof(time_stats) == size) - { + if (p && sizeof(time_stats) == size) { UMM_CRITICAL_ENTRY(id_no_tag); memcpy(p, &time_stats, size); UMM_CRITICAL_EXIT(id_no_tag); @@ -42,42 +41,45 @@ bool ICACHE_FLASH_ATTR get_umm_get_perf_data(UMM_TIME_STATS *p, size_t size) #if defined(UMM_POISON_CHECK_LITE) // We skip this when doing the full poison check. -static bool check_poison_neighbors( umm_heap_context_t *_context, uint16_t cur ) { - uint16_t c; +static bool check_poison_neighbors(umm_heap_context_t *_context, uint16_t cur) { + uint16_t c; - if ( 0 == cur ) - return true; + if (0 == cur) { + return true; + } - c = UMM_PBLOCK(cur) & UMM_BLOCKNO_MASK; - while( c && (UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) ) { - /* - There can be up to 1 free block neighbor in either direction. - This loop should self limit to 2 passes, due to heap design. - i.e. Adjacent free space is always consolidated. - */ - if ( !(UMM_NBLOCK(c) & UMM_FREELIST_MASK) ) { - if ( !check_poison_block(&UMM_BLOCK(c)) ) - return false; - - break; + c = UMM_PBLOCK(cur) & UMM_BLOCKNO_MASK; + while (c && (UMM_NBLOCK(c) & UMM_BLOCKNO_MASK)) { + /* + There can be up to 1 free block neighbor in either direction. + This loop should self limit to 2 passes, due to heap design. + i.e. Adjacent free space is always consolidated. + */ + if (!(UMM_NBLOCK(c) & UMM_FREELIST_MASK)) { + if (!check_poison_block(&UMM_BLOCK(c))) { + return false; + } + + break; + } + + c = UMM_PBLOCK(c) & UMM_BLOCKNO_MASK; } - c = UMM_PBLOCK(c) & UMM_BLOCKNO_MASK; - } + c = UMM_NBLOCK(cur) & UMM_BLOCKNO_MASK; + while ((UMM_NBLOCK(c) & UMM_BLOCKNO_MASK)) { + if (!(UMM_NBLOCK(c) & UMM_FREELIST_MASK)) { + if (!check_poison_block(&UMM_BLOCK(c))) { + return false; + } - c = UMM_NBLOCK(cur) & UMM_BLOCKNO_MASK; - while( (UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) ) { - if ( !(UMM_NBLOCK(c) & UMM_FREELIST_MASK) ) { - if ( !check_poison_block(&UMM_BLOCK(c)) ) - return false; + break; + } - break; + c = UMM_NBLOCK(c) & UMM_BLOCKNO_MASK; } - c = UMM_NBLOCK(c) & UMM_BLOCKNO_MASK; - } - - return true; + return true; } #endif @@ -85,67 +87,73 @@ static bool check_poison_neighbors( umm_heap_context_t *_context, uint16_t cur ) /* ------------------------------------------------------------------------ */ -static void *get_unpoisoned_check_neighbors( void *vptr, const char* file, int line ) { - uintptr_t ptr = (uintptr_t)vptr; - - if (ptr != 0) { - - ptr -= (sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE); - -#if defined(UMM_POISON_CHECK_LITE) - UMM_CRITICAL_DECL(id_poison); - uint16_t c; - bool poison = false; - umm_heap_context_t *_context = umm_get_ptr_context( vptr ); - if (NULL == _context) { - panic(); - return NULL; +static void *get_unpoisoned_check_neighbors(void *vptr, const char *file, int line) { + uintptr_t ptr = (uintptr_t)vptr; + + if (ptr != 0) { + + ptr -= (sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE); + + #if defined(UMM_POISON_CHECK_LITE) + UMM_CRITICAL_DECL(id_poison); + uint16_t c; + bool poison = false; + umm_heap_context_t *_context = _umm_get_ptr_context((void *)ptr); + if (_context) { + + /* Figure out which block we're in. Note the use of truncated division... */ + c = (ptr - (uintptr_t)(&(_context->heap[0]))) / sizeof(umm_block); + + UMM_CRITICAL_ENTRY(id_poison); + poison = + check_poison_block(&UMM_BLOCK(c)) && + check_poison_neighbors(_context, c); + UMM_CRITICAL_EXIT(id_poison); + } else { + DBGLOG_ERROR("\nPointer %p is not a Heap address.\n", vptr); + } + + if (!poison) { + if (file) { + __panic_func(file, line, ""); + } else { + abort(); + } + } + #else + /* + * No need to check poison here. POISON_CHECK() has already done a + * full heap check. + */ + (void)file; + (void)line; + #endif } - /* Figure out which block we're in. Note the use of truncated division... */ - c = (ptr - (uintptr_t)(&(_context->heap[0])))/sizeof(umm_block); - - UMM_CRITICAL_ENTRY(id_poison); - poison = check_poison_block(&UMM_BLOCK(c)) && check_poison_neighbors(_context, c); - UMM_CRITICAL_EXIT(id_poison); - - if (!poison) { - if (file) { - __panic_func(file, line, ""); - } else { - abort(); - } - } -#else - /* - * No need to check poison here. POISON_CHECK() has already done a - * full heap check. - */ - (void)file; - (void)line; -#endif - } - return (void *)ptr; + return (void *)ptr; } /* ------------------------------------------------------------------------ */ -void *umm_poison_realloc_fl(void *ptr, size_t size, const char* file, int line) { +void *umm_poison_realloc_fl(void *ptr, size_t size, const char *file, int line) { void *ret; ptr = get_unpoisoned_check_neighbors(ptr, file, line); - size += poison_size(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; } /* ------------------------------------------------------------------------ */ -void umm_poison_free_fl(void *ptr, const char* file, int line) { +void umm_poison_free_fl(void *ptr, const char *file, int line) { ptr = get_unpoisoned_check_neighbors(ptr, file, line); @@ -156,61 +164,118 @@ void umm_poison_free_fl(void *ptr, const char* file, int line) { /* ------------------------------------------------------------------------ */ #if defined(UMM_STATS) || defined(UMM_STATS_FULL) || defined(UMM_INFO) -size_t umm_block_size( void ) { - return sizeof(umm_block); +/* + 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 -size_t umm_free_heap_size_lw( void ) { - UMM_INIT_HEAP; +/* + 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); + 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) /* - 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. + 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 +/* + 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(); - - DBGLOG_FORCE( force, "umm heap statistics:\n"); - DBGLOG_FORCE( force, " Heap ID %5u\n", _context->id); - DBGLOG_FORCE( force, " Free Space %5u\n", _context->UMM_FREE_BLOCKS * sizeof(umm_block)); - DBGLOG_FORCE( force, " OOM Count %5u\n", _context->UMM_OOM_COUNT); -#if defined(UMM_STATS_FULL) - DBGLOG_FORCE( force, " Low Watermark %5u\n", _context->stats.free_blocks_min * sizeof(umm_block)); - DBGLOG_FORCE( force, " Low Watermark ISR %5u\n", _context->stats.free_blocks_isr_min * sizeof(umm_block)); - DBGLOG_FORCE( force, " MAX Alloc Request %5u\n", _context->stats.alloc_max_size); -#endif - DBGLOG_FORCE( force, " Size of umm_block %5u\n", sizeof(umm_block)); - DBGLOG_FORCE( force, "+--------------------------------------------------------------+\n" ); + umm_heap_context_t *_context = umm_get_current_heap(); + + DBGLOG_FORCE(force, "umm heap statistics:\n"); + DBGLOG_FORCE(force, " Heap ID %7u\n", _context->id); + DBGLOG_FORCE(force, " Free Space %7u\n", _context->UMM_FREE_BLOCKS * sizeof(umm_block)); + DBGLOG_FORCE(force, " OOM Count %7u\n", _context->UMM_OOM_COUNT); + #if defined(UMM_STATS_FULL) + DBGLOG_FORCE(force, " Low Watermark %7u\n", _context->stats.free_blocks_min * sizeof(umm_block)); + DBGLOG_FORCE(force, " Low Watermark ISR %7u\n", _context->stats.free_blocks_isr_min * sizeof(umm_block)); + DBGLOG_FORCE(force, " MAX Alloc Request %7u\n", _context->stats.alloc_max_size); + #endif + DBGLOG_FORCE(force, " Size of umm_block %7u\n", sizeof(umm_block)); + DBGLOG_FORCE(force, "+--------------------------------------------------------------+\n"); } #endif int ICACHE_FLASH_ATTR umm_info_safe_printf_P(const char *fmt, ...) { - /* - To use ets_strlen() and ets_strcpy() safely with PROGMEM, flash storage, - the PROGMEM address must be word (4 bytes) aligned. The destination - address for ets_memcpy must also be word-aligned. - */ - char ram_buf[ets_strlen(fmt) + 1] __attribute__((aligned(4))); - ets_strcpy(ram_buf, fmt); + char ram_buf[strlen_P(fmt) + 1]; + strcpy_P(ram_buf, fmt); va_list argPtr; va_start(argPtr, fmt); int result = ets_vprintf(ets_uart_putc1, ram_buf, argPtr); @@ -219,9 +284,9 @@ int ICACHE_FLASH_ATTR umm_info_safe_printf_P(const char *fmt, ...) { } #if defined(UMM_STATS) || defined(UMM_STATS_FULL) -size_t ICACHE_FLASH_ATTR umm_get_oom_count( void ) { - umm_heap_context_t *_context = umm_get_current_heap(); - return _context->UMM_OOM_COUNT; +size_t ICACHE_FLASH_ATTR umm_get_oom_count(void) { + umm_heap_context_t *_context = umm_get_current_heap(); + return _context->UMM_OOM_COUNT; } #endif @@ -233,70 +298,98 @@ size_t ICACHE_FLASH_ATTR umm_get_oom_count( void ) { // // If this is correct use alias. // -size_t ICACHE_FLASH_ATTR umm_free_heap_size_lw_min( void ) { - umm_heap_context_t *_context = umm_get_current_heap(); - return _context->stats.free_blocks_min * umm_block_size(); +size_t ICACHE_FLASH_ATTR umm_free_heap_size_lw_min(void) { + umm_heap_context_t *_context = umm_get_current_heap(); + return _context->stats.free_blocks_min * umm_block_size(); } -size_t ICACHE_FLASH_ATTR umm_free_heap_size_min_reset( void ) { - umm_heap_context_t *_context = umm_get_current_heap(); - _context->stats.free_blocks_min = _context->UMM_FREE_BLOCKS; - return _context->stats.free_blocks_min * umm_block_size(); +size_t ICACHE_FLASH_ATTR umm_free_heap_size_min_reset(void) { + umm_heap_context_t *_context = umm_get_current_heap(); + _context->stats.free_blocks_min = _context->UMM_FREE_BLOCKS; + return _context->stats.free_blocks_min * umm_block_size(); } #if 0 // TODO - Don't understand this why do both umm_free_heap_size_(lw_)min exist size_t umm_free_heap_size_min(void) __attribute__ ((alias("umm_free_heap_size_lw_min"))); #else -size_t ICACHE_FLASH_ATTR umm_free_heap_size_min( void ) { - umm_heap_context_t *_context = umm_get_current_heap(); - return _context->stats.free_blocks_min * umm_block_size(); +size_t ICACHE_FLASH_ATTR umm_free_heap_size_min(void) { + umm_heap_context_t *_context = umm_get_current_heap(); + return _context->stats.free_blocks_min * umm_block_size(); } #endif -size_t ICACHE_FLASH_ATTR umm_free_heap_size_isr_min( void ) { - umm_heap_context_t *_context = umm_get_current_heap(); - return _context->stats.free_blocks_isr_min * umm_block_size(); +size_t ICACHE_FLASH_ATTR umm_free_heap_size_isr_min(void) { + umm_heap_context_t *_context = umm_get_current_heap(); + return _context->stats.free_blocks_isr_min * umm_block_size(); } -size_t ICACHE_FLASH_ATTR umm_get_max_alloc_size( void ) { - umm_heap_context_t *_context = umm_get_current_heap(); - return _context->stats.alloc_max_size; +size_t ICACHE_FLASH_ATTR umm_get_max_alloc_size(void) { + umm_heap_context_t *_context = umm_get_current_heap(); + return _context->stats.alloc_max_size; } -size_t ICACHE_FLASH_ATTR umm_get_last_alloc_size( void ) { - umm_heap_context_t *_context = umm_get_current_heap(); - return _context->stats.last_alloc_size; +size_t ICACHE_FLASH_ATTR umm_get_last_alloc_size(void) { + umm_heap_context_t *_context = umm_get_current_heap(); + return _context->stats.last_alloc_size; } -size_t ICACHE_FLASH_ATTR umm_get_malloc_count( void ) { - umm_heap_context_t *_context = umm_get_current_heap(); - return _context->stats.id_malloc_count; +size_t ICACHE_FLASH_ATTR umm_get_malloc_count(void) { + umm_heap_context_t *_context = umm_get_current_heap(); + return _context->stats.id_malloc_count; } -size_t ICACHE_FLASH_ATTR umm_get_malloc_zero_count( void ) { - umm_heap_context_t *_context = umm_get_current_heap(); - return _context->stats.id_malloc_zero_count; +size_t ICACHE_FLASH_ATTR umm_get_malloc_zero_count(void) { + umm_heap_context_t *_context = umm_get_current_heap(); + return _context->stats.id_malloc_zero_count; } -size_t ICACHE_FLASH_ATTR umm_get_realloc_count( void ) { - umm_heap_context_t *_context = umm_get_current_heap(); - return _context->stats.id_realloc_count; +size_t ICACHE_FLASH_ATTR umm_get_realloc_count(void) { + umm_heap_context_t *_context = umm_get_current_heap(); + return _context->stats.id_realloc_count; } -size_t ICACHE_FLASH_ATTR umm_get_realloc_zero_count( void ) { - umm_heap_context_t *_context = umm_get_current_heap(); - return _context->stats.id_realloc_zero_count; +size_t ICACHE_FLASH_ATTR umm_get_realloc_zero_count(void) { + umm_heap_context_t *_context = umm_get_current_heap(); + return _context->stats.id_realloc_zero_count; } -size_t ICACHE_FLASH_ATTR umm_get_free_count( void ) { - umm_heap_context_t *_context = umm_get_current_heap(); - return _context->stats.id_free_count; +size_t ICACHE_FLASH_ATTR umm_get_free_count(void) { + umm_heap_context_t *_context = umm_get_current_heap(); + return _context->stats.id_free_count; } -size_t ICACHE_FLASH_ATTR umm_get_free_null_count( void ) { - umm_heap_context_t *_context = umm_get_current_heap(); - return _context->stats.id_free_null_count; +size_t ICACHE_FLASH_ATTR umm_get_free_null_count(void) { + umm_heap_context_t *_context = umm_get_current_heap(); + return _context->stats.id_free_null_count; } #endif // UMM_STATS_FULL +#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) +/* + * Saturated unsigned add + * Poison added to allocation size requires overflow protection. + */ +static size_t umm_uadd_sat(const size_t a, const size_t b) { + size_t r = a + b; + if (r < a) { + return SIZE_MAX; + } + return r; +} +#endif + +/* + * Use platform-specific functions to protect against unsigned overflow/wrap by + * implementing saturated unsigned multiply. + * The function umm_calloc requires a saturated multiply function. + */ +size_t umm_umul_sat(const size_t a, const size_t b) { + size_t r; + if (__builtin_mul_overflow(a, b, &r)) { + return SIZE_MAX; + } + return r; +} + + #endif // BUILD_UMM_MALLOC_C diff --git a/cores/esp8266/umm_malloc/umm_local.h b/cores/esp8266/umm_malloc/umm_local.h index cb72d669c1..c5dcffd73c 100644 --- a/cores/esp8266/umm_malloc/umm_local.h +++ b/cores/esp8266/umm_malloc/umm_local.h @@ -15,6 +15,14 @@ #define memset ets_memset +/* + * Saturated unsigned add and unsigned multiply + */ +size_t umm_umul_sat(const size_t a, const size_t b); // share with heap.cpp +#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) +static size_t umm_uadd_sat(const size_t a, const size_t b); +#endif + /* * This redefines DBGLOG_FORCE defined in dbglog/dbglog.h * Just for printing from umm_info() which is assumed to always be called from @@ -22,7 +30,7 @@ * string while INTLEVEL is non-zero. */ #undef DBGLOG_FORCE -#define DBGLOG_FORCE(force, format, ...) {if(force) {UMM_INFO_PRINTF(format, ## __VA_ARGS__);}} +#define DBGLOG_FORCE(force, format, ...) {if (force) {UMM_INFO_PRINTF(format,##__VA_ARGS__);}} // #define DBGLOG_FORCE(force, format, ...) {if(force) {::printf(PSTR(format), ## __VA_ARGS__);}} @@ -37,7 +45,7 @@ #if defined(UMM_POISON_CHECK_LITE) -static bool check_poison_neighbors( umm_heap_context_t *_context, uint16_t cur ); +static bool check_poison_neighbors(umm_heap_context_t *_context, uint16_t cur); #endif @@ -48,23 +56,22 @@ void ICACHE_FLASH_ATTR umm_print_stats(int force); int ICACHE_FLASH_ATTR umm_info_safe_printf_P(const char *fmt, ...) __attribute__((format(printf, 1, 2))); -#define UMM_INFO_PRINTF(fmt, ...) umm_info_safe_printf_P(PSTR4(fmt), ##__VA_ARGS__) -// use PSTR4() instead of PSTR() to ensure 4-bytes alignment in Flash, whatever the default alignment of PSTR_ALIGN +#define UMM_INFO_PRINTF(fmt, ...) umm_info_safe_printf_P(PSTR(fmt),##__VA_ARGS__) typedef struct umm_block_t umm_block; struct UMM_HEAP_CONTEXT { - umm_block *heap; - void *heap_end; -#if (!defined(UMM_INLINE_METRICS) && defined(UMM_STATS)) || defined(UMM_STATS_FULL) - UMM_STATISTICS stats; -#endif -#ifdef UMM_INFO - UMM_HEAP_INFO info; -#endif - unsigned short int numblocks; - unsigned char id; + umm_block *heap; + void *heap_end; + #if (!defined(UMM_INLINE_METRICS) && defined(UMM_STATS)) || defined(UMM_STATS_FULL) + UMM_STATISTICS stats; + #endif + #ifdef UMM_INFO + UMM_HEAP_INFO info; + #endif + unsigned short int numblocks; + unsigned char id; }; diff --git a/cores/esp8266/umm_malloc/umm_malloc.cpp b/cores/esp8266/umm_malloc/umm_malloc.cpp index 11b67d0fb9..e130862cf7 100644 --- a/cores/esp8266/umm_malloc/umm_malloc.cpp +++ b/cores/esp8266/umm_malloc/umm_malloc.cpp @@ -63,43 +63,57 @@ extern "C" { #define DBGLOG_LEVEL 0 #endif -// Save 104 bytes by calling umm_init() early once from app_entry() -// Some minor UMM_CRITICAL_METRICS counts will be lost through CRT0 init. -// #define UMM_INIT_HEAP if (!umm_heap) { umm_init(); } -#define UMM_INIT_HEAP (void)0 - #include "dbglog/dbglog.h" -//C This change is new in upstream umm_malloc.I think this would have created a -//C breaking change. Keeping the old #define method in umm_malloc_cfg.h. -//C I don't see a simple way of making it work. We would have to run code before -//C the SDK has run to set a value for uint32_t UMM_MALLOC_CFG_HEAP_SIZE. -//C On the other hand, a manual call to umm_init() before anything else has had a -//C chance to run would mean that all those calls testing to see if the heap has -//C been initialized at every umm_malloc API could be removed. -//C -//C before starting the NON OS SDK -//C extern void *UMM_MALLOC_CFG_HEAP_ADDR; -//C extern uint32_t UMM_MALLOC_CFG_HEAP_SIZE; +/* + * These variables are used in upstream umm_malloc for initializing the heap. + * Our port initialization is different and does not use them at this time. +extern void *UMM_MALLOC_CFG_HEAP_ADDR; +extern uint32_t UMM_MALLOC_CFG_HEAP_SIZE; + */ +/* + * In our port, we leave UMM_CHECK_INITIALIZED unset. Since we initialize the + * heap before CRT0 init has run, commonly used testing methods for heap init + * may not work. Not using UMM_CHECK_INITIALIZED saves about 104 bytes of IRAM. + * + * In our configuration app_entry_redefinable() must call umm_init(), before + * calling the SDK's app_entry_custom(). The DRAM Heap must be available before + * the SDK starts. + * + * If building with UMM_CRITICAL_METRICS, some minor counts will be lost through + * CRT0 init. + */ + +#if 0 // Must be zero at release +#warning "Macro NON_NULL_CONTEXT_ASSERT() is active!" +/* + * Keep for future debug/maintenance of umm_malloc. Not needed in a + * regular/debug build. Call paths that use NON_NULL_CONTEXT_ASSERT logically + * guard against returning NULL. This macro double-checks that assumption during + * development. + */ +#define NON_NULL_CONTEXT_ASSERT() assert((NULL != _context)) +#else +#define NON_NULL_CONTEXT_ASSERT() (void)0 +#endif #include "umm_local.h" // target-dependent supplemental /* ------------------------------------------------------------------------- */ UMM_H_ATTPACKPRE typedef struct umm_ptr_t { - uint16_t next; - uint16_t prev; + uint16_t next; + uint16_t prev; } UMM_H_ATTPACKSUF umm_ptr; - UMM_H_ATTPACKPRE typedef struct umm_block_t { - union { - umm_ptr used; - } header; - union { - umm_ptr free; - uint8_t data[4]; - } body; + union { + umm_ptr used; + } header; + union { + umm_ptr free; + uint8_t data[4]; + } body; } UMM_H_ATTPACKSUF umm_block; #define UMM_FREELIST_MASK ((uint16_t)(0x8000)) @@ -125,85 +139,85 @@ static unsigned char umm_heap_stack[UMM_HEAP_STACK_DEPTH]; #if (UMM_NUM_HEAPS == 1) size_t umm_get_current_heap_id(void) { - return 0; + return 0; } umm_heap_context_t *umm_get_current_heap(void) { - return &heap_context[0]; + return &heap_context[0]; } -static umm_heap_context_t *umm_get_heap_by_id( size_t which ) { - (void)which; - return &heap_context[0]; +static umm_heap_context_t *umm_get_heap_by_id(size_t which) { + (void)which; + return &heap_context[0]; } -umm_heap_context_t *umm_set_heap_by_id( size_t which ) { - (void)which; - return &heap_context[0]; +umm_heap_context_t *umm_set_heap_by_id(size_t which) { + (void)which; + return &heap_context[0]; } #else size_t umm_get_current_heap_id(void) { - return umm_heap_cur; + return umm_heap_cur; } umm_heap_context_t *umm_get_current_heap(void) { - return &heap_context[umm_heap_cur]; + return &heap_context[umm_heap_cur]; } -static umm_heap_context_t *umm_get_heap_by_id( size_t which ) { - if (which < UMM_NUM_HEAPS) { - return &heap_context[which]; - } - return NULL; +static umm_heap_context_t *umm_get_heap_by_id(size_t which) { + if (which < UMM_NUM_HEAPS) { + return &heap_context[which]; + } + return NULL; } -umm_heap_context_t *umm_set_heap_by_id( size_t which ) { - umm_heap_context_t *_context = umm_get_heap_by_id(which); - if (_context && _context->heap) { - umm_heap_cur = which; - return _context; - } - return NULL; +umm_heap_context_t *umm_set_heap_by_id(size_t which) { + umm_heap_context_t *_context = umm_get_heap_by_id(which); + if (_context && _context->heap) { + umm_heap_cur = which; + return _context; + } + return NULL; } #endif #if (UMM_NUM_HEAPS == 1) -umm_heap_context_t *umm_push_heap( size_t which ) { - (void)which; - return &heap_context[0]; +umm_heap_context_t *umm_push_heap(size_t which) { + (void)which; + return &heap_context[0]; } -umm_heap_context_t *umm_pop_heap( void ) { - return &heap_context[0]; +umm_heap_context_t *umm_pop_heap(void) { + return &heap_context[0]; } -int umm_get_heap_stack_index( void ) { - return 0; +int umm_get_heap_stack_index(void) { + return 0; } #else /* ------------------------------------------------------------------------ */ -umm_heap_context_t *umm_push_heap( size_t which ) { - if (umm_heap_stack_ptr < UMM_HEAP_STACK_DEPTH) { - umm_heap_stack[umm_heap_stack_ptr++] = umm_heap_cur; - return umm_set_heap_by_id( which ); - } - return NULL; +umm_heap_context_t *umm_push_heap(size_t which) { + if (umm_heap_stack_ptr < UMM_HEAP_STACK_DEPTH) { + umm_heap_stack[umm_heap_stack_ptr++] = umm_heap_cur; + return umm_set_heap_by_id(which); + } + return NULL; } /* ------------------------------------------------------------------------ */ -umm_heap_context_t *umm_pop_heap( void ) { - if (umm_heap_stack_ptr > 0 ) { - return umm_set_heap_by_id(umm_heap_stack[--umm_heap_stack_ptr]); - } - return NULL; +umm_heap_context_t *umm_pop_heap(void) { + if (umm_heap_stack_ptr > 0) { + return umm_set_heap_by_id(umm_heap_stack[--umm_heap_stack_ptr]); + } + return NULL; } // Intended for diagnosic use -int umm_get_heap_stack_index( void ) { - return umm_heap_stack_ptr; +int umm_get_heap_stack_index(void) { + return umm_heap_stack_ptr; } #endif /* ------------------------------------------------------------------------ */ @@ -212,22 +226,42 @@ int umm_get_heap_stack_index( void ) { * realloc or free since you may not be in the right heap to handle it. * */ -static bool test_ptr_context( size_t which, void *ptr ) { - return - heap_context[which].heap && - ptr >= (void *)heap_context[which].heap && - ptr < heap_context[which].heap_end; +static bool test_ptr_context(const size_t which, const void *const ptr) { + return + heap_context[which].heap && + ptr >= (void *)heap_context[which].heap && + ptr < heap_context[which].heap_end; } -static umm_heap_context_t *umm_get_ptr_context(void *ptr) { - for (size_t i = 0; i < UMM_NUM_HEAPS; i++) { - if (test_ptr_context( i, ptr ) ) { - return umm_get_heap_by_id( i ); +/* + * Find Heap context by allocation address - may return NULL + */ +umm_heap_context_t *_umm_get_ptr_context(const void *const ptr) { + for (size_t i = 0; i < UMM_NUM_HEAPS; i++) { + if (test_ptr_context(i, ptr)) { + return umm_get_heap_by_id(i); + } } - } - panic(); - return NULL; + return NULL; +} + +/* + * Find Heap context by allocation address - must either succeed or abort + */ +static umm_heap_context_t *umm_get_ptr_context(const void *const ptr) { + umm_heap_context_t *const _context = _umm_get_ptr_context(ptr); + if (_context) { + return _context; + } + + [[maybe_unused]] uintptr_t sketch_ptr = (uintptr_t)ptr; + #if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) + sketch_ptr += sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE; + #endif + DBGLOG_ERROR("\nPointer %p is not a Heap address.\n", (void *)sketch_ptr); + abort(); + return NULL; } #define UMM_NUMBLOCKS (_context->numblocks) @@ -266,28 +300,68 @@ static umm_heap_context_t *umm_get_ptr_context(void *ptr) { /* ------------------------------------------------------------------------ */ -static uint16_t umm_blocks( size_t size ) { +static uint16_t umm_blocks(size_t size) { - /* - * The calculation of the block size is not too difficult, but there are - * a few little things that we need to be mindful of. - * - * When a block removed from the free list, the space used by the free - * pointers is available for data. That's what the first calculation - * of size is doing. - */ + /* + * The calculation of the block size is not too difficult, but there are + * a few little things that we need to be mindful of. + * + * When a block removed from the free list, the space used by the free + * pointers is available for data. That's what the first calculation + * of size is doing. + * + * We don't check for the special case of (size == 0) here as this needs + * special handling in the caller depending on context. For example when we + * realloc() a block to size 0 it should simply be freed. + * + * We do NOT need to check for allocating more blocks than the heap can + * possibly hold - the allocator figures this out for us. + * + * There are only two cases left to consider: + * + * 1. (size <= body) Obviously this is just one block + * 2. (blocks > (2^15)) This should return ((2^15)) to force a + * failure when the allocator runs + * + * If the requested size is greater that 32677-2 blocks (max block index + * minus the overhead of the top and bottom bookkeeping blocks) then we + * will return an incorrectly truncated value when the result is cast to + * a uint16_t. + */ - if( size <= (sizeof(((umm_block *)0)->body)) ) - return( 1 ); + if (size <= (sizeof(((umm_block *)0)->body))) { + return 1; + } - /* - * If it's for more than that, then we need to figure out the number of - * additional whole blocks the size of an umm_block are required. - */ + /* + * If it's for more than that, then we need to figure out the number of + * additional whole blocks the size of an umm_block are required, so + * reduce the size request by the number of bytes in the body of the + * first block. + */ - size -= ( 1 + (sizeof(((umm_block *)0)->body)) ); + size -= (sizeof(((umm_block *)0)->body)); - return( 2 + size/(sizeof(umm_block)) ); + /* NOTE WELL that we take advantage of the fact that INT16_MAX is the + * number of blocks that we can index in 15 bits :-) + * + * The below expression looks wierd, but it's right. Assuming body + * size of 4 bytes and a block size of 8 bytes: + * + * BYTES (BYTES-BODY) (BYTES-BODY-1)/BLOCKSIZE BLOCKS + * 1 n/a n/a 1 + * 5 1 0 2 + * 12 8 0 2 + * 13 9 1 3 + */ + + size_t blocks = (2 + ((size - 1) / sizeof(umm_block))); + + if (blocks > (INT16_MAX)) { + blocks = INT16_MAX; + } + + return (uint16_t)blocks; } /* ------------------------------------------------------------------------ */ @@ -303,26 +377,26 @@ static void umm_split_block( umm_heap_context_t *_context, uint16_t c, uint16_t blocks, - uint16_t new_freemask ) { + uint16_t new_freemask) { - UMM_NBLOCK(c+blocks) = (UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) | new_freemask; - UMM_PBLOCK(c+blocks) = c; + UMM_NBLOCK(c + blocks) = (UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) | new_freemask; + UMM_PBLOCK(c + blocks) = c; - UMM_PBLOCK(UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) = (c+blocks); - UMM_NBLOCK(c) = (c+blocks); + UMM_PBLOCK(UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) = (c + blocks); + UMM_NBLOCK(c) = (c + blocks); } /* ------------------------------------------------------------------------ */ -static void umm_disconnect_from_free_list( umm_heap_context_t *_context, uint16_t c ) { - /* Disconnect this block from the FREE list */ +static void umm_disconnect_from_free_list(umm_heap_context_t *_context, uint16_t c) { + /* Disconnect this block from the FREE list */ - UMM_NFREE(UMM_PFREE(c)) = UMM_NFREE(c); - UMM_PFREE(UMM_NFREE(c)) = UMM_PFREE(c); + UMM_NFREE(UMM_PFREE(c)) = UMM_NFREE(c); + UMM_PFREE(UMM_NFREE(c)) = UMM_PFREE(c); - /* And clear the free block indicator */ + /* And clear the free block indicator */ - UMM_NBLOCK(c) &= (~UMM_FREELIST_MASK); + UMM_NBLOCK(c) &= (~UMM_FREELIST_MASK); } /* ------------------------------------------------------------------------ @@ -331,28 +405,28 @@ static void umm_disconnect_from_free_list( umm_heap_context_t *_context, uint16_ * next block is free. */ -static void umm_assimilate_up( umm_heap_context_t *_context, uint16_t c ) { +static void umm_assimilate_up(umm_heap_context_t *_context, uint16_t c) { - if( UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_FREELIST_MASK ) { + if (UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_FREELIST_MASK) { - UMM_FRAGMENTATION_METRIC_REMOVE( UMM_NBLOCK(c) ); + UMM_FRAGMENTATION_METRIC_REMOVE(UMM_NBLOCK(c)); - /* - * The next block is a free block, so assimilate up and remove it from - * the free list - */ + /* + * The next block is a free block, so assimilate up and remove it from + * the free list + */ - DBGLOG_DEBUG( "Assimilate up to next block, which is FREE\n" ); + DBGLOG_DEBUG("Assimilate up to next block, which is FREE\n"); - /* Disconnect the next block from the FREE list */ + /* Disconnect the next block from the FREE list */ - umm_disconnect_from_free_list( _context, UMM_NBLOCK(c) ); + umm_disconnect_from_free_list(_context, UMM_NBLOCK(c)); - /* Assimilate the next block with this one */ + /* Assimilate the next block with this one */ - UMM_PBLOCK(UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_BLOCKNO_MASK) = c; - UMM_NBLOCK(c) = UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_BLOCKNO_MASK; - } + UMM_PBLOCK(UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_BLOCKNO_MASK) = c; + UMM_NBLOCK(c) = UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_BLOCKNO_MASK; + } } /* ------------------------------------------------------------------------ @@ -361,50 +435,59 @@ static void umm_assimilate_up( umm_heap_context_t *_context, uint16_t c ) { * up before assimilating down. */ -static uint16_t umm_assimilate_down( umm_heap_context_t *_context, uint16_t c, uint16_t freemask ) { +static uint16_t umm_assimilate_down(umm_heap_context_t *_context, uint16_t c, uint16_t freemask) { - // We are going to assimilate down to the previous block because - // it was free, so remove it from the fragmentation metric + // We are going to assimilate down to the previous block because + // it was free, so remove it from the fragmentation metric - UMM_FRAGMENTATION_METRIC_REMOVE(UMM_PBLOCK(c)); + UMM_FRAGMENTATION_METRIC_REMOVE(UMM_PBLOCK(c)); - UMM_NBLOCK(UMM_PBLOCK(c)) = UMM_NBLOCK(c) | freemask; - UMM_PBLOCK(UMM_NBLOCK(c)) = UMM_PBLOCK(c); + UMM_NBLOCK(UMM_PBLOCK(c)) = UMM_NBLOCK(c) | freemask; + UMM_PBLOCK(UMM_NBLOCK(c)) = UMM_PBLOCK(c); - if (freemask) { - // We are going to free the entire assimilated block - // so add it to the fragmentation metric. A good - // compiler will optimize away the empty if statement - // when UMM_INFO is not defined, so don't worry about - // guarding it. + if (freemask) { + // We are going to free the entire assimilated block + // so add it to the fragmentation metric. A good + // compiler will optimize away the empty if statement + // when UMM_INFO is not defined, so don't worry about + // guarding it. - UMM_FRAGMENTATION_METRIC_ADD(UMM_PBLOCK(c)); - } + UMM_FRAGMENTATION_METRIC_ADD(UMM_PBLOCK(c)); + } - return( UMM_PBLOCK(c) ); + return UMM_PBLOCK(c); } /* ------------------------------------------------------------------------- */ - -static void umm_init_stage_2( umm_heap_context_t *_context ) { - /* setup initial blank heap structure */ +#undef ICACHE_MAYBE +#ifdef UMM_INIT_USE_IRAM +// umm_init(), ... stays in IRAM +#define ICACHE_MAYBE +#else +// Freeup IRAM +#define ICACHE_MAYBE ICACHE_FLASH_ATTR +#endif +/* + * In this port, we split the upstream version of umm_init_heap() into two + * parts: _umm_init_heap and umm_init_heap. Then add multiple heap support. + */ +static void ICACHE_MAYBE _umm_init_heap(umm_heap_context_t *_context) { + /* setup initial blank heap structure */ UMM_FRAGMENTATION_METRIC_INIT(); /* init stats.free_blocks */ -#if defined(UMM_STATS) || defined(UMM_STATS_FULL) -#if defined(UMM_STATS_FULL) - _context->stats.free_blocks_min = - _context->stats.free_blocks_isr_min = UMM_NUMBLOCKS - 2; -#endif -#ifndef UMM_INLINE_METRICS + #if defined(UMM_STATS_FULL) + _context->stats.free_blocks_min = UMM_NUMBLOCKS - 2; + _context->stats.free_blocks_isr_min = UMM_NUMBLOCKS - 2; + #endif + #if (defined(UMM_STATS) || defined(UMM_STATS_FULL)) && !defined(UMM_INLINE_METRICS) _context->stats.free_blocks = UMM_NUMBLOCKS - 2; -#endif -#endif + #endif /* Set up umm_block[0], which just points to umm_block[1] */ UMM_NBLOCK(0) = 1; - UMM_NFREE(0) = 1; - UMM_PFREE(0) = 1; + UMM_NFREE(0) = 1; + UMM_PFREE(0) = 1; /* * Now, we need to set the whole heap space as a huge free block. We should @@ -438,161 +521,171 @@ static void umm_init_stage_2( umm_heap_context_t *_context ) { UMM_PBLOCK(UMM_BLOCK_LAST) = 1; } +void ICACHE_MAYBE umm_init_heap(size_t id, void *start_addr, size_t size, bool full_init) { + /* Check for bad values and block duplicate init attempts. */ + umm_heap_context_t *_context = umm_get_heap_by_id(id); + if (NULL == start_addr || NULL == _context || _context->heap) { + return; + } + + /* init heap pointer and size, and memset it to 0 */ -void umm_init_common( size_t id, void *start_addr, size_t size, bool zero ) { - /* Preserve internal setup */ - umm_heap_context_t *_context = umm_get_heap_by_id(id); - if (NULL == start_addr || NULL == _context || _context->heap) { - return; - } - - /* init heap pointer and size, and memset it to 0 */ - _context->id = id; - _context->heap = (umm_block *)start_addr; - _context->heap_end = (void *)((uintptr_t)start_addr + size); - _context->numblocks = (size / sizeof(umm_block)); - - // An option for blocking the zeroing of extra heaps allows for performing - // post-crash discovery. - if (zero) { - memset(_context->heap, 0x00, size); -#if (!defined(UMM_INLINE_METRICS) && defined(UMM_STATS)) || defined(UMM_STATS_FULL) - memset(&_context->stats, 0x00, sizeof(_context->stats)); -#endif + _context->id = id; + _context->heap = (umm_block *)start_addr; + _context->heap_end = (void *)((uintptr_t)start_addr + size); + _context->numblocks = (size / sizeof(umm_block)); + + // An option for blocking the zeroing of extra heaps. This allows for + // post-crash debugging after reboot. + if (full_init) { + memset(_context->heap, 0x00, size); + #if (!defined(UMM_INLINE_METRICS) && defined(UMM_STATS)) || defined(UMM_STATS_FULL) + memset(&_context->stats, 0x00, sizeof(_context->stats)); + #endif - /* Set up internal data structures */ - umm_init_stage_2(_context); - } + /* Set up internal data structures */ + _umm_init_heap(_context); + } } -void umm_init( void ) { - // if (umm_heap) { - // return; - // } - for (size_t i = 0; i < UMM_NUM_HEAPS; i++) { - heap_context[i].heap = NULL; - } - memset(&heap_context[0], 0, sizeof(heap_context)); - umm_init_common( UMM_HEAP_DRAM, (void *)UMM_MALLOC_CFG_HEAP_ADDR, UMM_MALLOC_CFG_HEAP_SIZE, true ); - // umm_heap = (void *)&heap_context; +void ICACHE_MAYBE umm_init(void) { + + // We can get called before "C" runtime has run. Here we handles that + // beginning of time initialization. As such heap_context[] must be + // defined with attributes to prevent initialization by the "C" runtime. + // A late "C" runtime init would destroy our work. + + // Assume no "C" runtime zero init + for (size_t i = 0; i < UMM_NUM_HEAPS; i++) { + heap_context[i].heap = NULL; + } + memset(&heap_context[0], 0, sizeof(heap_context)); + // Note, full_init must be true for the primary heap, DRAM. + umm_init_heap(UMM_HEAP_DRAM, (void *)UMM_MALLOC_CFG_HEAP_ADDR, UMM_MALLOC_CFG_HEAP_SIZE, true); + + // upstream ref: + // Initialize the heap from linker supplied values */ + // umm_init_heap(UMM_MALLOC_CFG_HEAP_ADDR, UMM_MALLOC_CFG_HEAP_SIZE); } +/* + * Only the Internal DRAM init, needs (or maybe not) to be called from IRAM. + * umm_init_iram and umm_init_vm are called from user_init() after the SDK has + * inited and ICACHE has been enabled. + */ #ifdef UMM_HEAP_IRAM -void umm_init_iram_ex( void *addr, unsigned int size, bool zero ) { - /* We need the main, internal heap set up first */ - UMM_INIT_HEAP; +void ICACHE_FLASH_ATTR umm_init_iram_ex(void *addr, unsigned int size, bool full_init) { + /* We need the main, internal heap set up first */ + UMM_CHECK_INITIALIZED(); - umm_init_common(UMM_HEAP_IRAM, addr, size, zero); + umm_init_heap(UMM_HEAP_IRAM, addr, size, full_init); } void _text_end(void); -void umm_init_iram(void) __attribute__((weak)); +void ICACHE_FLASH_ATTR umm_init_iram(void) __attribute__((weak)); /* By using a weak link, it is possible to reduce the IRAM heap size with a user-supplied init function. This would allow the creation of a block of IRAM dedicated to a sketch and possibly used/preserved across reboots. */ -void umm_init_iram(void) { - umm_init_iram_ex(mmu_sec_heap(), mmu_sec_heap_size(), true); +void ICACHE_FLASH_ATTR umm_init_iram(void) { + umm_init_iram_ex(mmu_sec_heap(), mmu_sec_heap_size(), true); } -#endif // #ifdef UMM_HEAP_IRAM +#endif // #ifdef UMM_HEAP_IRAM #ifdef UMM_HEAP_EXTERNAL -void umm_init_vm( void *vmaddr, unsigned int vmsize ) { - /* We need the main, internal (DRAM) heap set up first */ - UMM_INIT_HEAP; +void ICACHE_FLASH_ATTR umm_init_vm(void *vmaddr, unsigned int vmsize) { + /* We need the main, internal (DRAM) heap set up first */ + UMM_CHECK_INITIALIZED(); - umm_init_common(UMM_HEAP_EXTERNAL, vmaddr, vmsize, true); + umm_init_heap(UMM_HEAP_EXTERNAL, vmaddr, vmsize, true); } #endif /* ------------------------------------------------------------------------ * Must be called only from within critical sections guarded by - * UMM_CRITICAL_ENTRY() and UMM_CRITICAL_EXIT(). + * UMM_CRITICAL_ENTRY(id) and UMM_CRITICAL_EXIT(id). */ -static void umm_free_core( umm_heap_context_t *_context, void *ptr ) { +static void umm_free_core(umm_heap_context_t *_context, void *ptr) { - uint16_t c; + uint16_t c; - if (NULL == _context) { - panic(); - return; - } + NON_NULL_CONTEXT_ASSERT(); - STATS__FREE_REQUEST(id_free); - /* - * FIXME: At some point it might be a good idea to add a check to make sure - * that the pointer we're being asked to free up is actually within - * the umm_heap! - * - * NOTE: See the new umm_info() function that you can use to see if a ptr is - * on the free list! - */ + STATS__FREE_REQUEST(id_free); + /* + * FIXME: At some point it might be a good idea to add a check to make sure + * that the pointer we're being asked to free up is actually within + * the umm_heap! + * + * NOTE: See the new umm_info() function that you can use to see if a ptr is + * on the free list! + */ - /* Figure out which block we're in. Note the use of truncated division... */ + /* Figure out which block we're in. Note the use of truncated division... */ - c = (((uintptr_t)ptr)-(uintptr_t)(&(_context->heap[0])))/sizeof(umm_block); + c = (((uintptr_t)ptr) - (uintptr_t)(&(_context->heap[0]))) / sizeof(umm_block); - DBGLOG_DEBUG( "Freeing block %6d\n", c ); + DBGLOG_DEBUG("Freeing block %6d\n", c); - /* Update stats Free Block count */ - STATS__FREE_BLOCKS_UPDATE(UMM_NBLOCK(c) - c); + /* Update stats Free Block count */ + STATS__FREE_BLOCKS_UPDATE(UMM_NBLOCK(c) - c); - /* Now let's assimilate this block with the next one if possible. */ + /* Now let's assimilate this block with the next one if possible. */ - umm_assimilate_up( _context, c ); + umm_assimilate_up(_context, c); - /* Then assimilate with the previous block if possible */ + /* Then assimilate with the previous block if possible */ - if( UMM_NBLOCK(UMM_PBLOCK(c)) & UMM_FREELIST_MASK ) { + if (UMM_NBLOCK(UMM_PBLOCK(c)) & UMM_FREELIST_MASK) { - DBGLOG_DEBUG( "Assimilate down to previous block, which is FREE\n" ); + DBGLOG_DEBUG("Assimilate down to previous block, which is FREE\n"); - c = umm_assimilate_down(_context, c, UMM_FREELIST_MASK); - } else { - /* - * The previous block is not a free block, so add this one to the head - * of the free list - */ - UMM_FRAGMENTATION_METRIC_ADD(c); + c = umm_assimilate_down(_context, c, UMM_FREELIST_MASK); + } else { + /* + * The previous block is not a free block, so add this one to the head + * of the free list + */ + UMM_FRAGMENTATION_METRIC_ADD(c); - DBGLOG_DEBUG( "Just add to head of free list\n" ); + DBGLOG_DEBUG("Just add to head of free list\n"); - UMM_PFREE(UMM_NFREE(0)) = c; - UMM_NFREE(c) = UMM_NFREE(0); - UMM_PFREE(c) = 0; - UMM_NFREE(0) = c; + UMM_PFREE(UMM_NFREE(0)) = c; + UMM_NFREE(c) = UMM_NFREE(0); + UMM_PFREE(c) = 0; + UMM_NFREE(0) = c; - UMM_NBLOCK(c) |= UMM_FREELIST_MASK; - } + UMM_NBLOCK(c) |= UMM_FREELIST_MASK; + } } /* ------------------------------------------------------------------------ */ -void umm_free( void *ptr ) { - UMM_CRITICAL_DECL(id_free); +void umm_free(void *ptr) { + UMM_CRITICAL_DECL(id_free); - UMM_INIT_HEAP; + UMM_CHECK_INITIALIZED(); - /* If we're being asked to free a NULL pointer, well that's just silly! */ + /* If we're being asked to free a NULL pointer, well that's just silly! */ - if( (void *)0 == ptr ) { - DBGLOG_DEBUG( "free a null pointer -> do nothing\n" ); - STATS__NULL_FREE_REQUEST(id_free); + if ((void *)0 == ptr) { + DBGLOG_DEBUG("free a null pointer -> do nothing\n"); + STATS__NULL_FREE_REQUEST(id_free); - return; - } + return; + } - /* Free the memory withing a protected critical section */ + /* Free the memory within a protected critical section */ - UMM_CRITICAL_ENTRY(id_free); + UMM_CRITICAL_ENTRY(id_free); - /* Need to be in the heap in which this block lives */ - umm_free_core( umm_get_ptr_context( ptr ), ptr ); + /* Need to be in the heap in which this block lives */ + umm_free_core(umm_get_ptr_context(ptr), ptr); - UMM_CRITICAL_EXIT(id_free); + UMM_CRITICAL_EXIT(id_free); } /* ------------------------------------------------------------------------ @@ -600,328 +693,325 @@ void umm_free( void *ptr ) { * UMM_CRITICAL_ENTRY() and UMM_CRITICAL_EXIT(). */ -static void *umm_malloc_core( umm_heap_context_t *_context, size_t size ) { - uint16_t blocks; - uint16_t blockSize = 0; +static void *umm_malloc_core(umm_heap_context_t *_context, size_t size) { + uint16_t blocks; + uint16_t blockSize = 0; - uint16_t bestSize; - uint16_t bestBlock; + uint16_t bestSize; + uint16_t bestBlock; - uint16_t cf; + uint16_t cf; - STATS__ALLOC_REQUEST(id_malloc, size); + NON_NULL_CONTEXT_ASSERT(); - if (NULL == _context) { - panic(); - return NULL; - } + STATS__ALLOC_REQUEST(id_malloc, size); - blocks = umm_blocks( size ); + blocks = umm_blocks(size); - /* - * Now we can scan through the free list until we find a space that's big - * enough to hold the number of blocks we need. - * - * This part may be customized to be a best-fit, worst-fit, or first-fit - * algorithm - */ + /* + * Now we can scan through the free list until we find a space that's big + * enough to hold the number of blocks we need. + * + * This part may be customized to be a best-fit, worst-fit, or first-fit + * algorithm + */ - cf = UMM_NFREE(0); + cf = UMM_NFREE(0); - bestBlock = UMM_NFREE(0); - bestSize = 0x7FFF; + bestBlock = UMM_NFREE(0); + bestSize = 0x7FFF; - while( cf ) { - blockSize = (UMM_NBLOCK(cf) & UMM_BLOCKNO_MASK) - cf; + while (cf) { + blockSize = (UMM_NBLOCK(cf) & UMM_BLOCKNO_MASK) - cf; - DBGLOG_TRACE( "Looking at block %6d size %6d\n", cf, blockSize ); + DBGLOG_TRACE("Looking at block %6d size %6d\n", cf, blockSize); + + #if defined UMM_BEST_FIT + if ((blockSize >= blocks) && (blockSize < bestSize)) { + bestBlock = cf; + bestSize = blockSize; + } + #elif defined UMM_FIRST_FIT + /* This is the first block that fits! */ + if ((blockSize >= blocks)) { + break; + } + #else + #error "No UMM_*_FIT is defined - check umm_malloc_cfg.h" + #endif -#if defined UMM_BEST_FIT - if( (blockSize >= blocks) && (blockSize < bestSize) ) { - bestBlock = cf; - bestSize = blockSize; + cf = UMM_NFREE(cf); } -#elif defined UMM_FIRST_FIT - /* This is the first block that fits! */ - if( (blockSize >= blocks) ) - break; -#else -# error "No UMM_*_FIT is defined - check umm_malloc_cfg.h" -#endif - cf = UMM_NFREE(cf); - } + if (0x7FFF != bestSize) { + cf = bestBlock; + blockSize = bestSize; + } - if( 0x7FFF != bestSize ) { - cf = bestBlock; - blockSize = bestSize; - } + POISON_CHECK_NEIGHBORS(cf); - POISON_CHECK_NEIGHBORS(cf); + if (UMM_NBLOCK(cf) & UMM_BLOCKNO_MASK && blockSize >= blocks) { - if( UMM_NBLOCK(cf) & UMM_BLOCKNO_MASK && blockSize >= blocks ) { + UMM_FRAGMENTATION_METRIC_REMOVE(cf); - UMM_FRAGMENTATION_METRIC_REMOVE(cf); + /* + * This is an existing block in the memory heap, we just need to split off + * what we need, unlink it from the free list and mark it as in use, and + * link the rest of the block back into the freelist as if it was a new + * block on the free list... + */ - /* - * This is an existing block in the memory heap, we just need to split off - * what we need, unlink it from the free list and mark it as in use, and - * link the rest of the block back into the freelist as if it was a new - * block on the free list... - */ + if (blockSize == blocks) { + /* It's an exact fit and we don't need to split off a block. */ + DBGLOG_DEBUG("Allocating %6d blocks starting at %6d - exact\n", blocks, cf); + + /* Disconnect this block from the FREE list */ + + umm_disconnect_from_free_list(_context, cf); + + } else { - if( blockSize == blocks ) { - /* It's an exact fit and we don't neet to split off a block. */ - DBGLOG_DEBUG( "Allocating %6d blocks starting at %6d - exact\n", blocks, cf ); + /* It's not an exact fit and we need to split off a block. */ + DBGLOG_DEBUG("Allocating %6d blocks starting at %6d - existing\n", blocks, cf); - /* Disconnect this block from the FREE list */ + /* + * split current free block `cf` into two blocks. The first one will be + * returned to user, so it's not free, and the second one will be free. + */ + umm_split_block(_context, cf, blocks, UMM_FREELIST_MASK /*new block is free*/); - umm_disconnect_from_free_list( _context, cf ); + UMM_FRAGMENTATION_METRIC_ADD(UMM_NBLOCK(cf)); + /* + * `umm_split_block()` does not update the free pointers (it affects + * only free flags), but effectively we've just moved beginning of the + * free block from `cf` to `cf + blocks`. So we have to adjust pointers + * to and from adjacent free blocks. + */ + + /* previous free block */ + UMM_NFREE(UMM_PFREE(cf)) = cf + blocks; + UMM_PFREE(cf + blocks) = UMM_PFREE(cf); + + /* next free block */ + UMM_PFREE(UMM_NFREE(cf)) = cf + blocks; + UMM_NFREE(cf + blocks) = UMM_NFREE(cf); + } + + STATS__FREE_BLOCKS_UPDATE(-blocks); + STATS__FREE_BLOCKS_MIN(); } else { + /* Out of memory */ + STATS__OOM_UPDATE(); + + DBGLOG_DEBUG("Can't allocate %5d blocks\n", blocks); - /* It's not an exact fit and we need to split off a block. */ - DBGLOG_DEBUG( "Allocating %6d blocks starting at %6d - existing\n", blocks, cf ); + return (void *)NULL; + } + + return (void *)&UMM_DATA(cf); +} - /* - * split current free block `cf` into two blocks. The first one will be - * returned to user, so it's not free, and the second one will be free. - */ - umm_split_block( _context, cf, blocks, UMM_FREELIST_MASK /*new block is free*/ ); +/* ------------------------------------------------------------------------ */ - UMM_FRAGMENTATION_METRIC_ADD(UMM_NBLOCK(cf)); +void *umm_malloc(size_t size) { + UMM_CRITICAL_DECL(id_malloc); - /* - * `umm_split_block()` does not update the free pointers (it affects - * only free flags), but effectively we've just moved beginning of the - * free block from `cf` to `cf + blocks`. So we have to adjust pointers - * to and from adjacent free blocks. - */ + void *ptr = NULL; - /* previous free block */ - UMM_NFREE( UMM_PFREE(cf) ) = cf + blocks; - UMM_PFREE( cf + blocks ) = UMM_PFREE(cf); + UMM_CHECK_INITIALIZED(); - /* next free block */ - UMM_PFREE( UMM_NFREE(cf) ) = cf + blocks; - UMM_NFREE( cf + blocks ) = UMM_NFREE(cf); + /* + * "Is it safe" + * + * Is it safe to call from an ISR? Is there a point during a malloc that a + * an interrupt and subsequent call to malloc result in undesired results? + * + * Heap selection in managed by the functions umm_push_heap, umm_pop_heap, + * umm_get_current_heap_id, and umm_set_heap_by_id. These functions are + * responsible for getting/setting the module static variable umm_heap_cur. + * The umm_heap_cur variable is an index that is used to select the current + * heap context. Depending on the situation this selection can be overriddened. + * + * All variables for a specific Heap are in a single structure. `heap_context` + * is an array of these structures. Each heap API function uses a function + * local variable `_context` to hold a pointer to the selected heap structure. + * This local pointer is referenced for all the "selected heap" operations. + * Coupled with critical sections around global data should allow the API + * functions to be reentrant. + * + * Using the `_context` name throughout made it easy to incorporate the + * context into existing macros. + * + * For allocating APIs `umm_heap_cur` is used to index and select a value for + * `_context`. If an allocation is made from an ISR, this value is ignored and + * the heap context for DRAM is loaded. For APIs that require operating on an + * existing allocation such as realloc and free, the heap context selected is + * done by matching the allocation's address with that of one of the heap + * address ranges. + * + * I think we are safe with multiple heaps when the non32-bit exception + * handler is used, as long as interrupts don't get enabled. There was a + * window in the Boot ROM "C" Exception Wrapper that would enable interrupts + * when running our non32-exception handler; however, that should be resolved + * by our replacement wrapper. For more information on exception handling + * issues for IRAM see comments above `_set_exception_handler_wrapper()` in + * `core_esp8266_non32xfer.cpp`. + * + * ISRs should not try and change heaps. umm_malloc will ignore the change. + * All should be fine as long as the caller puts the heap back the way it was. + * On return, everything must be the same. The foreground thread will continue + * with the same information that was there before the interrupt. All malloc() + * requests made from an ISR are fulfilled with DRAM. + * + * For umm_malloc, heap selection involves changing a single variable that is + * on the calling context stack. From the umm_mallac side, that variable is + * used to load a context pointer by index, heap ID. While an umm_malloc API + * function is running, all heap related variables are in the context variable + * pointer, registers, or the current stack as the request is processed. With + * a single variable to reference for heap selection, I think it is unlikely + * that umm_malloc can be called, with things in an unusable transition state. + */ + + umm_heap_context_t *_context = umm_get_current_heap(); + + /* + * the very first thing we do is figure out if we're being asked to allocate + * a size of 0 - and if we are we'll simply return a null pointer. if not + * then reduce the size by 1 byte so that the subsequent calculations on + * the number of blocks to allocate are easier... + */ + + if (0 == size) { + DBGLOG_DEBUG("malloc a block of 0 bytes -> do nothing\n"); + STATS__ZERO_ALLOC_REQUEST(id_malloc, size); + + return ptr; } - STATS__FREE_BLOCKS_UPDATE( -blocks ); - STATS__FREE_BLOCKS_MIN(); - } else { - /* Out of memory */ - STATS__OOM_UPDATE(); + /* Allocate the memory within a protected critical section */ - DBGLOG_DEBUG( "Can't allocate %5d blocks\n", blocks ); + UMM_CRITICAL_ENTRY(id_malloc); - return( (void *)NULL ); - } + /* + * We handle the realloc of an existing IRAM allocation from an ISR with IRAM, + * while a new malloc from an ISR will always supply DRAM. That said, realloc + * from an ISR is not generally safe without special locking mechanisms and is + * not formally supported. + * + * Additionally, to avoid extending the IRQs disabled period, it is best to + * use DRAM for an ISR. Each 16-bit access to IRAM that umm_malloc has to make + * requires a pass through the exception handling logic. + */ + if (UMM_CRITICAL_WITHINISR(id_malloc)) { + _context = umm_get_heap_by_id(UMM_HEAP_DRAM); + } - return( (void *)&UMM_DATA(cf) ); -} + ptr = umm_malloc_core(_context, size); -/* ------------------------------------------------------------------------ */ + ptr = POISON_CHECK_SET_POISON(ptr, size); -void *umm_malloc( size_t size ) { - UMM_CRITICAL_DECL(id_malloc); - - void *ptr = NULL; - - UMM_INIT_HEAP; - - /* - * "Is it safe" - * - * Is it safe to call from an ISR? Is there a point during a malloc that a - * an interrupt and subsequent call to malloc result in undesired results? - * - * Heap selection in managed by the functions umm_push_heap, umm_pop_heap, - * umm_get_current_heap_id, and umm_set_heap_by_id. These functions are - * responsible for getting/setting the module static variable umm_heap_cur. - * The umm_heap_cur variable is an index that is used to select the current - * heap context. Depending on the situation this selection can be overriddened. - * - * All variables for a specific Heap are in a single structure. `heap_context` - * is an array of these structures. Each heap API function uses a function - * local variable `_context` to hold a pointer to the selected heap structure. - * This local pointer is referenced for all the "selected heap" operations. - * Coupled with critical sections around global data should allow the API - * functions to be reentrant. - * - * Using the `_context` name throughout made it easy to incorporate the - * context into existing macros. - * - * For allocating APIs `umm_heap_cur` is used to index and select a value for - * `_context`. If an allocation is made from an ISR, this value is ignored and - * the heap context for DRAM is loaded. For APIs that require operating on an - * existing allcation such as realloc and free, the heap context selected is - * done by matching the allocation's address with that of one of the heap - * address ranges. - * - * I think we are safe with multiple heaps when the non32-bit exception - * handler is used, as long as interrupts don't get enabled. There was a - * window in the Boot ROM "C" Exception Wrapper that would enable interrupts - * when running our non32-exception handler; however, that should be resolved - * by our replacement wrapper. For more information on exception handling - * issues for IRAM see comments above `_set_exception_handler_wrapper()` in - * `core_esp8266_non32xfer.cpp`. - * - * ISRs should not try and change heaps. umm_malloc will ignore the change. - * All should be fine as long as the caller puts the heap back the way it was. - * On return, everything must be the same. The foreground thread will continue - * with the same information that was there before the interrupt. All malloc() - * requests made from an ISR are fulfilled with DRAM. - * - * For umm_malloc, heap selection involves changing a single variable that is - * on the calling context stack. From the umm_mallac side, that variable is - * used to load a context pointer by index, heap ID. While an umm_malloc API - * function is running, all heap related variables are in the context variable - * pointer, registers, or the current stack as the request is processed. With - * a single variable to reference for heap selection, I think it is unlikely - * that umm_malloc can be called, with things in an unusable transition state. - */ - - umm_heap_context_t *_context = umm_get_current_heap(); - - /* - * the very first thing we do is figure out if we're being asked to allocate - * a size of 0 - and if we are we'll simply return a null pointer. if not - * then reduce the size by 1 byte so that the subsequent calculations on - * the number of blocks to allocate are easier... - */ - - if( 0 == size ) { - DBGLOG_DEBUG( "malloc a block of 0 bytes -> do nothing\n" ); - STATS__ZERO_ALLOC_REQUEST(id_malloc, size); - - return( ptr ); - } - - /* Allocate the memory withing a protected critical section */ - - UMM_CRITICAL_ENTRY(id_malloc); - - /* - * We handle the realloc of an existing IRAM allocation from an ISR with IRAM, - * while a new malloc from an ISR will always supply DRAM. That said, realloc - * from an ISR is not generally safe without special locking mechanisms and is - * not formally supported. - * - * Additionally, to avoid extending the IRQs disabled period, it is best to - * use DRAM for an ISR. Each 16-bit access to IRAM that umm_malloc has to make - * requires a pass through the exception handling logic. - */ - if (UMM_CRITICAL_WITHINISR(id_malloc)) { - _context = umm_get_heap_by_id(UMM_HEAP_DRAM); - } - - ptr = umm_malloc_core( _context, size ); - - UMM_CRITICAL_EXIT(id_malloc); - - return( ptr ); + UMM_CRITICAL_EXIT(id_malloc); + + return ptr; } /* ------------------------------------------------------------------------ */ -void *umm_realloc( void *ptr, size_t size ) { - UMM_CRITICAL_DECL(id_realloc); +void *umm_realloc(void *ptr, size_t size) { + UMM_CRITICAL_DECL(id_realloc); - uint16_t blocks; - uint16_t blockSize; - uint16_t prevBlockSize = 0; - uint16_t nextBlockSize = 0; + uint16_t blocks; + uint16_t blockSize; + uint16_t prevBlockSize = 0; + uint16_t nextBlockSize = 0; - uint16_t c; + uint16_t c; - size_t curSize; + [[maybe_unused]] size_t curSize; - UMM_INIT_HEAP; + UMM_CHECK_INITIALIZED(); - /* - * This code looks after the case of a NULL value for ptr. The ANSI C - * standard says that if ptr is NULL and size is non-zero, then we've - * got to work the same a malloc(). If size is also 0, then our version - * of malloc() returns a NULL pointer, which is OK as far as the ANSI C - * standard is concerned. - */ + /* + * This code looks after the case of a NULL value for ptr. The ANSI C + * standard says that if ptr is NULL and size is non-zero, then we've + * got to work the same a malloc(). If size is also 0, then our version + * of malloc() returns a NULL pointer, which is OK as far as the ANSI C + * standard is concerned. + */ - if( ((void *)NULL == ptr) ) { - DBGLOG_DEBUG( "realloc the NULL pointer - call malloc()\n" ); + if (((void *)NULL == ptr)) { + DBGLOG_DEBUG("realloc the NULL pointer - call malloc()\n"); - return( umm_malloc(size) ); - } + return umm_malloc(size); + } - /* - * Now we're sure that we have a non_NULL ptr, but we're not sure what - * we should do with it. If the size is 0, then the ANSI C standard says that - * we should operate the same as free. - */ + /* + * Now we're sure that we have a non_NULL ptr, but we're not sure what + * we should do with it. If the size is 0, then the ANSI C standard says that + * we should operate the same as free. + */ - /* Need to be in the heap in which this block lives */ - umm_heap_context_t *_context = umm_get_ptr_context( ptr ); - if (NULL == _context) { - panic(); - return NULL; - } + /* Need to be in the heap in which this block lives */ + umm_heap_context_t *_context = umm_get_ptr_context(ptr); + NON_NULL_CONTEXT_ASSERT(); - if( 0 == size ) { - DBGLOG_DEBUG( "realloc to 0 size, just free the block\n" ); - STATS__ZERO_ALLOC_REQUEST(id_realloc, size); + if (0 == size) { + DBGLOG_DEBUG("realloc to 0 size, just free the block\n"); + STATS__ZERO_ALLOC_REQUEST(id_realloc, size); - umm_free( ptr ); + umm_free(ptr); - return( (void *)NULL ); - } + return (void *)NULL; + } - STATS__ALLOC_REQUEST(id_realloc, size); + STATS__ALLOC_REQUEST(id_realloc, size); - /* - * Otherwise we need to actually do a reallocation. A naiive approach - * would be to malloc() a new block of the correct size, copy the old data - * to the new block, and then free the old block. - * - * While this will work, we end up doing a lot of possibly unnecessary - * copying. So first, let's figure out how many blocks we'll need. - */ + /* + * Otherwise we need to actually do a reallocation. A naiive approach + * would be to malloc() a new block of the correct size, copy the old data + * to the new block, and then free the old block. + * + * While this will work, we end up doing a lot of possibly unnecessary + * copying. So first, let's figure out how many blocks we'll need. + */ - blocks = umm_blocks( size ); + blocks = umm_blocks(size); - /* Figure out which block we're in. Note the use of truncated division... */ + /* Figure out which block we're in. Note the use of truncated division... */ - c = (((uintptr_t)ptr)-(uintptr_t)(&(_context->heap[0])))/sizeof(umm_block); + c = (((uintptr_t)ptr) - (uintptr_t)(&(_context->heap[0]))) / sizeof(umm_block); - /* Figure out how big this block is ... the free bit is not set :-) */ + /* Figure out how big this block is ... the free bit is not set :-) */ - blockSize = (UMM_NBLOCK(c) - c); + blockSize = (UMM_NBLOCK(c) - c); - /* Figure out how many bytes are in this block */ + /* Figure out how many bytes are in this block */ - curSize = (blockSize*sizeof(umm_block))-(sizeof(((umm_block *)0)->header)); + curSize = (blockSize * sizeof(umm_block)) - (sizeof(((umm_block *)0)->header)); - /* Protect the critical section... */ - UMM_CRITICAL_ENTRY(id_realloc); + /* Protect the critical section... */ + UMM_CRITICAL_ENTRY(id_realloc); - /* Now figure out if the previous and/or next blocks are free as well as - * their sizes - this will help us to minimize special code later when we - * decide if it's possible to use the adjacent blocks. - * - * We set prevBlockSize and nextBlockSize to non-zero values ONLY if they - * are free! - */ + /* Now figure out if the previous and/or next blocks are free as well as + * their sizes - this will help us to minimize special code later when we + * decide if it's possible to use the adjacent blocks. + * + * We set prevBlockSize and nextBlockSize to non-zero values ONLY if they + * are free! + */ - if ((UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_FREELIST_MASK)) { - nextBlockSize = (UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_BLOCKNO_MASK) - UMM_NBLOCK(c); - } + if ((UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_FREELIST_MASK)) { + nextBlockSize = (UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_BLOCKNO_MASK) - UMM_NBLOCK(c); + } - if ((UMM_NBLOCK(UMM_PBLOCK(c)) & UMM_FREELIST_MASK)) { - prevBlockSize = (c - UMM_PBLOCK(c)); - } + if ((UMM_NBLOCK(UMM_PBLOCK(c)) & UMM_FREELIST_MASK)) { + prevBlockSize = (c - UMM_PBLOCK(c)); + } - DBGLOG_DEBUG( "realloc blocks %d blockSize %d nextBlockSize %d prevBlockSize %d\n", blocks, blockSize, nextBlockSize, prevBlockSize ); + DBGLOG_DEBUG("realloc blocks %d blockSize %d nextBlockSize %d prevBlockSize %d\n", blocks, blockSize, nextBlockSize, prevBlockSize); -//C With each upstream update this section should be reevaluated. +// C With each upstream update this section should be reevaluated. /*C * * The `#if defined(UMM_REALLOC_MINIMIZE_COPY)` section tracks the content of @@ -936,107 +1026,111 @@ void *umm_realloc( void *ptr, size_t size ) { * confirm; however, I think this to be the best option when considering the * amount of reallocates that can occur with the Strings library. */ -#if defined(UMM_REALLOC_MINIMIZE_COPY) - /* - * Ok, now that we're here we know how many blocks we want and the current - * blockSize. The prevBlockSize and nextBlockSize are set and we can figure - * out the best strategy for the new allocation as follows: - * - * 1. If the new block is the same size or smaller than the current block do - * nothing. - * 2. If the next block is free and adding it to the current block gives us - * EXACTLY enough memory, assimilate the next block. This avoids unwanted - * fragmentation of free memory. - * - * The following cases may be better handled with memory copies to reduce - * fragmentation - * - * 3. If the previous block is NOT free and the next block is free and - * adding it to the current block gives us enough memory, assimilate - * the next block. This may introduce a bit of fragmentation. - * 4. If the prev block is free and adding it to the current block gives us - * enough memory, remove the previous block from the free list, assimilate - * it, copy to the new block. - * 5. If the prev and next blocks are free and adding them to the current - * block gives us enough memory, assimilate the next block, remove the - * previous block from the free list, assimilate it, copy to the new block. - * 6. Otherwise try to allocate an entirely new block of memory. If the - * allocation works free the old block and return the new pointer. If - * the allocation fails, return NULL and leave the old block intact. - * - * TODO: Add some conditional code to optimise for less fragmentation - * by simply allocating new memory if we need to copy anyways. - * - * All that's left to do is decide if the fit was exact or not. If the fit - * was not exact, then split the memory block so that we use only the requested - * number of blocks and add what's left to the free list. - */ + #if defined(UMM_REALLOC_MINIMIZE_COPY) + /* + * Ok, now that we're here we know how many blocks we want and the current + * blockSize. The prevBlockSize and nextBlockSize are set and we can figure + * out the best strategy for the new allocation as follows: + * + * 1. If the new block is the same size or smaller than the current block do + * nothing. + * 2. If the next block is free and adding it to the current block gives us + * EXACTLY enough memory, assimilate the next block. This avoids unwanted + * fragmentation of free memory. + * + * The following cases may be better handled with memory copies to reduce + * fragmentation + * + * 3. If the previous block is NOT free and the next block is free and + * adding it to the current block gives us enough memory, assimilate + * the next block. This may introduce a bit of fragmentation. + * 4. If the prev block is free and adding it to the current block gives us + * enough memory, remove the previous block from the free list, assimilate + * it, copy to the new block. + * 5. If the prev and next blocks are free and adding them to the current + * block gives us enough memory, assimilate the next block, remove the + * previous block from the free list, assimilate it, copy to the new block. + * 6. Otherwise try to allocate an entirely new block of memory. If the + * allocation works free the old block and return the new pointer. If + * the allocation fails, return NULL and leave the old block intact. + * + * TODO: Add some conditional code to optimise for less fragmentation + * by simply allocating new memory if we need to copy anyways. + * + * All that's left to do is decide if the fit was exact or not. If the fit + * was not exact, then split the memory block so that we use only the requested + * number of blocks and add what's left to the free list. + */ // Case 1 - block is same size or smaller if (blockSize >= blocks) { - DBGLOG_DEBUG( "realloc the same or smaller size block - %i, do nothing\n", blocks ); + DBGLOG_DEBUG("realloc the same or smaller size block - %i, do nothing\n", blocks); /* This space intentionally left blank */ - // Case 2 - block + next block fits EXACTLY + // 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 ); - STATS__FREE_BLOCKS_UPDATE( - nextBlockSize ); + DBGLOG_DEBUG("exact realloc using next block - %i\n", blocks); + umm_assimilate_up(_context, c); + STATS__FREE_BLOCKS_UPDATE(-nextBlockSize); blockSize += nextBlockSize; - // Case 3 - prev block NOT free and block + next block fits + // Case 3 - prev block NOT free and block + next block fits } else if ((0 == prevBlockSize) && (blockSize + nextBlockSize) >= blocks) { - DBGLOG_DEBUG( "realloc using next block - %i\n", blocks ); - umm_assimilate_up( _context, c ); - STATS__FREE_BLOCKS_UPDATE( - nextBlockSize ); + DBGLOG_DEBUG("realloc using next block - %i\n", blocks); + umm_assimilate_up(_context, c); + STATS__FREE_BLOCKS_UPDATE(-nextBlockSize); blockSize += nextBlockSize; - // Case 4 - prev block + block fits + // Case 4 - prev block + block fits } else if ((prevBlockSize + blockSize) >= blocks) { - DBGLOG_DEBUG( "realloc using prev block - %i\n", blocks ); - umm_disconnect_from_free_list( _context, UMM_PBLOCK(c) ); + DBGLOG_DEBUG("realloc using prev block - %i\n", blocks); + umm_disconnect_from_free_list(_context, UMM_PBLOCK(c)); c = umm_assimilate_down(_context, c, 0); - STATS__FREE_BLOCKS_UPDATE( - prevBlockSize ); + 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 + // Case 5 - prev block + block + next block fits } else if ((prevBlockSize + blockSize + nextBlockSize) >= blocks) { - DBGLOG_DEBUG( "realloc using prev and next block - %d\n", blocks ); - umm_assimilate_up( _context, c ); - umm_disconnect_from_free_list( _context, UMM_PBLOCK(c) ); + DBGLOG_DEBUG("realloc using prev and next block - %d\n", blocks); + umm_assimilate_up(_context, c); + umm_disconnect_from_free_list(_context, UMM_PBLOCK(c)); c = umm_assimilate_down(_context, c, 0); - STATS__FREE_BLOCKS_UPDATE( - prevBlockSize - nextBlockSize ); -#ifdef UMM_LIGHTWEIGHT_CPU + STATS__FREE_BLOCKS_UPDATE(-prevBlockSize - nextBlockSize); + #ifdef UMM_LIGHTWEIGHT_CPU if ((prevBlockSize + blockSize + nextBlockSize) > blocks) { - umm_split_block( _context, c, blocks, 0 ); - umm_free_core( _context, (void *)&UMM_DATA(c+blocks) ); + umm_split_block(_context, c, blocks, 0); + umm_free_core(_context, (void *)&UMM_DATA(c + blocks)); } STATS__FREE_BLOCKS_ISR_MIN(); blockSize = blocks; -#else + #else blockSize += (prevBlockSize + nextBlockSize); -#endif + #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); - // Case 6 - default is we need to realloc a new block + // Case 6 - default is we need to realloc a new block } else { - DBGLOG_DEBUG( "realloc a completely new block %i\n", blocks ); + DBGLOG_DEBUG("realloc a completely new block %i\n", blocks); 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 ); + 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 ); + umm_free_core(_context, oldptr); } else { - DBGLOG_DEBUG( "realloc %i to a bigger block %i failed - return NULL and leave the old block!\n", blockSize, blocks ); + DBGLOG_DEBUG("realloc %i to a bigger block %i failed - return NULL and leave the old block!\n", blockSize, blocks); /* This space intentionally left blnk */ /* STATS__OOM_UPDATE() has already been called by umm_malloc_core - don't duplicate count */ } @@ -1045,77 +1139,80 @@ void *umm_realloc( void *ptr, size_t size ) { */ blockSize = blocks; } -#elif defined(UMM_REALLOC_DEFRAG) - /* - * Ok, now that we're here we know how many blocks we want and the current - * blockSize. The prevBlockSize and nextBlockSize are set and we can figure - * out the best strategy for the new allocation. The following strategy is - * focused on defragging the heap: - * - * 1. If the prev is free and adding it to the current, or current and next - * block, gives us enough memory, proceed. Note, that next block may not - * be available. - * a. Remove the previous block from the free list, assimilate it. - * b. If this new block gives enough memory, copy to the new block. - * Note, this includes the case of same size or smaller block. - * c. Else assimilate the next block, copy to the new block. - * 2. If the new block is the same size or smaller than the current block do - * nothing. - * 3. If the next block is free and adding it to the current block gives us - * enough memory, assimilate the next block. - * 4. Otherwise try to allocate an entirely new block of memory. If the - * allocation works free the old block and return the new pointer. If - * the allocation fails, return NULL and leave the old block intact. - * - * All that's left to do is decide if the fit was exact or not. If the fit - * was not exact, then split the memory block so that we use only the - * requested number of blocks and add what's left to the free list. - */ - if (prevBlockSize && (prevBlockSize + blockSize + nextBlockSize) >= blocks) { // 1 - umm_disconnect_from_free_list( _context, UMM_PBLOCK(c) ); - c = umm_assimilate_down( _context, c, 0 ); - STATS__FREE_BLOCKS_UPDATE( - prevBlockSize ); + #elif defined(UMM_REALLOC_DEFRAG) + /* + * Ok, now that we're here we know how many blocks we want and the current + * blockSize. The prevBlockSize and nextBlockSize are set and we can figure + * out the best strategy for the new allocation. The following strategy is + * focused on defragging the heap: + * + * 1. If the prev is free and adding it to the current, or current and next + * block, gives us enough memory, proceed. Note, that next block may not + * be available. + * a. Remove the previous block from the free list, assimilate it. + * b. If this new block gives enough memory, copy to the new block. + * Note, this includes the case of same size or smaller block. + * c. Else assimilate the next block, copy to the new block. + * 2. If the new block is the same size or smaller than the current block do + * nothing. + * 3. If the next block is free and adding it to the current block gives us + * enough memory, assimilate the next block. + * 4. Otherwise try to allocate an entirely new block of memory. If the + * allocation works free the old block and return the new pointer. If + * the allocation fails, return NULL and leave the old block intact. + * + * All that's left to do is decide if the fit was exact or not. If the fit + * was not exact, then split the memory block so that we use only the + * requested number of blocks and add what's left to the free list. + */ + if (prevBlockSize && (prevBlockSize + blockSize + nextBlockSize) >= blocks) { // 1 + umm_disconnect_from_free_list(_context, UMM_PBLOCK(c)); + c = umm_assimilate_down(_context, c, 0); + STATS__FREE_BLOCKS_UPDATE(-prevBlockSize); blockSize += prevBlockSize; if (blockSize >= blocks) { - DBGLOG_DEBUG( "realloc using prev block - %d\n", blocks ); + DBGLOG_DEBUG("realloc using prev block - %d\n", blocks); STATS__FREE_BLOCKS_ISR_MIN(); } else { - DBGLOG_DEBUG( "realloc using prev and next block - %d\n", blocks ); - umm_assimilate_up( _context, c ); - STATS__FREE_BLOCKS_UPDATE( - nextBlockSize ); + DBGLOG_DEBUG("realloc using prev and next block - %d\n", blocks); + umm_assimilate_up(_context, c); + STATS__FREE_BLOCKS_UPDATE(-nextBlockSize); blockSize += nextBlockSize; -#ifdef UMM_LIGHTWEIGHT_CPU + #ifdef UMM_LIGHTWEIGHT_CPU if (blockSize > blocks) { - umm_split_block( _context, c, blocks, 0 ); - umm_free_core( _context, (void *)&UMM_DATA(c+blocks) ); + umm_split_block(_context, c, blocks, 0); + umm_free_core(_context, (void *)&UMM_DATA(c + blocks)); } STATS__FREE_BLOCKS_ISR_MIN(); blockSize = blocks; -#endif + #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 - DBGLOG_DEBUG( "realloc the same or smaller size block - %d, do nothing\n", blocks ); + DBGLOG_DEBUG("realloc the same or smaller size block - %d, do nothing\n", blocks); /* This space intentionally left blank */ } else if ((blockSize + nextBlockSize) >= blocks) { // 3 - DBGLOG_DEBUG( "realloc using next block - %d\n", blocks ); - umm_assimilate_up( _context, c ); - STATS__FREE_BLOCKS_UPDATE( - nextBlockSize ); + DBGLOG_DEBUG("realloc using next block - %d\n", blocks); + umm_assimilate_up(_context, c); + STATS__FREE_BLOCKS_UPDATE(-nextBlockSize); blockSize += nextBlockSize; } else { // 4 - DBGLOG_DEBUG( "realloc a completely new block %d\n", blocks ); + DBGLOG_DEBUG("realloc a completely new block %d\n", blocks); 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 ); + 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); + umm_free_core(_context, oldptr); } else { - DBGLOG_DEBUG( "realloc %d to a bigger block %d failed - return NULL and leave the old block!\n", blockSize, blocks ); + DBGLOG_DEBUG("realloc %d to a bigger block %d failed - return NULL and leave the old block!\n", blockSize, blocks); /* This space intentionally left blnk */ /* STATS__OOM_UPDATE() has already been called by umm_malloc_core - don't duplicate count */ } @@ -1124,23 +1221,24 @@ void *umm_realloc( void *ptr, size_t size ) { */ blockSize = blocks; } -#else -#warning "Neither UMM_REALLOC_DEFRAG nor UMM_REALLOC_MINIMIZE_COPY is defined - check umm_malloc_cfg.h" + #else + #warning "Neither UMM_REALLOC_DEFRAG nor UMM_REALLOC_MINIMIZE_COPY is defined - check umm_malloc_cfg.h" /* An always copy option just for performance/fragmentation comparison */ if (blockSize >= blocks) { - DBGLOG_DEBUG( "realloc the same or smaller size block - %d, do nothing\n", blocks ); + DBGLOG_DEBUG("realloc the same or smaller size block - %d, do nothing\n", blocks); /* This space intentionally left blank */ } else { - DBGLOG_DEBUG( "realloc a completely new block %d\n", blocks ); + DBGLOG_DEBUG("realloc a completely new block %d\n", blocks); 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 ); + 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 ); + umm_free_core(_context, oldptr); } else { - DBGLOG_DEBUG( "realloc %d to a bigger block %d failed - return NULL and leave the old block!\n", blockSize, blocks ); + DBGLOG_DEBUG("realloc %d to a bigger block %d failed - return NULL and leave the old block!\n", blockSize, blocks); /* This space intentionally left blnk */ /* STATS__OOM_UPDATE() has already been called by umm_malloc_core - don't duplicate count */ } @@ -1149,37 +1247,46 @@ void *umm_realloc( void *ptr, size_t size ) { */ blockSize = blocks; } -#endif + #endif /* Now all we need to do is figure out if the block fit exactly or if we * need to split and free ... */ - if (blockSize > blocks ) { - DBGLOG_DEBUG( "split and free %d blocks from %d\n", blocks, blockSize ); - umm_split_block( _context, c, blocks, 0 ); - umm_free_core( _context, (void *)&UMM_DATA(c+blocks) ); + if (blockSize > blocks) { + DBGLOG_DEBUG("split and free %d blocks from %d\n", blocks, blockSize); + umm_split_block(_context, c, blocks, 0); + umm_free_core(_context, (void *)&UMM_DATA(c + blocks)); } STATS__FREE_BLOCKS_MIN(); + ptr = POISON_CHECK_SET_POISON(ptr, size); + /* Release the critical section... */ UMM_CRITICAL_EXIT(id_realloc); - return( ptr ); + return ptr; } /* ------------------------------------------------------------------------ */ -void *umm_calloc( size_t num, size_t item_size ) { - void *ret; +#if !defined(UMM_POISON_CHECK) && !defined(UMM_POISON_CHECK_LITE) +void *umm_calloc(size_t num, size_t item_size) { + void *ret; - ret = umm_malloc((size_t)(item_size * num)); + // Use saturated multiply. + // Rely on umm_malloc to supply the fail response as needed. + size_t size = umm_umul_sat(num, item_size); - if (ret) - memset(ret, 0x00, (size_t)(item_size * num)); + ret = umm_malloc(size); - return ret; + if (ret) { + memset(ret, 0x00, size); + } + + return ret; } +#endif /* ------------------------------------------------------------------------ */ diff --git a/cores/esp8266/umm_malloc/umm_malloc.h b/cores/esp8266/umm_malloc/umm_malloc.h index f7dcc72c1b..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" { @@ -19,28 +21,28 @@ extern "C" { #ifdef UMM_HEAP_EXTERNAL -extern void umm_init_vm( void *vmaddr, unsigned int vmsize ); +extern void umm_init_vm(void *vmaddr, unsigned int vmsize); #endif #ifdef UMM_HEAP_IRAM extern void umm_init_iram(void); -extern void umm_init_iram_ex( void *addr, unsigned int size, bool zero ); +extern void umm_init_iram_ex(void *addr, unsigned int size, bool zero); #endif /* ------------------------------------------------------------------------ */ -extern void umm_init( void ); -extern void *umm_malloc( size_t size ); -extern void *umm_calloc( size_t num, size_t size ); -extern void *umm_realloc( void *ptr, size_t size ); -extern void umm_free( void *ptr ); +extern void umm_init(void); +extern void *umm_malloc(size_t size); +extern void *umm_calloc(size_t num, size_t size); +extern void *umm_realloc(void *ptr, size_t size); +extern void umm_free(void *ptr); /* ------------------------------------------------------------------------ */ -extern umm_heap_context_t *umm_push_heap( size_t heap_number ); -extern umm_heap_context_t *umm_pop_heap( void ); -extern int umm_get_heap_stack_index( void ); -extern umm_heap_context_t *umm_set_heap_by_id( size_t which ); -extern size_t umm_get_current_heap_id( void ); -extern umm_heap_context_t *umm_get_current_heap( void ); +extern umm_heap_context_t *umm_push_heap(size_t heap_number); +extern umm_heap_context_t *umm_pop_heap(void); +extern int umm_get_heap_stack_index(void); +extern umm_heap_context_t *umm_set_heap_by_id(size_t which); +extern size_t umm_get_current_heap_id(void); +extern umm_heap_context_t *umm_get_current_heap(void); #ifdef __cplusplus } diff --git a/cores/esp8266/umm_malloc/umm_malloc_cfg.h b/cores/esp8266/umm_malloc/umm_malloc_cfg.h index 1e5d7f8003..bcc355f893 100644 --- a/cores/esp8266/umm_malloc/umm_malloc_cfg.h +++ b/cores/esp8266/umm_malloc/umm_malloc_cfg.h @@ -1,14 +1,16 @@ /* - * Configuration for umm_malloc - target Arduino ESP8266 core - * - * Changes specific to a target platform go here. - * - * This comment section changed to below in the upstream version, keeping old method for now. - * * Configuration for umm_malloc - DO NOT EDIT THIS FILE BY HAND! * - * Refer to the notes below for how to configure the build at compile time - * using -D to define non-default values + * NOTE WELL: Your project MUST have a umm_malloc_cfgport.h - even if + * it's empty!!! + * + * Refer to the notes below for details on the umm_malloc configuration + * options. + */ + +/* + * Minimized changes in umm_malloc_cfg.h, transition Arduino ESP8266 specific + * changes to umm_malloc_cfgport.h. */ #ifndef _UMM_MALLOC_CFG_H @@ -18,61 +20,10 @@ #include #include -#include -#include -#include "../debug.h" -#include "../esp8266_undocumented.h" - #ifdef __cplusplus extern "C" { #endif -#include -#include -#include - -#include "c_types.h" - -/* - * Define active Heaps - */ -#if defined(MMU_IRAM_HEAP) -#define UMM_HEAP_IRAM -#else -#undef UMM_HEAP_IRAM -#endif - -// #define UMM_HEAP_EXTERNAL - -/* - * Assign IDs to active Heaps and tally. DRAM is always active. - */ -#define UMM_HEAP_DRAM 0 -#define UMM_HEAP_DRAM_DEFINED 1 - -#ifdef UMM_HEAP_IRAM -#undef UMM_HEAP_IRAM -#define UMM_HEAP_IRAM_DEFINED 1 -#define UMM_HEAP_IRAM UMM_HEAP_DRAM_DEFINED -#else -#define UMM_HEAP_IRAM_DEFINED 0 -#endif - -#ifdef UMM_HEAP_EXTERNAL -#undef UMM_HEAP_EXTERNAL -#define UMM_HEAP_EXTERNAL_DEFINED 1 -#define UMM_HEAP_EXTERNAL (UMM_HEAP_DRAM_DEFINED + UMM_HEAP_IRAM_DEFINED) -#else -#define UMM_HEAP_EXTERNAL_DEFINED 0 -#endif - -#define UMM_NUM_HEAPS (UMM_HEAP_DRAM_DEFINED + UMM_HEAP_IRAM_DEFINED + UMM_HEAP_EXTERNAL_DEFINED) - -#if (UMM_NUM_HEAPS == 1) -#else -#define UMM_HEAP_STACK_DEPTH 32 -#endif - /* * There are a number of defines you can set at compile time that affect how * the memory allocator will operate. @@ -112,6 +63,16 @@ extern "C" { * Setting this at compile time will automatically set UMM_INFO. * Note that enabling this define will add a slight runtime penalty. * + * UMM_CHECK_INITIALIZED + * + * Set if you want to be able to verify that the heap is intialized + * before any operation - the default is no check. You may set the + * UMM_CHECK_INITIALIZED macro to the following provided macros, or + * write your own handler: + * + * UMM_INIT_IF_UNINITIALIZED + * UMM_HANG_IF_UNINITIALIZED + * * UMM_INTEGRITY_CHECK * * Set if you want to be able to verify that the heap is semantically correct @@ -139,45 +100,10 @@ extern "C" { * ---------------------------------------------------------------------------- */ -#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. - * - */ - - -#ifdef UMM_TEST_BUILD -extern char test_umm_heap[]; -#endif - -#ifdef UMM_TEST_BUILD -/* Start addresses and the size of the heap */ -#define UMM_MALLOC_CFG_HEAP_ADDR (test_umm_heap) -#define UMM_MALLOC_CFG_HEAP_SIZE 0x10000 +#ifdef UMM_CFGFILE +#include UMM_CFGFILE #else -/* Start addresses and the size of the heap */ -extern char _heap_start[]; -#define UMM_HEAP_END_ADDR 0x3FFFC000UL -#define UMM_MALLOC_CFG_HEAP_ADDR ((uint32_t)&_heap_start[0]) -#define UMM_MALLOC_CFG_HEAP_SIZE ((size_t)(UMM_HEAP_END_ADDR - UMM_MALLOC_CFG_HEAP_ADDR)) +#include "umm_malloc_cfgport.h" #endif /* A couple of macros to make packing structures less compiler dependent */ @@ -187,14 +113,28 @@ extern char _heap_start[]; /* -------------------------------------------------------------------------- */ +#ifndef UMM_INIT_IF_UNINITIALIZED + #define UMM_INIT_IF_UNINITIALIZED() do { if (UMM_HEAP == NULL) { umm_init(); } } while (0) +#endif + +#ifndef UMM_HANG_IF_UNINITIALIZED + #define UMM_HANG_IF_UNINITIALIZED() do { if (UMM_HEAP == NULL) { while (1) {} } } while (0) +#endif + +#ifndef UMM_CHECK_INITIALIZED + #define UMM_CHECK_INITIALIZED() +#endif + +/* -------------------------------------------------------------------------- */ + #ifdef UMM_BEST_FIT - #ifdef UMM_FIRST_FIT - #error Both UMM_BEST_FIT and UMM_FIRST_FIT are defined - pick one! - #endif +#ifdef UMM_FIRST_FIT +#error Both UMM_BEST_FIT and UMM_FIRST_FIT are defined - pick one! +#endif #else /* UMM_BEST_FIT is not defined */ - #ifndef UMM_FIRST_FIT +#ifndef UMM_FIRST_FIT #define UMM_BEST_FIT - #endif +#endif #endif /* -------------------------------------------------------------------------- */ @@ -203,21 +143,31 @@ extern char _heap_start[]; #define UMM_FRAGMENTATION_METRIC_INIT() umm_fragmentation_metric_init(_context) #define UMM_FRAGMENTATION_METRIC_ADD(c) umm_fragmentation_metric_add(_context, c) #define UMM_FRAGMENTATION_METRIC_REMOVE(c) umm_fragmentation_metric_remove(_context, c) - #ifndef UMM_INFO +#ifndef UMM_INFO #define UMM_INFO - #endif +#endif #else #define UMM_FRAGMENTATION_METRIC_INIT() #define UMM_FRAGMENTATION_METRIC_ADD(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! */ @@ -225,7 +175,7 @@ extern char _heap_start[]; // #define UMM_INFO #ifdef UMM_INFO - typedef struct UMM_HEAP_INFO_t { +typedef struct UMM_HEAP_INFO_t { unsigned int totalEntries; unsigned int usedEntries; unsigned int freeEntries; @@ -234,42 +184,33 @@ extern char _heap_start[]; unsigned int usedBlocks; unsigned int freeBlocks; unsigned int freeBlocksSquared; -#ifdef UMM_INLINE_METRICS + #ifdef UMM_INLINE_METRICS size_t oom_count; #define UMM_OOM_COUNT info.oom_count #define UMM_FREE_BLOCKS info.freeBlocks -#endif + #endif unsigned int maxFreeContiguousBlocks; - } - UMM_HEAP_INFO; - - // extern UMM_HEAP_INFO ummHeapInfo; -struct UMM_HEAP_CONTEXT; -typedef struct UMM_HEAP_CONTEXT umm_heap_context_t; +} +UMM_HEAP_INFO; - extern ICACHE_FLASH_ATTR void *umm_info( void *ptr, bool force ); -#ifdef UMM_INLINE_METRICS - extern size_t umm_free_heap_size( void ); -#else - extern ICACHE_FLASH_ATTR size_t umm_free_heap_size( void ); -#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 ); +// extern UMM_HEAP_INFO ummHeapInfo; +extern ICACHE_FLASH_ATTR void *umm_info(void *ptr, bool force); +#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 - #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) +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_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); #endif /* @@ -308,27 +249,27 @@ typedef struct UMM_HEAP_CONTEXT umm_heap_context_t; #if defined(UMM_STATS) || defined(UMM_STATS_FULL) typedef struct UMM_STATISTICS_t { -#ifndef UMM_INLINE_METRICS + #ifndef UMM_INLINE_METRICS // If we are doing UMM_INLINE_METRICS, we can move oom_count and free_blocks to // umm_info's structure and save a little DRAM and IRAM. // Otherwise it is defined here. - size_t free_blocks; - size_t oom_count; + size_t free_blocks; + size_t oom_count; #define UMM_OOM_COUNT stats.oom_count #define UMM_FREE_BLOCKS stats.free_blocks -#endif -#ifdef UMM_STATS_FULL - size_t free_blocks_min; - size_t free_blocks_isr_min; - size_t alloc_max_size; - size_t last_alloc_size; - size_t id_malloc_count; - size_t id_malloc_zero_count; - size_t id_realloc_count; - size_t id_realloc_zero_count; - size_t id_free_count; - size_t id_free_null_count; -#endif + #endif + #ifdef UMM_STATS_FULL + size_t free_blocks_min; + size_t free_blocks_isr_min; + size_t alloc_max_size; + size_t last_alloc_size; + size_t id_malloc_count; + size_t id_malloc_zero_count; + size_t id_realloc_count; + size_t id_realloc_zero_count; + size_t id_free_count; + size_t id_free_null_count; + #endif } UMM_STATISTICS; @@ -340,8 +281,7 @@ 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 ); +extern size_t umm_get_oom_count(void); #else // not UMM_STATS or UMM_STATS_FULL #define STATS__FREE_BLOCKS_UPDATE(s) (void)(s) @@ -349,59 +289,62 @@ extern size_t umm_get_oom_count( void ); #endif #if defined(UMM_STATS) || defined(UMM_STATS_FULL) || defined(UMM_INFO) -size_t ICACHE_FLASH_ATTR umm_block_size( void ); +size_t ICACHE_FLASH_ATTR umm_block_size(void); #endif #ifdef UMM_STATS_FULL #define STATS__FREE_BLOCKS_MIN() \ -do { \ - if (_context->UMM_FREE_BLOCKS < _context->stats.free_blocks_min) \ - _context->stats.free_blocks_min = _context->UMM_FREE_BLOCKS; \ -} while(false) + do { \ + if (_context->UMM_FREE_BLOCKS < _context->stats.free_blocks_min) { \ + _context->stats.free_blocks_min = _context->UMM_FREE_BLOCKS; \ + } \ + } while (false) #define STATS__FREE_BLOCKS_ISR_MIN() \ -do { \ - if (_context->UMM_FREE_BLOCKS < _context->stats.free_blocks_isr_min) \ - _context->stats.free_blocks_isr_min = _context->UMM_FREE_BLOCKS; \ -} while(false) + do { \ + if (_context->UMM_FREE_BLOCKS < _context->stats.free_blocks_isr_min) { \ + _context->stats.free_blocks_isr_min = _context->UMM_FREE_BLOCKS; \ + } \ + } while (false) #define STATS__ALLOC_REQUEST(tag, s) \ -do { \ - _context->stats.tag##_count += 1; \ - _context->stats.last_alloc_size = s; \ - if (_context->stats.alloc_max_size < s) \ - _context->stats.alloc_max_size = s; \ -} while(false) + do { \ + _context->stats.tag##_count += 1; \ + _context->stats.last_alloc_size = s; \ + if (_context->stats.alloc_max_size < s) { \ + _context->stats.alloc_max_size = s; \ + } \ + } while (false) #define STATS__ZERO_ALLOC_REQUEST(tag, s) \ -do { \ - _context->stats.tag##_zero_count += 1; \ -} while(false) + do { \ + _context->stats.tag##_zero_count += 1; \ + } while (false) #define STATS__NULL_FREE_REQUEST(tag) \ -do { \ - umm_heap_context_t *_context = umm_get_current_heap(); \ - _context->stats.tag##_null_count += 1; \ -} while(false) + do { \ + umm_heap_context_t *_context = umm_get_current_heap(); \ + _context->stats.tag##_null_count += 1; \ + } while (false) #define STATS__FREE_REQUEST(tag) \ -do { \ - _context->stats.tag##_count += 1; \ -} while(false) - - -size_t umm_free_heap_size_lw_min( void ); -size_t umm_free_heap_size_min_reset( void ); -size_t umm_free_heap_size_min( void ); -size_t umm_free_heap_size_isr_min( void ); -size_t umm_get_max_alloc_size( void ); -size_t umm_get_last_alloc_size( void ); -size_t umm_get_malloc_count( void ); -size_t umm_get_malloc_zero_count( void ); -size_t umm_get_realloc_count( void ); -size_t umm_get_realloc_zero_count( void ); -size_t umm_get_free_count( void ); -size_t umm_get_free_null_count( void ); + do { \ + _context->stats.tag##_count += 1; \ + } while (false) + + +size_t umm_free_heap_size_lw_min(void); +size_t umm_free_heap_size_min_reset(void); +size_t umm_free_heap_size_min(void); +size_t umm_free_heap_size_isr_min(void); +size_t umm_get_max_alloc_size(void); +size_t umm_get_last_alloc_size(void); +size_t umm_get_malloc_count(void); +size_t umm_get_malloc_zero_count(void); +size_t umm_get_realloc_count(void); +size_t umm_get_realloc_zero_count(void); +size_t umm_get_free_count(void); +size_t umm_get_free_null_count(void); #else // Not UMM_STATS_FULL #define STATS__FREE_BLOCKS_MIN() (void)0 @@ -444,10 +387,10 @@ size_t umm_get_free_null_count( void ); // This option adds support for gathering time locked data typedef struct UMM_TIME_STAT_t { - uint32_t min; - uint32_t max; - uint32_t start; - uint32_t intlevel; + uint32_t min; + uint32_t max; + uint32_t start; + uint32_t intlevel; } UMM_TIME_STAT; @@ -458,23 +401,25 @@ extern UMM_TIME_STATS time_stats; bool get_umm_get_perf_data(UMM_TIME_STATS *p, size_t size); static inline void _critical_entry(UMM_TIME_STAT *p, uint32_t *saved_ps) { - *saved_ps = xt_rsil(DEFAULT_CRITICAL_SECTION_INTLEVEL); - if (0U != (*saved_ps & 0x0FU)) { - p->intlevel += 1U; - } + *saved_ps = xt_rsil(DEFAULT_CRITICAL_SECTION_INTLEVEL); + if (0U != (*saved_ps & 0x0FU)) { + p->intlevel += 1U; + } - p->start = esp_get_cycle_count(); + p->start = esp_get_cycle_count(); } static inline void _critical_exit(UMM_TIME_STAT *p, uint32_t *saved_ps) { - uint32_t elapse = esp_get_cycle_count() - p->start; - if (elapse < p->min) - p->min = elapse; + uint32_t elapse = esp_get_cycle_count() - p->start; + if (elapse < p->min) { + p->min = elapse; + } - if (elapse > p->max) - p->max = elapse; + if (elapse > p->max) { + p->max = elapse; + } - xt_wsr_ps(*saved_ps); + xt_wsr_ps(*saved_ps); } #endif ////////////////////////////////////////////////////////////////////////////////////// @@ -491,33 +436,33 @@ static inline void _critical_exit(UMM_TIME_STAT *p, uint32_t *saved_ps) { */ #ifdef UMM_TEST_BUILD - extern int umm_critical_depth; - extern int umm_max_critical_depth; - #define UMM_CRITICAL_ENTRY() {\ - ++umm_critical_depth; \ - if (umm_critical_depth > umm_max_critical_depth) { \ - umm_max_critical_depth = umm_critical_depth; \ - } \ - } +extern int umm_critical_depth; +extern int umm_max_critical_depth; + #define UMM_CRITICAL_ENTRY() { \ + ++umm_critical_depth; \ + if (umm_critical_depth > umm_max_critical_depth) { \ + umm_max_critical_depth = umm_critical_depth; \ + } \ +} #define UMM_CRITICAL_EXIT() (umm_critical_depth--) #else - #if defined(UMM_CRITICAL_METRICS) +#if defined(UMM_CRITICAL_METRICS) #define UMM_CRITICAL_DECL(tag) uint32_t _saved_ps_##tag #define UMM_CRITICAL_ENTRY(tag)_critical_entry(&time_stats.tag, &_saved_ps_##tag) #define UMM_CRITICAL_EXIT(tag) _critical_exit(&time_stats.tag, &_saved_ps_##tag) #define UMM_CRITICAL_WITHINISR(tag) (0 != (_saved_ps_##tag & 0x0F)) - #else // ! UMM_CRITICAL_METRICS - // This method preserves the intlevel on entry and restores the - // original intlevel at exit. +#else // ! UMM_CRITICAL_METRICS +// This method preserves the intlevel on entry and restores the +// original intlevel at exit. #define UMM_CRITICAL_DECL(tag) uint32_t _saved_ps_##tag #define UMM_CRITICAL_ENTRY(tag) _saved_ps_##tag = xt_rsil(DEFAULT_CRITICAL_SECTION_INTLEVEL) #define UMM_CRITICAL_EXIT(tag) xt_wsr_ps(_saved_ps_##tag) #define UMM_CRITICAL_WITHINISR(tag) (0 != (_saved_ps_##tag & 0x0F)) - #endif +#endif #endif - /* +/* * -D UMM_LIGHTWEIGHT_CPU * * The use of this macro is hardware/application specific. @@ -546,8 +491,8 @@ static inline void _critical_exit(UMM_TIME_STAT *p, uint32_t *saved_ps) { #define UMM_CRITICAL_SUSPEND(tag) UMM_CRITICAL_EXIT(tag) #define UMM_CRITICAL_RESUME(tag) UMM_CRITICAL_ENTRY(tag) #else -#define UMM_CRITICAL_SUSPEND(tag) do {} while(0) -#define UMM_CRITICAL_RESUME(tag) do {} while(0) +#define UMM_CRITICAL_SUSPEND(tag) do {} while (0) +#define UMM_CRITICAL_RESUME(tag) do {} while (0) #endif /* @@ -560,7 +505,7 @@ static inline void _critical_exit(UMM_TIME_STAT *p, uint32_t *saved_ps) { * direction of the beginning of the heap when possible. * * Status: TODO: These are new options introduced to optionally restore the - * previous defrag propery of realloc. The issue has been raised in the upstream + * previous defrag property of realloc. The issue has been raised in the upstream * repo. No response at this time. Based on response, may propose for upstream. */ /* @@ -590,12 +535,12 @@ static inline void _critical_exit(UMM_TIME_STAT *p, uint32_t *saved_ps) { */ #ifdef UMM_INTEGRITY_CHECK - extern bool umm_integrity_check( void ); -# define INTEGRITY_CHECK() umm_integrity_check() - extern void umm_corruption(void); -# define UMM_HEAP_CORRUPTION_CB() DBGLOG_FUNCTION( "Heap Corruption!" ) +extern bool umm_integrity_check(void); +#define INTEGRITY_CHECK() umm_integrity_check() +extern void umm_corruption(void); +#define UMM_HEAP_CORRUPTION_CB() DBGLOG_FUNCTION("Heap Corruption!") #else -# define INTEGRITY_CHECK() (1) +#define INTEGRITY_CHECK() (1) #endif ///////////////////////////////////////////////// @@ -665,35 +610,49 @@ static inline void _critical_exit(UMM_TIME_STAT *p, uint32_t *saved_ps) { #define UMM_POISONED_BLOCK_LEN_TYPE uint32_t #if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) - extern void *umm_poison_malloc( size_t size ); - extern void *umm_poison_calloc( size_t num, size_t size ); - extern void *umm_poison_realloc( void *ptr, size_t size ); - extern void umm_poison_free( void *ptr ); - 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 ); - #if defined(UMM_POISON_CHECK_LITE) - /* +extern void *umm_poison_malloc(size_t size); +extern void *umm_poison_calloc(size_t num, size_t size); +extern void *umm_poison_realloc(void *ptr, size_t size); +extern void umm_poison_free(void *ptr); +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 * under 10us or close. */ - # define POISON_CHECK() 1 - # define POISON_CHECK_NEIGHBORS(c) \ - do {\ - if(!check_poison_neighbors(_context, c)) \ - panic();\ - } while(false) - #else - /* Not normally enabled. A full heap poison check may exceed 10us. */ - # define POISON_CHECK() umm_poison_check() - # define POISON_CHECK_NEIGHBORS(c) do{}while(false) - #endif + #define POISON_CHECK() 1 + #define POISON_CHECK_NEIGHBORS(c) \ + do { \ + if (!check_poison_neighbors(_context, c)) \ + panic(); \ + } while (false) #else -# define POISON_CHECK() 1 -# define POISON_CHECK_NEIGHBORS(c) do{}while(false) +/* Not normally enabled. A full heap poison check may exceed 10us. */ + #define POISON_CHECK() umm_poison_check() + #define POISON_CHECK_NEIGHBORS(c) do {} while (false) +#endif +#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) /* @@ -701,13 +660,13 @@ static inline void _critical_exit(UMM_TIME_STAT *p, uint32_t *saved_ps) { * that can actually be allocated. */ #define UMM_OVERHEAD_ADJUST ( \ - umm_block_size()/2 + \ - UMM_POISON_SIZE_BEFORE + \ - UMM_POISON_SIZE_AFTER + \ - sizeof(UMM_POISONED_BLOCK_LEN_TYPE)) + umm_block_size() / 2 + \ + UMM_POISON_SIZE_BEFORE + \ + UMM_POISON_SIZE_AFTER + \ + sizeof(UMM_POISONED_BLOCK_LEN_TYPE)) #else -#define UMM_OVERHEAD_ADJUST (umm_block_size()/2) +#define UMM_OVERHEAD_ADJUST (umm_block_size() / 2) #endif @@ -718,9 +677,9 @@ static inline void _critical_exit(UMM_TIME_STAT *p, uint32_t *saved_ps) { #if defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_OOM) || \ defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) || \ defined(UMM_INTEGRITY_CHECK) -#define DBGLOG_FUNCTION(fmt, ...) ets_uart_printf(fmt, ##__VA_ARGS__) +#define DBGLOG_FUNCTION(fmt, ...) ets_uart_printf(fmt,##__VA_ARGS__) #else -#define DBGLOG_FUNCTION(fmt, ...) do { (void)fmt; } while(false) +#define DBGLOG_FUNCTION(fmt, ...) do { (void)fmt; } while (false) #endif ///////////////////////////////////////////////// @@ -735,105 +694,24 @@ static inline void _critical_exit(UMM_TIME_STAT *p, uint32_t *saved_ps) { #if defined(UMM_CRITICAL_METRICS) struct UMM_TIME_STATS_t { - UMM_TIME_STAT id_malloc; - UMM_TIME_STAT id_realloc; - UMM_TIME_STAT id_free; -#ifdef UMM_INFO - UMM_TIME_STAT id_info; -#endif -#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) - UMM_TIME_STAT id_poison; -#endif -#ifdef UMM_INTEGRITY_CHECK - UMM_TIME_STAT id_integrity; -#endif - UMM_TIME_STAT id_no_tag; + UMM_TIME_STAT id_malloc; + UMM_TIME_STAT id_realloc; + UMM_TIME_STAT id_free; + #ifdef UMM_INFO + UMM_TIME_STAT id_info; + #endif + #if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) + UMM_TIME_STAT id_poison; + #endif + #ifdef UMM_INTEGRITY_CHECK + UMM_TIME_STAT id_integrity; + #endif + 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. -void* ICACHE_RAM_ATTR pvPortMalloc(size_t size, const char* file, int line); -void* ICACHE_RAM_ATTR pvPortCalloc(size_t count, size_t size, const char* file, int line); -void* ICACHE_RAM_ATTR pvPortRealloc(void *ptr, size_t size, const char* file, int line); -void* ICACHE_RAM_ATTR pvPortZalloc(size_t size, const char* file, int line); -void ICACHE_RAM_ATTR vPortFree(void *ptr, const char* file, int line); - -#define malloc(s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; pvPortMalloc(s, mem_debug_file, __LINE__); }) -#define calloc(n,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; pvPortCalloc(n, s, mem_debug_file, __LINE__); }) -#define realloc(p,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; 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__; 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) -#include -void* ICACHE_RAM_ATTR 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__; pvPortRealloc(p, s, mem_debug_file, __LINE__); }) - -void ICACHE_RAM_ATTR 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__; 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 new file mode 100644 index 0000000000..233671304f --- /dev/null +++ b/cores/esp8266/umm_malloc/umm_malloc_cfgport.h @@ -0,0 +1,219 @@ +/* + * 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" +#include "../esp8266_undocumented.h" + +#include +#include +#include + +#include "c_types.h" +#endif + + +#ifndef _UMM_MALLOC_CFGPORT_H +#define _UMM_MALLOC_CFGPORT_H + +/* + * 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 + * restore the legacy behavor. + * + * If you have your own app_entry_redefinable() function, see + * 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.~ + * For now revert default back to IRAM + * define UMM_INIT_USE_ICACHE to use ICACHE/IROM + */ +#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 + */ +extern char _heap_start[]; +#define UMM_HEAP_END_ADDR 0x3FFFC000UL +#define UMM_MALLOC_CFG_HEAP_ADDR ((uint32_t)&_heap_start[0]) +#define UMM_MALLOC_CFG_HEAP_SIZE ((size_t)(UMM_HEAP_END_ADDR - UMM_MALLOC_CFG_HEAP_ADDR)) + +/* + * Define active Heaps + */ +#if defined(MMU_IRAM_HEAP) +#define UMM_HEAP_IRAM +#else +#undef UMM_HEAP_IRAM +#endif + +#if defined(MMU_EXTERNAL_HEAP) +#define UMM_HEAP_EXTERNAL +#else +#undef UMM_HEAP_EXTERNAL +#endif + +/* + * Assign IDs to active Heaps and tally. DRAM is always active. + */ +#define UMM_HEAP_DRAM 0 +#define UMM_HEAP_DRAM_DEFINED 1 + +#ifdef UMM_HEAP_IRAM +#undef UMM_HEAP_IRAM +#define UMM_HEAP_IRAM_DEFINED 1 +#define UMM_HEAP_IRAM UMM_HEAP_DRAM_DEFINED +#else +#define UMM_HEAP_IRAM_DEFINED 0 +#endif + +#ifdef UMM_HEAP_EXTERNAL +#undef UMM_HEAP_EXTERNAL +#define UMM_HEAP_EXTERNAL_DEFINED 1 +#define UMM_HEAP_EXTERNAL (UMM_HEAP_DRAM_DEFINED + UMM_HEAP_IRAM_DEFINED) +#else +#define UMM_HEAP_EXTERNAL_DEFINED 0 +#endif + +#define UMM_NUM_HEAPS (UMM_HEAP_DRAM_DEFINED + UMM_HEAP_IRAM_DEFINED + UMM_HEAP_EXTERNAL_DEFINED) + +#if (UMM_NUM_HEAPS == 1) +#else +#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 01122c1a73..ca41cabf4f 100644 --- a/cores/esp8266/umm_malloc/umm_poison.c +++ b/cores/esp8266/umm_malloc/umm_poison.c @@ -8,32 +8,36 @@ #include #include +#define UMM_POISON_BLOCK_SIZE (UMM_POISON_SIZE_BEFORE + sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_AFTER) + /* - * Yields a size of the poison for the block of size `s`. + * Yields the total size of a poison block of size `s`. * If `s` is 0, returns 0. + * If result overflows/wraps, return saturation value. */ -static size_t poison_size(size_t s) { - return(s ? (UMM_POISON_SIZE_BEFORE + - sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + - UMM_POISON_SIZE_AFTER) - : 0); +static void add_poison_size(size_t *s) { + if (*s == 0) { + return; + } + + *s = umm_uadd_sat(*s, UMM_POISON_BLOCK_SIZE); } /* * Print memory contents starting from given `ptr` */ -static void dump_mem ( const void *vptr, size_t len ) { - const uint8_t *ptr = (const uint8_t *)vptr; - while (len--) { - DBGLOG_ERROR(" 0x%.2x", (unsigned int)(*ptr++)); - } +static void dump_mem(const void *vptr, size_t len) { + const uint8_t *ptr = (const uint8_t *)vptr; + while (len--) { + DBGLOG_ERROR(" 0x%.2x", (unsigned int)(*ptr++)); + } } /* * Put poison data at given `ptr` and `poison_size` */ -static void put_poison( void *ptr, size_t poison_size ) { - memset(ptr, POISON_BYTE, poison_size); +static void put_poison(void *ptr, size_t poison_size) { + memset(ptr, POISON_BYTE, poison_size); } /* @@ -43,56 +47,56 @@ static void put_poison( void *ptr, size_t poison_size ) { * If poison is there, returns 1. * Otherwise, prints the appropriate message, and returns 0. */ -static bool check_poison( const void *ptr, size_t poison_size, +static bool check_poison(const void *ptr, size_t poison_size, const char *where) { - size_t i; - bool ok = true; - - for (i = 0; i < poison_size; i++) { - if (((const uint8_t *)ptr)[i] != POISON_BYTE) { - ok = false; - break; + size_t i; + bool ok = true; + + for (i = 0; i < poison_size; i++) { + if (((const uint8_t *)ptr)[i] != POISON_BYTE) { + ok = false; + break; + } } - } - if (!ok) { - DBGLOG_ERROR( "No poison %s block at: 0x%lx, actual data:", where, (unsigned long)ptr); - dump_mem(ptr, poison_size); - DBGLOG_ERROR( "\n" ); - } + if (!ok) { + DBGLOG_ERROR("No poison %s block at: 0x%lx, actual data:", where, (unsigned long)ptr); + dump_mem(ptr, poison_size); + DBGLOG_ERROR("\n"); + } - return ok; + return ok; } /* * Check if a block is properly poisoned. Must be called only for non-free * blocks. */ -static bool check_poison_block( umm_block *pblock ) { - bool ok = true; - - if (pblock->header.used.next & UMM_FREELIST_MASK) { - DBGLOG_ERROR( "check_poison_block is called for free block 0x%lx\n", (unsigned long)pblock); - } else { - /* the block is used; let's check poison */ - unsigned char *pc = (unsigned char *)pblock->body.data; - unsigned char *pc_cur; - - pc_cur = pc + sizeof(UMM_POISONED_BLOCK_LEN_TYPE); - if (!check_poison(pc_cur, UMM_POISON_SIZE_BEFORE, "before")) { - ok = false; - goto clean; - } - - pc_cur = pc + *((UMM_POISONED_BLOCK_LEN_TYPE *)pc) - UMM_POISON_SIZE_AFTER; - if (!check_poison(pc_cur, UMM_POISON_SIZE_AFTER, "after")) { - ok = false; - goto clean; +static bool check_poison_block(umm_block *pblock) { + bool ok = true; + + if (pblock->header.used.next & UMM_FREELIST_MASK) { + DBGLOG_ERROR("check_poison_block is called for free block 0x%lx\n", (unsigned long)pblock); + } else { + /* the block is used; let's check poison */ + unsigned char *pc = (unsigned char *)pblock->body.data; + unsigned char *pc_cur; + + pc_cur = pc + sizeof(UMM_POISONED_BLOCK_LEN_TYPE); + if (!check_poison(pc_cur, UMM_POISON_SIZE_BEFORE, "before")) { + ok = false; + goto clean; + } + + pc_cur = pc + *((UMM_POISONED_BLOCK_LEN_TYPE *)pc) - UMM_POISON_SIZE_AFTER; + if (!check_poison(pc_cur, UMM_POISON_SIZE_AFTER, "after")) { + ok = false; + goto clean; + } } - } clean: - return ok; + return ok; } /* @@ -102,25 +106,25 @@ static bool check_poison_block( umm_block *pblock ) { * * `size_w_poison` is a size of the whole block, including a poison. */ -static void *get_poisoned( void *vptr, size_t size_w_poison ) { - unsigned char *ptr = (unsigned char *)vptr; +static void *get_poisoned(void *vptr, size_t size_w_poison) { + unsigned char *ptr = (unsigned char *)vptr; - if (size_w_poison != 0 && ptr != NULL) { + if (size_w_poison != 0 && ptr != NULL) { - /* Poison beginning and the end of the allocated chunk */ - put_poison(ptr + sizeof(UMM_POISONED_BLOCK_LEN_TYPE), - UMM_POISON_SIZE_BEFORE); - put_poison(ptr + size_w_poison - UMM_POISON_SIZE_AFTER, - UMM_POISON_SIZE_AFTER); + /* Poison beginning and the end of the allocated chunk */ + put_poison(ptr + sizeof(UMM_POISONED_BLOCK_LEN_TYPE), + UMM_POISON_SIZE_BEFORE); + put_poison(ptr + size_w_poison - UMM_POISON_SIZE_AFTER, + UMM_POISON_SIZE_AFTER); - /* Put exact length of the user's chunk of memory */ - *(UMM_POISONED_BLOCK_LEN_TYPE *)ptr = (UMM_POISONED_BLOCK_LEN_TYPE)size_w_poison; + /* Put exact length of the user's chunk of memory */ + *(UMM_POISONED_BLOCK_LEN_TYPE *)ptr = (UMM_POISONED_BLOCK_LEN_TYPE)size_w_poison; - /* Return pointer at the first non-poisoned byte */ - ptr += sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE; - } + /* Return pointer at the first non-poisoned byte */ + ptr += sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE; + } - return (void *)ptr; + return (void *)ptr; } /* @@ -129,84 +133,91 @@ static void *get_poisoned( void *vptr, size_t size_w_poison ) { * * Returns unpoisoned pointer, i.e. actual pointer to the allocated memory. */ -static void *get_unpoisoned( void *vptr ) { - uintptr_t ptr = (uintptr_t)vptr; +static void *get_unpoisoned(void *vptr) { + uintptr_t ptr = (uintptr_t)vptr; - if (ptr != 0) { - uint16_t c; + if (ptr != 0) { + uint16_t c; - ptr -= (sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE); + ptr -= (sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_BEFORE); - umm_heap_context_t *_context = umm_get_ptr_context( vptr ); - if (NULL == _context) { - panic(); - return NULL; - } - /* Figure out which block we're in. Note the use of truncated division... */ - c = (ptr - (uintptr_t)(&(_context->heap[0])))/sizeof(umm_block); + umm_heap_context_t *_context = umm_get_ptr_context(vptr); + NON_NULL_CONTEXT_ASSERT(); + + /* Figure out which block we're in. Note the use of truncated division... */ + c = (ptr - (uintptr_t)(&(_context->heap[0]))) / sizeof(umm_block); - check_poison_block(&UMM_BLOCK(c)); - } + check_poison_block(&UMM_BLOCK(c)); + } - return (void *)ptr; + return (void *)ptr; } /* }}} */ /* ------------------------------------------------------------------------ */ -void *umm_poison_malloc( size_t size ) { - void *ret; - - size += poison_size(size); +void *umm_poison_malloc(size_t size) { + void *ret; - ret = umm_malloc( size ); + add_poison_size(&size); - ret = get_poisoned(ret, size); + ret = umm_malloc(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; + return ret; } /* ------------------------------------------------------------------------ */ -void *umm_poison_calloc( size_t num, size_t item_size ) { - void *ret; - size_t size = item_size * num; +void *umm_poison_calloc(size_t num, size_t item_size) { + void *ret; - size += poison_size(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; - ret = umm_malloc(size); + add_poison_size(&size); - if (NULL != ret) - memset(ret, 0x00, size); + ret = umm_malloc(size); - ret = get_poisoned(ret, size); + if (NULL != ret) { + memset(ret, 0x00, request_sz); + } - return ret; + return ret; } /* ------------------------------------------------------------------------ */ -void *umm_poison_realloc( void *ptr, size_t size ) { - void *ret; - - ptr = get_unpoisoned(ptr); +void *umm_poison_realloc(void *ptr, size_t size) { + void *ret; - size += poison_size(size); - ret = umm_realloc( ptr, size ); + ptr = get_unpoisoned(ptr); - ret = get_poisoned(ret, size); + add_poison_size(&size); + ret = umm_realloc(ptr, 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; + return ret; } /* ------------------------------------------------------------------------ */ -void umm_poison_free( void *ptr ) { +void umm_poison_free(void *ptr) { - ptr = get_unpoisoned(ptr); + ptr = get_unpoisoned(ptr); - umm_free( ptr ); + umm_free(ptr); } /* @@ -215,32 +226,32 @@ void umm_poison_free( void *ptr ) { */ bool umm_poison_check(void) { - UMM_CRITICAL_DECL(id_poison); - bool ok = true; - uint16_t cur; + UMM_CRITICAL_DECL(id_poison); + bool ok = true; + uint16_t cur; - UMM_INIT_HEAP; + UMM_CHECK_INITIALIZED(); - UMM_CRITICAL_ENTRY(id_poison); - umm_heap_context_t *_context = umm_get_current_heap(); + UMM_CRITICAL_ENTRY(id_poison); + umm_heap_context_t *_context = umm_get_current_heap(); - /* Now iterate through the blocks list */ - cur = UMM_NBLOCK(0) & UMM_BLOCKNO_MASK; + /* Now iterate through the blocks list */ + cur = UMM_NBLOCK(0) & UMM_BLOCKNO_MASK; - while( UMM_NBLOCK(cur) & UMM_BLOCKNO_MASK ) { - if ( !(UMM_NBLOCK(cur) & UMM_FREELIST_MASK) ) { - /* This is a used block (not free), so, check its poison */ - ok = check_poison_block(&UMM_BLOCK(cur)); - if (!ok){ - break; - } - } + while (UMM_NBLOCK(cur) & UMM_BLOCKNO_MASK) { + if (!(UMM_NBLOCK(cur) & UMM_FREELIST_MASK)) { + /* This is a used block (not free), so, check its poison */ + ok = check_poison_block(&UMM_BLOCK(cur)); + if (!ok) { + break; + } + } - cur = UMM_NBLOCK(cur) & UMM_BLOCKNO_MASK; - } - UMM_CRITICAL_EXIT(id_poison); + cur = UMM_NBLOCK(cur) & UMM_BLOCKNO_MASK; + } + UMM_CRITICAL_EXIT(id_poison); - return ok; + return ok; } /* ------------------------------------------------------------------------ */ 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/cores/esp8266/xtruntime-frames.h b/cores/esp8266/xtruntime-frames.h index 8b5a7463ab..42ac0e2eb9 100644 --- a/cores/esp8266/xtruntime-frames.h +++ b/cores/esp8266/xtruntime-frames.h @@ -54,7 +54,7 @@ * stack frame is limited to 128 bytes (currently at 64). */ STRUCT_BEGIN -STRUCT_FIELD (long,4,KEXC_,pc) /* "parm" */ +STRUCT_FIELD (long,4,KEXC_,pc) /* "param" */ STRUCT_FIELD (long,4,KEXC_,ps) STRUCT_AFIELD(long,4,KEXC_,areg, 4) /* a12 .. a15 */ STRUCT_FIELD (long,4,KEXC_,sar) /* "save" */ 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/PROGMEM.rst b/doc/PROGMEM.rst index e455df639d..8cf86ce25b 100644 --- a/doc/PROGMEM.rst +++ b/doc/PROGMEM.rst @@ -5,7 +5,7 @@ Intro ----- PROGMEM is a Arduino AVR feature that has been ported to ESP8266 to -ensure compatability with existing Arduino libraries, as well as, saving +ensure compatibility with existing Arduino libraries, as well as, saving RAM. On the esp8266 declaring a string such as ``const char * xyz = "this is a string"`` will place this string in RAM, not flash. It is possible to place a String into flash, and then load it into RAM when @@ -283,7 +283,7 @@ generate as they are basically ``const char *``. On the other hand conversions from, very useful when overloading functions, and doing implicit type conversions. It is worth adding that if you wish to store an ``int``, ``float`` or pointer these can be stored and read back -directly as they are 4 bytes in size and therefor will be always +directly as they are 4 bytes in size and therefore will be always aligned! Hope this helps. diff --git a/doc/Troubleshooting/debugging.rst b/doc/Troubleshooting/debugging.rst index 0a96def3a5..4216a0c793 100644 --- a/doc/Troubleshooting/debugging.rst +++ b/doc/Troubleshooting/debugging.rst @@ -44,8 +44,8 @@ Usage 5. Check the Serial Output -Informations ------------- +Information +----------- It work with every sketch that enables the Serial interface that is selected as debug port. @@ -74,7 +74,7 @@ Debug Level All defines for the different levels starts with ``DEBUG_ESP_`` a full list can be found here in the -`boards.txt `__ +`boards.txt `__ Example for own debug messages ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/doc/Troubleshooting/stack_dump.rst b/doc/Troubleshooting/stack_dump.rst index 4ac4907e1b..ef320392e7 100644 --- a/doc/Troubleshooting/stack_dump.rst +++ b/doc/Troubleshooting/stack_dump.rst @@ -12,52 +12,55 @@ 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 <<`__ +full list of all causes can be found `here <../exception_causes.rst>`__ the hex after are the stack dump. Decode ~~~~~~ -It's possible to decode the Stack to readable information. For more info see the `Esp Exception Decoder `__ 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 1b8ac2614a..99421d86b9 100644 --- a/doc/boards.rst +++ b/doc/boards.rst @@ -6,13 +6,13 @@ Generic ESP8266 Module These modules come in different form factors and pinouts. See the page at ESP8266 community wiki for more info: `ESP8266 Module Family `__. -Usually these modules have no bootstapping resistors on board, insufficient decoupling capacitors, no voltage regulator, no reset circuit, and no USB-serial adapter. This makes using them somewhat tricky, compared to development boards which add these features. +Usually these modules have no bootstrapping resistors on board, insufficient decoupling capacitors, no voltage regulator, no reset circuit, and no USB-serial adapter. This makes using them somewhat tricky, compared to development boards which add these features. In order to use these modules, make sure to observe the following: - **Provide sufficient power to the module.** For stable use of the ESP8266 a power supply with 3.3V and >= 250mA is required. Using the power available from USB to Serial adapter is not recommended, these adapters typically do not supply enough current to run ESP8266 reliably in every situation. An external supply or regulator alongwith filtering capacitors is preferred. -- **Connect bootstapping resistors** to GPIO0, GPIO2, GPIO15 according to the schematics below. +- **Connect bootstrapping resistors** to GPIO0, GPIO2, GPIO15 according to the schematics below. - **Put ESP8266 into bootloader mode** before uploading code. @@ -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 ---------------------- @@ -207,6 +216,18 @@ ESP8285 (`datasheet `__ below for more information. @@ -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. @@ -219,3 +219,13 @@ setCiphersLessSecure() ^^^^^^^^^^^^^^^^^^^^^^ Helper function which essentially limits BearSSL to less secure ciphers than it would natively choose, but they may be helpful and faster if your server depended on specific crypto options. + +Limiting TLS(SSL) Versions +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By default, BearSSL will connect with TLS 1.0, TLS 1.1, or TLS 1.2 protocols (depending on the request of the remote side). If you want to limit to a subset, use the following call: + +setSSLVersion(uint32_t min, uint32_t max) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Valid values for min and max are `BR_TLS10`, `BR_TLS11`, `BR_TLS12`. Min and max may be set to the same value if only a single TLS version is desired. diff --git a/doc/esp8266wifi/bearssl-server-secure-class.rst b/doc/esp8266wifi/bearssl-server-secure-class.rst index f25ce384e1..5f9df7504d 100644 --- a/doc/esp8266wifi/bearssl-server-secure-class.rst +++ b/doc/esp8266wifi/bearssl-server-secure-class.rst @@ -36,7 +36,7 @@ Sets an elliptic curve certificate and key for the server. Needs to be called b Client sessions (Resuming connections fast) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The TLS handshake process takes a long time because of all the back and forth between the client and the server. You can shorten it by caching the clients' sessions which will skip a few steps in the TLS handshake. In order for this to work, your client also needs to cache the session. `BearSSL::WiFiClientSecure `__ can do that as well as modern web browers. +The TLS handshake process takes a long time because of all the back and forth between the client and the server. You can shorten it by caching the clients' sessions which will skip a few steps in the TLS handshake. In order for this to work, your client also needs to cache the session. `BearSSL::WiFiClientSecure `__ can do that as well as modern web browsers. Here are the kind of performance improvements that you'll be able to see for TLS handshakes with an ESP8266 with it's clock set at 160MHz on a network with fairly low latency: 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 a26f925858..f722bff3af 100644 --- a/doc/esp8266wifi/generic-class.rst +++ b/doc/esp8266wifi/generic-class.rst @@ -32,7 +32,7 @@ For the SoftAP interface, when the interface is brought up, any servers should b A detailed explanation of ``WiFiEventHandler`` can be found in the section with `examples :arrow\_right: `__ dedicated specifically to the Generic Class.. -Alternatively, check the example sketch `WiFiEvents.ino `__ available inside examples folder of the ESP8266WiFi library. +Alternatively, check the example sketch `WiFiEvents.ino `__ available in the examples folder of the ESP8266WiFi library. persistent @@ -42,30 +42,67 @@ persistent WiFi.persistent(persistent) -ESP8266 is able to reconnect to the last used Wi-Fi network or establishes the same Access Point upon power up or reset. +Starting from version 3 of this core, **persistence is disabled by default +and WiFi does not start automatically at boot** (see PR `#7902 `__). + +Previously, SDK was automatically starting WiFi at boot. This was probably +intended for the Espressif AT FW which is interactive and preserves WiFi +state across reboots. This behavior is generally irrelevant with the +Arduino API because sketches start with ``WiFi.begin()`` or +``WiFi.softAP()``. + +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). + +.. code:: cpp + + #include + + void setup () { + #ifdef WIFI_IS_OFF_AT_BOOT + enableWiFiAtBootTime(); // can be called from anywhere with the same effect + #endif + .... + } + +When legacy behavior is restored thanks to this call, +ESP8266 is able to reconnect to the last used WiFi network or establishes the same Access Point upon power up or reset. By default, these settings are written to specific sectors of flash memory every time they are changed in ``WiFi.begin(ssid, passphrase)`` or ``WiFi.softAP(ssid, passphrase, channel)``, and when ``WiFi.disconnect`` or ``WiFi.softAPdisconnect`` is invoked. Frequently calling these functions could cause wear on the flash memory (see issue `#1054 `__). -Once ``WiFi.persistent(false)`` is called, ``WiFi.begin``, ``WiFi.disconnect``, ``WiFi.softAP``, or ``WiFi.softAPdisconnect`` only changes the current in-memory Wi-Fi settings, and does not affect the Wi-Fi settings stored in flash memory. +Once ``WiFi.persistent(false)`` is called, ``WiFi.begin``, ``WiFi.disconnect``, ``WiFi.softAP``, or ``WiFi.softAPdisconnect`` only changes the current in-memory WiFi settings, and does not affect the WiFi settings stored in flash memory. mode ~~~~ .. code:: cpp - WiFi.mode(m) + bool mode(WiFiMode_t m) -- ``WiFi.mode(m)``: set mode to ``WIFI_AP``, ``WIFI_STA``, - ``WIFI_AP_STA`` or ``WIFI_OFF`` +Switches to one of the regular WiFi modes, where ``m`` is one of: + +- ``WIFI_OFF``: turn WiFi off. +- ``WIFI_STA``: switch to `Station (STA) `__ mode. +- ``WIFI_AP``: switch to `Access Point (AP) `__ mode. +- ``WIFI_AP_STA``: enable both Station (STA) and Access Point (AP) mode. getMode ~~~~~~~ .. code:: cpp - WiFiMode_t WiFi.getMode() + WiFiMode_t getMode() -- ``WiFi.getMode()``: return current Wi-Fi mode (one out of four modes above) +Gets the current WiFi mode (one out of four regular modes above). WiFi power management, DTIM ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -144,6 +181,41 @@ getPhyMode Gets the WiFi radio phy mode that is currently set. +forceSleepBegin +~~~~~~~~~~~~~~~ + +.. code:: cpp + + bool forceSleepBegin (uint32 sleepUs=0) + +Saves the currently set WiFi mode and starts forced modem sleep for the specified time (us) + +forceSleepWake +~~~~~~~~~~~~~~ + +.. code:: cpp + + bool forceSleepWake () + +Called after `forceSleepBegin()`. Restores the previous WiFi mode and attempts reconnection when STA was active. + +shutdown and resumeFromShutdown +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: cpp + + bool shutdown (WiFiState& state) + bool shutdown (WiFiState& state, uint32 sleepUs) + bool resumeFromShutdown (WiFiState& state) + bool shutdownValidCRC (const WiFiState& state) + +Stores the STA interface IP configuration in the specified ``state`` struct and calls ``forceSleepBegin(sleepUs)``. +Restores STA interface configuration from the ``state`` and calls ``forceSleepWake()``. + +These methods are intended to be used in low-power scenarios, e.g. where ESP.deepSleep is used between actions to preserve battery power. It is the user's responsibility to preserve the WiFiState between ``shutdown()`` and ``resumeFromShutdown()`` by storing it in the RTC user data and/or flash memory. + +See `WiFiShutdown.ino `__ for an example of usage. + Other Function Calls ~~~~~~~~~~~~~~~~~~~~ @@ -153,11 +225,13 @@ Other Function Calls WiFiSleepType_t getSleepMode () bool enableSTA (bool enable) bool enableAP (bool enable) - bool forceSleepBegin (uint32 sleepUs=0) - bool forceSleepWake () 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/readme.rst b/doc/esp8266wifi/readme.rst index 2c22ee150b..b9d309d5fb 100644 --- a/doc/esp8266wifi/readme.rst +++ b/doc/esp8266wifi/readme.rst @@ -7,7 +7,7 @@ ESP8266 is all about Wi-Fi. If you are eager to connect your new ESP8266 module Introduction ------------ -The `Wi-Fi library for ESP8266 `__ has been developed based on `ESP8266 SDK `__, using the naming conventions and overall functionality philosophy of the `Arduino WiFi library `__. Over time, the wealth of Wi-Fi features ported from ESP8266 SDK to `esp8266 / +The `Wi-Fi library for ESP8266 `__ has been developed based on `ESP8266 SDK `__, using the naming conventions and overall functionality philosophy of the `Arduino WiFi library `__. Over time, the wealth of Wi-Fi features ported from ESP8266 SDK to `esp8266 / Arduino `__ outgrew `Arduino WiFi library `__ and it became apparent that we would need to provide separate documentation on what is new and extra. This documentation will walk you through several classes, methods and properties of the `ESP8266WiFi `__ library. If you are new to C++ and Arduino, don't worry. We will start from general concepts and then move to detailed description of members of each particular class including usage examples. @@ -164,6 +164,7 @@ WiFi Multi Example: .. code:: cpp + #include ESP8266WiFiMulti wifiMulti; 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 5603228378..8bcce99944 100644 --- a/doc/esp8266wifi/server-class.rst +++ b/doc/esp8266wifi/server-class.rst @@ -12,13 +12,34 @@ Methods documented for the `Server Class `__ 6. `println() `__ +In ESP8266WiFi library the ``ArduinoWiFiServer`` class implements ``available`` and the write-to-all-clients functionality as described in the Arduino WiFi library reference. The PageServer example shows how ``available`` and the write-to-all-clients works. + +For most use cases the basic WiFiServer class of the ESP8266WiFi library is suitable. + 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 +~~~~~~ + +Method ``accept()`` returns a waiting client connection. `accept() is documented `__ for the Arduino Ethernet library. + +available +~~~~~~~~~ +.. deprecated:: 3.1.0 + see ``accept`` + +``available`` in the ESP8266WiFi library's WiFiServer class doesn't work as documented for the Arduino WiFi library. It works the same way as ``accept``. + write (write to all clients) not supported ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Please note that the ``write`` method on the ``WiFiServer`` object is not implemented and returns failure always. Use the returned -``WiFiClient`` object from the ``WiFiServer::available()`` method to communicate with individual clients. If you need to send +``WiFiClient`` object from the ``WiFiServer::accept()`` method to communicate with individual clients. If you need to send the exact same packets to a series of clients, your application must maintain a list of connected clients and iterate over them manually. setNoDelay @@ -49,6 +70,8 @@ Other Function Calls .. code:: cpp bool hasClient () + size_t hasClientData () + bool hasMaxPendingClients () bool getNoDelay () virtual size_t write (const uint8_t *buf, size_t size) uint8_t status () diff --git a/doc/esp8266wifi/server-examples.rst b/doc/esp8266wifi/server-examples.rst index cdd18fe81a..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 @@ -92,7 +91,7 @@ Serving of this web page will be done in the ``loop()`` where server is waiting void loop() { - WiFiClient client = server.available(); + WiFiClient client = server.accept(); if (client) { // we have a new client sending some request @@ -196,7 +195,7 @@ Complete sketch is presented below. void loop() { - WiFiClient client = server.available(); + WiFiClient client = server.accept(); // wait for a client (web browser) to connect if (client) { diff --git a/doc/esp8266wifi/soft-access-point-class.rst b/doc/esp8266wifi/soft-access-point-class.rst index 473960fd63..06e84b08a4 100644 --- a/doc/esp8266wifi/soft-access-point-class.rst +++ b/doc/esp8266wifi/soft-access-point-class.rst @@ -40,18 +40,18 @@ terms `__) of this function WiFi.softAP(ssid) -To set up password protected network, or to configure additional network parameters, use the following overload: +To set up pre-shared key protected network, or to configure additional network parameters, use the following overload: .. code:: cpp - WiFi.softAP(ssid, password, channel, hidden, max_connection) + WiFi.softAP(ssid, psk, channel, hidden, max_connection) The first parameter of this function is required, remaining four are optional. Meaning of all parameters is as follows: -- ``ssid`` - character string containing network SSID (max. 31 characters) -- ``password`` - optional character string with a password. For WPA2-PSK network it should be at least 8 character long. If not specified, the access point will be open for anybody to connect, (max. 63 characters). +- ``ssid`` - character string containing network SSID (max. 32 characters) +- ``psk`` - optional character string with a pre-shared key. For WPA2-PSK network it should be minimum 8 characters long and not longer than 64 characters. If not specified, the access point will be open for anybody to connect. - ``channel`` - optional parameter to set Wi-Fi channel, from 1 to 13. Default channel = 1. - ``hidden`` - optional parameter, if set to ``true`` will hide SSID. - ``max_connection`` - optional parameter to set max simultaneous connected stations, `from 0 to 8 `__. Defaults to 4. Once the max number has been reached, any other station that wants to connect will be forced to wait until an already connected station disconnects. @@ -152,7 +152,7 @@ Disconnect stations from the network established by the soft-AP. WiFi.softAPdisconnect(wifioff) -Function will set currently configured SSID and password of the soft-AP to null values. The parameter ``wifioff`` is optional. If set to ``true`` it will switch the soft-AP mode off. +Function will set currently configured SSID and pre-shared key of the soft-AP to null values. The parameter ``wifioff`` is optional. If set to ``true`` it will switch the soft-AP mode off. Function will return ``true`` if operation was successful or ``false`` if otherwise. 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 a0d575e8ed..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 @@ -41,9 +41,8 @@ following three things right: 1. Module is provided with enough power, 2. GPIO0, GPIO15 and CH\_PD are connected using pull up / pull down resistors, 3. Module is put into boot loader mode. -For specific details please refer to section on `Generic ESP8266 -modules <../boards.rst#generic-esp8266-modules>`__. Example modules -without USB to serial converter on board are shown below. +For specific details please refer to section on `Generic ESP8266 module <../boards.rst#generic-esp8266-module>`__. +Example modules without USB to serial converter on board are shown below. .. figure:: pictures/a01-example-boards-without-usb.png :alt: Example ESP8266 modules without USB to serial converter diff --git a/doc/faq/a02-my-esp-crashes.rst b/doc/faq/a02-my-esp-crashes.rst index c2433b5931..ec6340e8c7 100644 --- a/doc/faq/a02-my-esp-crashes.rst +++ b/doc/faq/a02-my-esp-crashes.rst @@ -5,11 +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>`__ @@ -73,6 +75,43 @@ In any case, make sure that your module is able to stably run standard example sketches that establish Wi-Fi connection like, e.g., `HelloServer.ino `__. +Enable compilation warnings +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Most common issues may be resolved by enabling compilation warnings and fixing them. + +For Arduino IDE, select ``File -> Preferences``: + +- Make sure ``Show verbose output during: compilation`` is enabled +- Set ``Compiler warnings`` to ``More`` or ``All`` + +For PlatformIO, all warnings should already be enabled by default. + +Notice that the default configuration of Arduino IDE inhibits **all** compilation warnings. +For the ESP8266 platform, some warnings should be treated as errors, otherwise it may cause unexpected issues at runtime: + +.. code:: cpp + + int func() { + } + + int other() { + return func(); + } + +Should fail to build with the following message: + +.. code:: console + + return-value.cpp: In function ‘int func()’: + return-value.cpp:2:1: error: no return statement in function returning non-void [-Werror=return-type] + 2 | } + | ^ + compilation terminated due to -Wfatal-errors. + cc1plus: some warnings being treated as errors + +Notice that ``-Werror=return-type`` is the default starting with Core 3.0.2 w/ GCC 10.3 + What is the Cause of Restart? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -109,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 @@ -198,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 @@ -232,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 - ICACHE_RAM_ATTR attribute declared. Not only that, but the entire function tree - called from the ISR must also have the ICACHE_RAM_ATTR declared. + 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. @@ -265,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. @@ -273,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+() @@ -310,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). @@ -354,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/a04-board-generic-is-unknown.rst b/doc/faq/a04-board-generic-is-unknown.rst index b33f75edb5..88e2f3e7be 100644 --- a/doc/faq/a04-board-generic-is-unknown.rst +++ b/doc/faq/a04-board-generic-is-unknown.rst @@ -48,7 +48,7 @@ follows: Error compiling for board Generic ESP8266 Module. Below is an example messages for -`WeMos <../boards.rst#wemos-d1-r2-mini>`__: +`WeMos <../boards.rst#lolin-wemos-d1-r2-mini>`__: :: 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 74e2211966..cfd65eca90 100644 --- a/doc/faq/readme.rst +++ b/doc/faq/readme.rst @@ -177,3 +177,26 @@ will need to implement an additional (short) deep sleep using ``WAKE_RF_DEFAULT``. Ref. `#3072 `__ + +My WiFi was previously automatically connected right after booting, but isn't anymore +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This was WiFi persistence. Starting from version 3 of this core, WiFi is +indeed off at boot and is powered on only when starting to be used with the +regular API. + +Read more at `former WiFi persistent mode <../esp8266wifi/generic-class.rst#persistent>`__. + +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 a5a3cffbbe..1dca7dc707 100644 --- a/doc/filesystem.rst +++ b/doc/filesystem.rst @@ -20,41 +20,43 @@ environment: Sketch OTA update File system EEPROM WiFi config (SDK) File system size depends on the flash chip size. Depending on the board -which is selected in IDE, you have the following options for flash size: - -+---------------------------------+--------------------------+---------------------------+ -| Board | Flash chip size, bytes | File system size, bytes | -+=================================+==========================+===========================+ -| Generic module | 512k | 64k, 128k | -+---------------------------------+--------------------------+---------------------------+ -| Generic module | 1M | 64k, 128k, 256k, 512k | -+---------------------------------+--------------------------+---------------------------+ -| Generic module | 2M | 1M | -+---------------------------------+--------------------------+---------------------------+ -| Generic module | 4M | 1M, 2M, 3M | -+---------------------------------+--------------------------+---------------------------+ -| Adafruit HUZZAH | 4M | 1M, 2M, 3M | -+---------------------------------+--------------------------+---------------------------+ -| ESPresso Lite 1.0 | 4M | 1M, 2M, 3M | -+---------------------------------+--------------------------+---------------------------+ -| ESPresso Lite 2.0 | 4M | 1M, 2M, 3M | -+---------------------------------+--------------------------+---------------------------+ -| NodeMCU 0.9 | 4M | 1M, 2M, 3M | -+---------------------------------+--------------------------+---------------------------+ -| NodeMCU 1.0 | 4M | 1M, 2M, 3M | -+---------------------------------+--------------------------+---------------------------+ -| Olimex MOD-WIFI-ESP8266(-DEV) | 2M | 1M | -+---------------------------------+--------------------------+---------------------------+ -| SparkFun Thing | 512k | 64k | -+---------------------------------+--------------------------+---------------------------+ -| SweetPea ESP-210 | 4M | 1M, 2M, 3M | -+---------------------------------+--------------------------+---------------------------+ -| WeMos D1 R1, R2 & mini | 4M | 1M, 2M, 3M | -+---------------------------------+--------------------------+---------------------------+ -| ESPDuino | 4M | 1M, 2M, 3M | -+---------------------------------+--------------------------+---------------------------+ -| WiFiduino | 4M | 1M, 2M, 3M | -+---------------------------------+--------------------------+---------------------------+ +which is selected in IDE, the following table shows options for flash size. + +Another option called ``Mapping defined by Hardware and Sketch`` is available. +It allows a sketch, not the user, to select FS configuration at boot +according to flash chip size. + +This option is also enabled with this compilation define: ``-DFLASH_MAP_SUPPORT=1``. + +There are three possible configurations: + +- ``FLASH_MAP_OTA_FS``: largest available space for onboard FS, allowing OTA (noted 'OTA' in the table) +- ``FLASH_MAP_MAX_FS``: largest available space for onboard FS (noted 'MAX' in the table) +- ``FLASH_MAP_NO_FS``: no onboard FS + +Sketch can invoke a particular configuration by adding this line: + +.. code:: cpp + + FLASH_MAP_SETUP_CONFIG(FLASH_MAP_OTA_FS) + void setup () { ... } + void loop () { ... } + ++-------+--------------------------+----------------------------------------------------------+ +| Board | Flash chip size (bytes) | File system size (bytes) | ++=======+==========================+==========================================================+ +| Any | 512KBytes | 32KB(OTA), 64KB, 128KB(MAX) | ++-------+--------------------------+----------------------------------------------------------+ +| Any | 1MBytes | 64KB(OTA), 128KB, 144KB, 160KB, 192KB, 256KB, 512KB(MAX) | ++-------+--------------------------+----------------------------------------------------------+ +| Any | 2MBytes | 64KB, 128KB, 256KB(OTA), 512KB, 1MB(MAX) | ++-------+--------------------------+----------------------------------------------------------+ +| Any | 4MBytes | 1MB, 2MB(OTA), 3MB(MAX) | ++-------+--------------------------+----------------------------------------------------------+ +| Any | 8MBytes | 6MB(OTA), 7MB(MAX) | ++-------+--------------------------+----------------------------------------------------------+ +| Any | 16MBytes | 14MB(OTA), 15MB(MAX) | ++-------+--------------------------+----------------------------------------------------------+ **Note:** to use any of file system functions in the sketch, add the following include to the sketch: @@ -63,6 +65,7 @@ following include to the sketch: //#include "FS.h" // SPIFFS is declared #include "LittleFS.h" // LittleFS is declared + //#include "SDFS.h" // SDFS is declared SPIFFS Deprecation Warning -------------------------- @@ -90,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. @@ -127,7 +130,7 @@ when updgrading core versions. SPIFFS file system limitations ------------------------------ -The SPIFFS implementation for ESP8266 had to accomodate the +The SPIFFS implementation for ESP8266 had to accommodate the constraints of the chip, among which its limited RAM. `SPIFFS `__ was selected because it is designed for small systems, but that comes at the cost of some @@ -225,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) -------------------------------------------- @@ -474,8 +483,8 @@ Performs the same operation as ``info`` but allows for reporting greater than 4GB for filesystem size/used/etc. Should be used with the SD and SDFS filesystems since most SD cards today are greater than 4GB in size. -setTimeCallback(time_t (*cb)(void)) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +setTimeCallback(time_t (\*cb)(void)) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code:: cpp @@ -574,8 +583,8 @@ rewind Resets the internal pointer to the start of the directory. -setTimeCallback(time_t (*cb)(void)) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +setTimeCallback(time_t (\*cb)(void)) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Sets the time callback for any files accessed from this Dir object via openNextFile. Note that the SD and SDFS filesystems only support a filesystem-wide callback and @@ -693,7 +702,7 @@ Close the file. No other operations should be performed on *File* object after ``close`` function was called. openNextFile (compatibiity method, not recommended for new code) -~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code:: cpp @@ -705,7 +714,7 @@ Opens the next file in the directory pointed to by the File. Only valid when ``File.isDirectory() == true``. rewindDirectory (compatibiity method, not recommended for new code) -~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code:: cpp @@ -718,8 +727,8 @@ rewindDirectory (compatibiity method, not recommended for new code) Resets the ``openNextFile`` pointer to the top of the directory. Only valid when ``File.isDirectory() == true``. -setTimeCallback(time_t (*cb)(void)) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +setTimeCallback(time_t (\*cb)(void)) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Sets the time callback for this specific file. Note that the SD and SDFS filesystems only support a filesystem-wide callback and calls to 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 new file mode 100644 index 0000000000..25fd08defa --- /dev/null +++ b/doc/ideoptions.rst @@ -0,0 +1,297 @@ +esp8266 configuration +===================== + +Overview +-------- + +There are a number of specific options for esp8266 in the Arduino IDE Tools +menu. Not all of them are available for every board. If one is needed and +not visible, please try using the generic esp8266 or esp8285 board. + +In every menu entry, the first option is the default one and is suitable for +most users (except for flash size in the generic ESP8266 board). + +Note about PlatformIO +--------------------- + +`PlatformIO specific documentation +`__ +is also available. Note that this link is available here for reference and +is not maintained by the esp8266 Arduino core platform team. + +Arduino IDE Tools Menu +---------------------- + +Board +~~~~~ + +Most of the time there is only one type of ESP8266 chip and only one type of +ESP8285(1M) chip shipped with hardware or DIY boards. Capabilities are the +same everywhere. Hardware devices differ only on routed GPIO and external +components. + +If a specific hardware is not available in this list, "Generic ESP82xx" +always work. + +Upload Speed +~~~~~~~~~~~~ + +This the UART speed setup for flashing the ESP. It is not related with the +UART(Serial) speed programmed from inside the sketch, if enabled. Default +values are legacy. The highest speed option (around 1Mbaud) should always +work. For specific boards, defaults can be updated using the `board.txt +generator `__. + +CPU Frequency +~~~~~~~~~~~~~ + +Any ESP82xx can run at 80 or 160MHz. + +Crystal Frequency +~~~~~~~~~~~~~~~~~ + +This is the on-board crystal frequency (26 or 40Mhz). Default is 26MHz. +But the chip was designed with 40MHz. It explains the default strange 74880 +baud rate at boot, which is 115200\*26/40 (115200 being quite a lot used +by default nowadays). + +Flash Size +~~~~~~~~~~ + +With the Arduino core, ESP82xx can use at most 1MB to store the main sketch +in flash memory. + +ESP8285 has 1MB internal flash capacity. ESP8266 is always shipped with an +external flash chip that is most often 1MB (esp01, esp01s, lots of +commercial appliances), 4MB (DIY boards like wemos/lolin D1 mini or nodemcu) +or 16MB (lolin D1 mini pro). But configurations with 2MB and 8MB also +exist. This core is also able to use older 512KB chips that are today not +much used and officially deprecated by Espressif. + +Flash space is divided into 3 main zones. The first is the user program +space, 1MB at most. The second is enough space for the OTA ability. The +third, the remaining space, can be used to hold a filesystem (LittleFS). + +This list proposes many different configurations. In the generic board +list, the first one of each size is the default and suitable for many cases. + +Example: ``4MB (FS:2MB OTA:~1019KB)``: + +- 4MB is the flash chip size (= 4 MBytes, sometimes oddly noted 32Mbits) +- ``OTA:~1019KB`` (around 1MB) is used for Over The Air flashing (note that OTA binary can be gzip-ed) +- ``FS:2MB`` means that 2MBytes are used for an internal filesystem (LittleFS). + +Flash Mode +~~~~~~~~~~ + +There are four options. The most compatible and slowest is ``DOUT``. The +fasted is ``QIO``. ESP8266 mcu is able to use any of these modes, but +depending on the flash chips capabilities and how it is connected to the +esp8266, the fastest mode may not be working. Note that ESP8285 requires +the ``DOUT`` mode. + +Here is some more insights about that in `esp32 forums `__. + +Reset Method +~~~~~~~~~~~~ + +| On some boards (commonly NodeMCU, Lolin/Wemos) an electronic trick allows to + use the UART DTR line to reset the esp8266 *and* put it in flash mode. This + is the default ``dtr (aka nodemcu)`` option. It provides an extra-easy way of + flashing from serial port. +| When not available, the ``no dtr`` option can be + used in conjunction with a flash button on the board (or by driving the ESP + dedicated GPIOs to boot in flash mode at power-on). + +Debug Port +~~~~~~~~~~ + +There are three UART options: + +- disabled +- Serial +- Serial1 + +When on ``Serial`` or ``Serial1`` options (see +`reference `__), messages are sent at 74880 bauds at +boot time then baud rate is changed to user configuration in sketch. These +messages are generated by the internal bootloader. Subsequent serial data +are coming either from the firmware, this Arduino core, and user application. + +Debug Level +~~~~~~~~~~~ + +There are a number of options. + +- The first (``None``) is explained by itself. +- 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 + 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 +~~~~~~~~~~~~ + +`lwIP `__ is the internal network +software stack. It is highly configurable and comes with features that can +be enabled, at the price of RAM or FLASH space usage. + +There are 6 variants. As always, the first and default option is a good +compromise. Note that cores v2.x were or could be using the lwIP-v1 stack. +Only lwIP-v2 is available on cores v3+. + +- v2 Lower Memory + + This is lwIP-v2 with MSS=536 bytes. MSS is TCP's `Maximum Segment Size`, + and different from MTU (IP's Maximum Transfer Unit) which is always 1480 + in our case. + Using such value for MSS is 99.9% compatible with any TCP peers, allows to + store less data in RAM, and is consequently slower when transmitting large + segments of data (using TCP) because of a larger overhead and latency due to + smaller payload and larger number of packets. + + UDP and other IP protocols are not affected by MSS value. + +- v2 Higher Bandwidth + + When streaming large amount of data, prefer this option. It uses more + memory (MSS=1460) so it allows faster transfers thanks to a smaller number + of packets providing lower overhead and higher bandwidth. + +- ... (no features) + + Disabled features to get more flash space and RAM for users are: + + - No IP Forwarding (=> no NAT), + + - No IP Fragmentation and reassembly, + + - No AutoIP (not getting 169.254.x.x on DHCP request when there is no DHCP answer), + + - | No SACK-OUT (= no Selective ACKnowledgements for OUTput): + | no better stability with long distance TCP transfers, + + - No listen backlog (no protection against DOS attacks for TCP server). + +- IPv6 ... + + With these options, IPv6 is enabled, with features. It uses about 20-30KB + of supplementary flash space. + +VTable location +~~~~~~~~~~~~~~~ + +This is the mechanism used in C++ to support dynamic dispatch of virtual +methods. By default these tables are stored in flash to save precious RAM +bytes, but in very specific cases they can be stored in Heap space, or IRAM +space (both in RAM). + +C++ Exceptions +~~~~~~~~~~~~~~ + +- C++ exceptions are disabled by default. Consequently the ``new`` + operator will cause a general failure and a reboot when memory is full. + + Note that the C-``malloc`` function always returns ``nullptr`` when + memory is full. + +- Enabled: on this Arduino core, exceptions are possible. Note that they + are quite ram and flash consuming. + +Stack protection +~~~~~~~~~~~~~~~~ + +- This is disabled by default + +- When Enabled, the compiler generated extra code to check for stack + overflows. When this happens, an exception is raised with a message and + the ESP reboots. + +Erase Flash +~~~~~~~~~~~ + +- ``Only sketch``: When WiFi is enabled at boot and persistent WiFi + credentials are enabled, these data are preserved across flashings. + Filesystem is preserved. + +- ``Sketch + WiFi settings``: persistent WiFi settings are not + preserved accross flashings. Filesystem is preserved. + +- ``All Flash``: WiFi settings and Filesystems are erased. + +NONOS SDK Version +~~~~~~~~~~~~~~~~~~ + +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 +~~~~~~~~~~~ + +The first and default choice (``All SSL ciphers``) is good. The second +option enables only the main ciphers and can be used to lower flash +occupation. + +MMU (Memory Management Unit) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Head to its `specific documentation `__. Note that there is an option +providing an additional 16KB of IRAM to your application which can be used +with ``new`` and ``malloc``. + +Non-32-Bit Access +~~~~~~~~~~~~~~~~~ + +On esp82xx architecture, DRAM can be accessed byte by byte, but read-only +flash space (``PROGMEM`` variables) and IRAM cannot. By default they can +only be safely accessed in a compatible way using special macros +``pgm_read_some()``. + +With the non-default option ``Byte/Word access``, an exception manager +allows to transparently use them as if they were byte-accessible. As a +result, any type of access works but in a very slow way for the usually +illegal ones. This mode can also be enabled from the MMU options. diff --git a/doc/index.rst b/doc/index.rst index dcc5f69628..5f3ec247c6 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -6,6 +6,7 @@ Welcome to ESP8266 Arduino Core's documentation! :caption: Contents: Installing + Arduino IDE options Reference Libraries File system diff --git a/doc/installing.rst b/doc/installing.rst index cd8e58a7ab..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,20 +101,21 @@ 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 .. code:: bash - cd esp8266/tools + cd tools python3 get.py - Restart Arduino @@ -181,25 +182,28 @@ 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 .. code:: bash - - cd esp8266/tools + + 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/libraries.rst b/doc/libraries.rst index cf860231ec..625935554b 100644 --- a/doc/libraries.rst +++ b/doc/libraries.rst @@ -1,8 +1,8 @@ Libraries ========= -WiFi(ESP8266WiFi library) -------------------------- +WiFi (ESP8266WiFi library) +-------------------------- ESP8266WiFi library has been developed basing on ESP8266 SDK, using naming convention and overall functionality philosophy of the `Arduino WiFi Shield library `__. Over time the wealth Wi-Fi features ported from ESP8266 SDK to this library outgrew the APIs of WiFi Shield library and it became apparent that we need to provide separate documentation on what is new and extra. @@ -87,7 +87,7 @@ Some ESP-specific APIs related to deep sleep, RTC and flash memories are availab ``ESP.getHeapFragmentation()`` returns the fragmentation metric (0% is clean, more than ~50% is not harmless) -``ESP.getMaxFreeBlockSize()`` returns the largest contiguous free RAM block in the heap, useful for checking heap fragmentation. **NOTE:** Maximum ``malloc()``able block will be smaller due to memory manager overheads. +``ESP.getMaxFreeBlockSize()`` returns the largest contiguous free RAM block in the heap, useful for checking heap fragmentation. **NOTE:** Maximum ``malloc()`` -able block will be smaller due to memory manager overheads. ``ESP.getChipId()`` returns the ESP8266 chip ID as a 32-bit integer. @@ -172,7 +172,7 @@ Libraries that don't rely on low-level access to AVR registers should work well. - `RTC `__ - Arduino Library for Ds1307 & Ds3231 compatible with ESP8266. - `Souliss, Smart Home `__ - Framework for Smart Home based on Arduino, Android and openHAB. - `ST7735 `__ - Adafruit's ST7735 library modified to be compatible with ESP8266. Just make sure to modify the pins in the examples as they are still AVR specific. -- `Task `__ - Arduino Nonpreemptive multitasking library. While similiar to the included Ticker library in the functionality provided, this library was meant for cross Arduino compatibility. +- `Task `__ - Arduino Nonpreemptive multitasking library. While similar to the included Ticker library in the functionality provided, this library was meant for cross Arduino compatibility. - `TickerScheduler `__ - Library provides simple scheduler for ``Ticker`` to avoid WDT reset - `Teleinfo `__ - Generic French Power Meter library to read Teleinfo energy monitoring data such as consuption, contract, power, period, ... This library is cross platform, ESP8266, Arduino, Particle, and simple C++. French dedicated `post `__ on author's blog and all related information about `Teleinfo `__ also available. - `UTFT-ESP8266 `__ - UTFT display library with support for ESP8266. Only serial interface (SPI) displays are supported for now (no 8-bit parallel mode, etc). Also includes support for the hardware SPI controller of the ESP8266. diff --git a/doc/mmu.rst b/doc/mmu.rst index 41446cf76c..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,25 +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/a-ota-python-configuration.png b/doc/ota_updates/a-ota-python-configuration.png deleted file mode 100644 index 0e78f72a12..0000000000 Binary files a/doc/ota_updates/a-ota-python-configuration.png and /dev/null differ diff --git a/doc/ota_updates/readme.rst b/doc/ota_updates/readme.rst index 5575d45f24..de351fe3f2 100755 --- a/doc/ota_updates/readme.rst +++ b/doc/ota_updates/readme.rst @@ -50,7 +50,7 @@ Signed updates are updates whose compiled binaries are signed with a private key Cryptographic signing only protects against tampering with binaries delivered via OTA. If someone has physical access, they will always be able to flash the device over the serial port. Signing also does not encrypt anything but the hash (so that it can't be modified), so this does not protect code inside the device: if a user has physical access they can read out your program. -**Securing your private key is paramount. The same private/public keypair that was used with the original upload must also be used to sign later binaries. Loss of the private key associated with a binary means that you will not be able to OTA-update any of your devices in the field. Alternatively, if someone else copies the private key, then they will be able to use it to sign binaries which will be accepted by the ESP.** +**Securing your private key is paramount. The same private/public key pair that was used with the original upload must also be used to sign later binaries. Loss of the private key associated with a binary means that you will not be able to OTA-update any of your devices in the field. Alternatively, if someone else copies the private key, then they will be able to use it to sign binaries which will be accepted by the ESP.** Signed Binary Format ^^^^^^^^^^^^^^^^^^^^ @@ -68,7 +68,7 @@ Signed Binary Prerequisites OpenSSL is required to run the standard signing steps, and should be available on any UNIX-like or Windows system. As usual, the latest stable version of OpenSSL is recommended. -Signing requires the generation of an RSA-2048 key (other bit lengths are supported as well, but 2048 is a good selection today) using any appropriate tool. The following shell commands will generate a new public/private keypair. Run them in the sketch directory: +Signing requires the generation of an RSA-2048 key (other bit lengths are supported as well, but 2048 is a good selection today) using any appropriate tool. The following shell commands will generate a new public/private key pair. Run them in the sketch directory: .. code:: bash @@ -161,7 +161,7 @@ If signing is desired, sign the gzip compressed file *after* compression. Updating apps in the field to support compression ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If you have applications deployed in the field and wish to update them to support compressed OTA uploads, you will need to first recompile the application, then _upload the uncompressed `.bin` file once_. Attempting to upload a `gzip` compressed binary to a legacy app will result in the Updater rejecting the upload as it does not understand the `gzip` format. After this initial upload, which will include the new bootloader and `Updater` class with compression support, compressed updates can then be used. +If you have applications deployed in the field and wish to update them to support compressed OTA uploads, you will need to first recompile the application, then _upload the uncompressed `.bin` file once. Attempting to upload a `gzip` compressed binary to a legacy app will result in the Updater rejecting the upload as it does not understand the `gzip` format. After this initial upload, which will include the new bootloader and `Updater` class with compression support, compressed updates can then be used. Safety @@ -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: @@ -226,13 +226,6 @@ Instructions below show configuration of OTA on a NodeMCU 1.0 (ESP-12E Module) b - esp8266/Arduino platform package 2.0.0 or newer - for instructions follow https://github.com/esp8266/Arduino#installing-with-boards-manager - - Python 3.x - https://www.python.org/ - - **Note:** Windows users should select “Add python.exe to Path” - (see below – this option is not selected by default). - - .. figure:: a-ota-python-configuration.png - :alt: Python installation set up 2. Now prepare the sketch and configuration for upload via a serial port. @@ -343,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 @@ -414,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: @@ -508,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. @@ -530,7 +523,10 @@ Simple updater downloads the file every time the function is called. .. code:: cpp - ESPhttpUpdate.update("192.168.0.2", 80, "/arduino.bin"); + #include + + WiFiClient client; + ESPhttpUpdate.update(client, "192.168.0.2", 80, "/arduino.bin"); Advanced updater ^^^^^^^^^^^^^^^^ @@ -541,7 +537,10 @@ The server-side script can respond as follows: - response code 200, and send the .. code:: cpp - t_httpUpdate_return ret = ESPhttpUpdate.update("192.168.0.2", 80, "/esp/update/arduino.php", "optional current version string here"); + #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) { case HTTP_UPDATE_FAILED: Serial.println("[update] Update failed."); @@ -554,6 +553,11 @@ The server-side script can respond as follows: - response code 200, and send the break; } +TLS updater +^^^^^^^^^^^ + +Please read and try the examples provided with the library. + Server request handling ~~~~~~~~~~~~~~~~~~~~~~~ @@ -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 2ae8ca5cd8..e06dfd8a3d 100644 --- a/doc/reference.rst +++ b/doc/reference.rst @@ -9,13 +9,13 @@ and have several limitations: * Interrupt callback functions must be in IRAM, because the flash may be in the middle of other operations when they occur. Do this by adding - the ``ICACHE_RAM_ATTR`` attribute on the function definition. If this + the ``IRAM_ATTR`` attribute on the function definition. If this attribute is not present, the sketch will crash when it attempts to ``attachInterrupt`` with an error message. .. code:: cpp - ICACHE_RAM_ATTR void gpio_change_handler(void *data) {... + IRAM_ATTR void gpio_change_handler(void *data) {... * Interrupts must not call ``delay()`` or ``yield()``, or call any routines which internally use ``delay()`` or ``yield()`` either. @@ -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 ---------- @@ -69,17 +76,22 @@ Pin interrupts are supported through ``attachInterrupt``, ``detachInterrupt`` functions. Interrupts may be attached to any GPIO pin, except GPIO16. Standard Arduino interrupt types are supported: ``CHANGE``, ``RISING``, ``FALLING``. ISRs need to have -``ICACHE_RAM_ATTR`` before the function definition. +``IRAM_ATTR`` before the function definition. Analog input ------------ +**NOTE:** +Calling ``analogRead()`` too frequently causes WiFi to stop working. When +WiFi is under operation, ``analogRead()`` result may be cached for at least +5ms between effective calls. + ESP8266 has a single ADC channel available to users. It may be used either to read voltage at ADC pin, or to read module supply voltage (VCC). To read external voltage applied to ADC pin, use ``analogRead(A0)``. -Input voltage range of bare ESP8266 is 0 — 1.0V, however some many +Input voltage range of bare ESP8266 is 0 — 1.0V, however some boards may implement voltage dividers. To be on the safe side, <1.0V can be tested. If e.g. 0.5V delivers values around ~512, then maximum voltage is very likely to be 1.0V and 3.3V may harm the ESP8266. @@ -109,6 +121,9 @@ PWM range may be changed by calling ``analogWriteRange(new_range)`` or ``analogWriteResolution(bits)``. ``new_range`` may be from 15...65535 or ``bits`` may be from 4...16. +The function ``analogWriteMode(pin, value, openDrain)`` allows to sets +the pin mode to ``OUTPUT_OPEN_DRAIN`` instead of ``OUTPUT``. + **NOTE:** The default ``analogWrite`` range was 1023 in releases before 3.0, but this lead to incompatibility with external libraries which depended on the Arduino core default of 256. Existing applications which @@ -323,3 +338,196 @@ C++ This assures correct behavior, including handling of all subobjects, which guarantees stability. History: `#6269 `__ `#6309 `__ `#6312 `__ + +Streams +------- + +Arduino API + + Stream is one of the core classes in the Arduino API. Wire, serial, network and + filesystems are streams, from which data are read or written. + + Making a transfer with streams is quite common, like for example the + historical WiFiSerial sketch: + + .. code:: cpp + + //check clients for data + //get data from the telnet client and push it to the UART + while (serverClient.available()) { + Serial.write(serverClient.read()); + } + + //check UART for data + if (Serial.available()) { + size_t len = Serial.available(); + uint8_t sbuf[len]; + Serial.readBytes(sbuf, len); + //push UART data to all connected telnet clients + if (serverClient && serverClient.connected()) { + serverClient.write(sbuf, len); + } + } + + One will notice that in the network to serial direction, data are transferred + byte by byte while data are available. In the other direction, a temporary + buffer is created on stack, filled with available serial data, then + transferred to network. + + The ``readBytes(buffer, length)`` method includes a timeout to ensure that + all required bytes are received. The ``write(buffer, length)`` (inherited + from ``Print::``) function is also usually blocking until the full buffer is + transmitted. Both functions return the number of transmitted bytes. + + That's the way the Stream class works and is commonly used. + + Classes derived from ``Stream::`` also usually introduce the ``read(buffer, + len)`` method, which is similar to ``readBytes(buffer, len)`` without + timeout: the returned value can be less than the requested size, so special + care must be taken with this function, introduced in the Arduino + ``Client::`` class (cf. AVR reference implementation). + This function has also been introduced in other classes + that don't derive from ``Client::``, e.g. ``HardwareSerial::``. + +Stream extensions + + Stream extensions are designed to be compatible with Arduino API, and + offer additional methods to make transfers more efficient and easier to + use. + + The serial to network transfer above can be written like this: + + .. code:: cpp + + serverClient.sendAvailable(Serial); // chunk by chunk + Serial.sendAvailable(serverClient); // chunk by chunk + + An echo service can be written like this: + + .. code:: cpp + + serverClient.sendAvailable(serverClient); // tcp echo service + + Serial.sendAvailable(Serial); // serial software loopback + + Beside reducing coding time, these methods optimize transfers by avoiding + buffer copies when possible. + + - User facing API: ``Stream::send()`` + + The goal of streams is to transfer data between producers and consumers, + like the telnet/serial example above. Four methods are provided, all of + them return the number of transmitted bytes: + + - ``Stream::sendSize(dest, size [, timeout])`` + + This method waits up to the given or default timeout to transfer + ``size`` bytes to the the ``dest`` Stream. + + - ``Stream::sendUntil(dest, delim [, timeout])`` + + This method waits up to the given or default timeout to transfer data + until the character ``delim`` is met. + Note: The delimiter is read but not transferred (like ``readBytesUntil``) + + - ``Stream::sendAvailable(dest)`` + + This method transfers all already available data to the destination. + There is no timeout and the returned value is 0 when there is nothing + to transfer or no room in the destination. + + - ``Stream::sendAll(dest [, timeout])`` + + This method waits up to the given or default timeout to transfer all + available data. It is useful when source is able to tell that no more + data will be available for this call, or when destination can tell + that it will no be able to receive anymore. + + For example, a source String will not grow during the transfer, or a + particular network connection supposed to send a fixed amount of data + before closing. ``::sendAll()`` will receive all bytes. Timeout is + useful when destination needs processing time (e.g. network or serial + input buffer full = please wait a bit). + + - String, flash strings helpers + + Two additional classes are provided. + + - ``StreamConstPtr::`` is designed to hold a constant buffer (in ram or flash). + + With this class, a ``Stream::`` can be made from ``const char*``, + ``F("some words in flash")`` or ``PROGMEM`` strings. This class makes + no copy, even with data in flash. For flash content, byte-by-byte + transfers is a consequence when "memcpy_P" cannot be used. Other + contents can be transferred at once when possible. + + .. code:: cpp + + StreamConstPtr css(F("my long css data")); // CSS data not copied to RAM + server.sendAll(css); + + - ``S2Stream::`` is designed to make a ``Stream::`` out of a ``String::`` without copy. + + .. code:: cpp + + String helloString("hello"); + S2Stream hello(helloString); + hello.reset(0); // prevents ::read() to consume the string + + hello.sendAll(Serial); // shows "hello" + hello.sendAll(Serial); // shows nothing, content has already been read + hello.reset(); // reset content pointer + hello.sendAll(Serial); // shows "hello" + hello.reset(3); // reset content pointer to a specific position + hello.sendAll(Serial); // shows "lo" + + hello.setConsume(); // ::read() will consume, this is the default + Serial.println(helloString.length()); // shows 5 + hello.sendAll(Serial); // shows "hello" + Serial.println(helloString.length()); // shows 0, string is consumed + + ``StreamString::`` derives from ``S2Stream`` + + .. code:: cpp + + StreamString contentStream; + client.sendSize(contentStream, SOME_SIZE); // receives at most SOME_SIZE bytes + + // equivalent to: + + String content; + S2Stream contentStream(content); + client.sendSize(contentStream, SOME_SIZE); // receives at most SOME_SIZE bytes + // content has the data + + - Internal Stream API: ``peekBuffer`` + + Here is the method list and their significations. They are currently + implemented in ``HardwareSerial``, ``WiFiClient`` and + ``WiFiClientSecure``. + + - ``virtual bool hasPeekBufferAPI ()`` returns ``true`` when the API is present in the class + + - ``virtual size_t peekAvailable ()`` returns the number of reachable bytes + + - ``virtual const char* peekBuffer ()`` returns the pointer to these bytes + + This API requires that any kind of ``"read"`` function must not be called after ``peekBuffer()`` + and until ``peekConsume()`` is called. + + - ``virtual void peekConsume (size_t consume)`` tells to discard that number of bytes + + - ``virtual bool inputCanTimeout ()`` + + A ``StringStream`` will return false. A closed network connection returns false. + This function allows ``Stream::sendAll()`` to return earlier. + + - ``virtual bool outputCanTimeout ()`` + + A closed network connection returns false. + This function allows ``Stream::sendAll()`` to return earlier. + + - ``virtual ssize_t streamRemaining()`` + + It returns -1 when stream remaining size is unknown, depending on implementation + (string size, file size..). diff --git a/doc/requirements.txt b/doc/requirements.txt index 0db3ae5bac..6b2684762d 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1,6 +1,8 @@ # Requirements file for pip # list of Python packages used in documentation build -sphinx -sphinx-rtd-theme -breathe -nbsphinx +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/keywords.txt b/keywords.txt index 3272304b92..ca7b2f1337 100644 --- a/keywords.txt +++ b/keywords.txt @@ -13,9 +13,9 @@ analogWriteFreq KEYWORD2 analogWriteRange KEYWORD2 baudrate KEYWORD2 swap KEYWORD2 +enablePhaseLockedWaveform KEYWORD2 ###################################### # Constants (LITERAL1) ####################################### INPUT_PULLDOWN_16 LITERAL1 -PWMRANGE LITERAL1 diff --git a/libraries/ArduinoOTA/ArduinoOTA.cpp b/libraries/ArduinoOTA/ArduinoOTA.cpp index 83cc5c7905..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 { @@ -349,6 +358,34 @@ void ArduinoOTAClass::_runUpdate() { } } +void ArduinoOTAClass::end() { + if (!_initialized) + return; + + _initialized = false; + 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; + 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 323c8b73d5..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); @@ -62,6 +73,13 @@ class ArduinoOTAClass //Starts the ArduinoOTA service void begin(bool useMDNS = true); + //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(); @@ -82,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/BasicOTA/BasicOTA.ino b/libraries/ArduinoOTA/examples/BasicOTA/BasicOTA.ino index ddc5629741..6f175ccaff 100644 --- a/libraries/ArduinoOTA/examples/BasicOTA/BasicOTA.ino +++ b/libraries/ArduinoOTA/examples/BasicOTA/BasicOTA.ino @@ -5,7 +5,7 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif const char* ssid = STASSID; @@ -39,7 +39,7 @@ void setup() { String type; if (ArduinoOTA.getCommand() == U_FLASH) { type = "sketch"; - } else { // U_FS + } else { // U_FS type = "filesystem"; } 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/OTALeds/OTALeds.ino b/libraries/ArduinoOTA/examples/OTALeds/OTALeds.ino index 7d05074e15..57b1b75a22 100644 --- a/libraries/ArduinoOTA/examples/OTALeds/OTALeds.ino +++ b/libraries/ArduinoOTA/examples/OTALeds/OTALeds.ino @@ -5,7 +5,7 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif const char* ssid = STASSID; @@ -14,7 +14,7 @@ const char* host = "OTA-LEDS"; int led_pin = 13; #define N_DIMMERS 3 -int dimmer_pin[] = {14, 5, 15}; +int dimmer_pin[] = { 14, 5, 15 }; void setup() { Serial.begin(115200); @@ -45,14 +45,14 @@ void setup() { } ArduinoOTA.setHostname(host); - ArduinoOTA.onStart([]() { // switch off all the PWMs during upgrade + ArduinoOTA.onStart([]() { // switch off all the PWMs during upgrade for (int i = 0; i < N_DIMMERS; i++) { analogWrite(dimmer_pin[i], 0); } analogWrite(led_pin, 0); }); - ArduinoOTA.onEnd([]() { // do a fancy thing with our board led at end + ArduinoOTA.onEnd([]() { // do a fancy thing with our board led at end for (int i = 0; i < 30; i++) { analogWrite(led_pin, (i * 100) % 1001); delay(50); @@ -67,7 +67,6 @@ void setup() { /* setup the OTA server */ ArduinoOTA.begin(); Serial.println("Ready"); - } void loop() { 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 5f1f5020a1..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. @@ -20,7 +21,7 @@ /* Set these to your desired softAP credentials. They are not configurable at runtime */ #ifndef APSSID #define APSSID "ESP_ap" -#define APPSK "12345678" +#define APPSK "12345678" #endif const char *softAP_ssid = APSSID; @@ -62,7 +63,7 @@ void setup() { /* You can remove the password parameter if you want the AP to be open. */ WiFi.softAPConfig(apIP, apIP, netMsk); WiFi.softAP(softAP_ssid, softAP_password); - delay(500); // Without delay I've seen the IP address blank + delay(500); // Without delay I've seen the IP address blank Serial.print("AP IP address: "); Serial.println(WiFi.softAPIP()); @@ -74,13 +75,13 @@ 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 + server.begin(); // Web server start Serial.println("HTTP server started"); - loadCredentials(); // Load WLAN credentials from network - connect = strlen(ssid) > 0; // Request WLAN connect if there is a SSID + loadCredentials(); // Load WLAN credentials from network + connect = strlen(ssid) > 0; // Request WLAN connect if there is a SSID } void connectWifi() { @@ -106,7 +107,7 @@ void loop() { /* Don't set retry time too low as retry interfere the softAP operation */ connect = true; } - if (status != s) { // WLAN status change + if (status != s) { // WLAN status change Serial.print("Status: "); Serial.println(s); status = s; @@ -130,13 +131,11 @@ void loop() { WiFi.disconnect(); } } - if (s == WL_CONNECTED) { - MDNS.update(); - } + if (s == WL_CONNECTED) { MDNS.update(); } } // Do work: - //DNS + // DNS dnsServer.processNextRequest(); - //HTTP + // HTTP server.handleClient(); } diff --git a/libraries/DNSServer/examples/CaptivePortalAdvanced/handleHttp.ino b/libraries/DNSServer/examples/CaptivePortalAdvanced/handleHttp.ino index dc286c1840..c7380017d1 100644 --- a/libraries/DNSServer/examples/CaptivePortalAdvanced/handleHttp.ino +++ b/libraries/DNSServer/examples/CaptivePortalAdvanced/handleHttp.ino @@ -1,6 +1,6 @@ /** Handle root or redirect to captive portal */ void handleRoot() { - if (captivePortal()) { // If caprive portal redirect instead of displaying the page. + if (captivePortal()) { // If caprive portal redirect instead of displaying the page. return; } server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); @@ -8,8 +8,7 @@ void handleRoot() { server.sendHeader("Expires", "-1"); String Page; - Page += F( - "" + Page += F("" "" "CaptivePortal" "

HELLO WORLD!!

"); @@ -18,8 +17,7 @@ void handleRoot() { } else { Page += String(F("

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

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

You may want to config the wifi connection.

" + Page += F("

You may want to config the wifi connection.

" ""); server.send(200, "text/html", Page); @@ -29,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; @@ -44,8 +40,7 @@ void handleWifi() { server.sendHeader("Expires", "-1"); String Page; - Page += F( - "" + Page += F("" "" "CaptivePortal" "

Wifi config

"); @@ -54,40 +49,31 @@ void handleWifi() { } else { Page += String(F("

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

"); } - Page += - String(F( - "\r\n
" - "" - "" - "" - "
SoftAP config
SSID ")) + - String(softAP_ssid) + - F("
IP ") + - toStringIp(WiFi.softAPIP()) + - F("
" - "\r\n
" - "" - "" - "" - "
WLAN config
SSID ") + - String(ssid) + - F("
IP ") + - toStringIp(WiFi.localIP()) + - F("
" - "\r\n
" - ""); + Page += String(F("\r\n
" + "
WLAN list (refresh if any missing)
" + "" + "" + "
SoftAP config
SSID ")) + + String(softAP_ssid) + F("
IP ") + + toStringIp(WiFi.softAPIP()) + F("
" + "\r\n
" + "" + "" + "" + "
WLAN config
SSID ") + + String(ssid) + F("
IP ") + + toStringIp(WiFi.localIP()) + F("
" + "\r\n
" + ""); Serial.println("scan start"); int n = WiFi.scanNetworks(); Serial.println("scan done"); if (n > 0) { - for (int i = 0; i < n; i++) { - Page += String(F("\r\n"); - } + for (int i = 0; i < n; i++) { Page += String(F("\r\n"); } } else { Page += F(""); } - Page += F( - "
WLAN list (refresh if any missing)
SSID ")) + WiFi.SSID(i) + ((WiFi.encryptionType(i) == ENC_TYPE_NONE) ? F(" ") : F(" *")) + F(" (") + WiFi.RSSI(i) + F(")
SSID ")) + WiFi.SSID(i) + ((WiFi.encryptionType(i) == ENC_TYPE_NONE) ? F(" ") : F(" *")) + F(" (") + WiFi.RSSI(i) + F(")
No WLAN found
" + Page += F("" "\r\n

Connect to network:

" "" "
" @@ -95,7 +81,7 @@ void handleWifi() { "

You may want to return to the home page.

" ""); server.send(200, "text/html", Page); - server.client().stop(); // Stop is needed because we sent no content length + server.client().stop(); // Stop is needed because we sent no content length } /** Handle the WLAN save form and redirect to WLAN config page again */ @@ -103,18 +89,13 @@ 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 + connect = strlen(ssid) > 0; // Request WLAN connect with new credentials if there is a SSID } void handleNotFound() { - if (captivePortal()) { // If caprive portal redirect instead of displaying the error page. + if (captivePortal()) { // If caprive portal redirect instead of displaying the error page. return; } String message = F("File Not Found\n\n"); @@ -126,9 +107,7 @@ void handleNotFound() { message += server.args(); message += F("\n"); - for (uint8_t i = 0; i < server.args(); i++) { - message += String(F(" ")) + server.argName(i) + F(": ") + server.arg(i) + F("\n"); - } + for (uint8_t i = 0; i < server.args(); i++) { message += String(F(" ")) + server.argName(i) + F(": ") + server.arg(i) + F("\n"); } server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); server.sendHeader("Pragma", "no-cache"); server.sendHeader("Expires", "-1"); diff --git a/libraries/DNSServer/examples/CaptivePortalAdvanced/tools.ino b/libraries/DNSServer/examples/CaptivePortalAdvanced/tools.ino index e4840a1c12..65219bdccb 100644 --- a/libraries/DNSServer/examples/CaptivePortalAdvanced/tools.ino +++ b/libraries/DNSServer/examples/CaptivePortalAdvanced/tools.ino @@ -2,9 +2,7 @@ boolean isIp(String str) { for (size_t i = 0; i < str.length(); i++) { int c = str.charAt(i); - if (c != '.' && (c < '0' || c > '9')) { - return false; - } + if (c != '.' && (c < '0' || c > '9')) { return false; } } return true; } @@ -12,10 +10,7 @@ boolean isIp(String str) { /** IP to String? */ String toStringIp(IPAddress ip) { String res = ""; - for (int i = 0; i < 3; i++) { - res += String((ip >> (8 * i)) & 0xFF) + "."; - } + for (int i = 0; i < 3; i++) { res += String((ip >> (8 * i)) & 0xFF) + "."; } res += String(((ip >> 8 * 3)) & 0xFF); return res; } - 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 0f3ebd7a34..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 @@ -33,7 +43,7 @@ struct DNSHeader uint16_t ID; // identification number unsigned char RD : 1; // recursion desired unsigned char TC : 1; // truncated message - unsigned char AA : 1; // authoritive answer + unsigned char AA : 1; // authoritative answer unsigned char OPCode : 4; // message_type unsigned char QR : 1; // query/response flag unsigned char RCode : 4; // response code @@ -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/EEPROM/EEPROM.cpp b/libraries/EEPROM/EEPROM.cpp index 90bc5d1787..e193237d77 100644 --- a/libraries/EEPROM/EEPROM.cpp +++ b/libraries/EEPROM/EEPROM.cpp @@ -31,7 +31,7 @@ extern "C" { #include "spi_flash.h" } -extern "C" uint32_t _EEPROM_start; +#include EEPROMClass::EEPROMClass(uint32_t sector) : _sector(sector) @@ -39,7 +39,7 @@ EEPROMClass::EEPROMClass(uint32_t sector) } EEPROMClass::EEPROMClass(void) -: _sector((((uint32_t)&_EEPROM_start - 0x40200000) / SPI_FLASH_SEC_SIZE)) +: _sector(((EEPROM_start - 0x40200000) / SPI_FLASH_SEC_SIZE)) { } @@ -110,7 +110,7 @@ void EEPROMClass::write(int const address, uint8_t const value) { return; } if(!_data) { - DEBUGV("EEPROMClass::read without ::begin\n"); + DEBUGV("EEPROMClass::write without ::begin\n"); return; } diff --git a/libraries/EEPROM/examples/eeprom_clear/eeprom_clear.ino b/libraries/EEPROM/examples/eeprom_clear/eeprom_clear.ino index bb7d887c57..99ec3d98de 100644 --- a/libraries/EEPROM/examples/eeprom_clear/eeprom_clear.ino +++ b/libraries/EEPROM/examples/eeprom_clear/eeprom_clear.ino @@ -11,9 +11,7 @@ void setup() { EEPROM.begin(512); // write a 0 to all 512 bytes of the EEPROM - for (int i = 0; i < 512; i++) { - EEPROM.write(i, 0); - } + for (int i = 0; i < 512; i++) { EEPROM.write(i, 0); } // turn the LED on when we're done pinMode(13, OUTPUT); @@ -21,5 +19,4 @@ void setup() { EEPROM.end(); } -void loop() { -} +void loop() {} diff --git a/libraries/EEPROM/examples/eeprom_read/eeprom_read.ino b/libraries/EEPROM/examples/eeprom_read/eeprom_read.ino index 3295fecf9f..30d25a67de 100644 --- a/libraries/EEPROM/examples/eeprom_read/eeprom_read.ino +++ b/libraries/EEPROM/examples/eeprom_read/eeprom_read.ino @@ -32,9 +32,7 @@ void loop() { // there are only 512 bytes of EEPROM, from 0 to 511, so if we're // on address 512, wrap around to address 0 - if (address == 512) { - address = 0; - } + if (address == 512) { address = 0; } delay(500); } diff --git a/libraries/ESP8266AVRISP/examples/Arduino_Wifi_AVRISP/Arduino_Wifi_AVRISP.ino b/libraries/ESP8266AVRISP/examples/Arduino_Wifi_AVRISP/Arduino_Wifi_AVRISP.ino index fdc34cff2b..b86a4fc021 100644 --- a/libraries/ESP8266AVRISP/examples/Arduino_Wifi_AVRISP/Arduino_Wifi_AVRISP.ino +++ b/libraries/ESP8266AVRISP/examples/Arduino_Wifi_AVRISP/Arduino_Wifi_AVRISP.ino @@ -5,7 +5,7 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif const char* host = "esp8266-avrisp"; @@ -20,7 +20,7 @@ void setup() { Serial.begin(115200); Serial.println(""); Serial.println("Arduino AVR-ISP over TCP"); - avrprog.setReset(false); // let the AVR run + avrprog.setReset(false); // let the AVR run WiFi.mode(WIFI_STA); WiFi.begin(ssid, pass); @@ -51,17 +51,20 @@ void loop() { AVRISPState_t new_state = avrprog.update(); if (last_state != new_state) { switch (new_state) { - case AVRISP_STATE_IDLE: { + case AVRISP_STATE_IDLE: + { Serial.printf("[AVRISP] now idle\r\n"); // Use the SPI bus for other purposes break; } - case AVRISP_STATE_PENDING: { + case AVRISP_STATE_PENDING: + { Serial.printf("[AVRISP] connection pending\r\n"); // Clean up your other purposes and prepare for programming mode break; } - case AVRISP_STATE_ACTIVE: { + case AVRISP_STATE_ACTIVE: + { Serial.printf("[AVRISP] programming mode\r\n"); // Stand by for completion break; @@ -70,11 +73,7 @@ void loop() { last_state = new_state; } // Serve the client - if (last_state != AVRISP_STATE_IDLE) { - avrprog.serve(); - } + if (last_state != AVRISP_STATE_IDLE) { avrprog.serve(); } - if (WiFi.status() == WL_CONNECTED) { - MDNS.update(); - } + if (WiFi.status() == WL_CONNECTED) { MDNS.update(); } } diff --git a/libraries/ESP8266AVRISP/src/ESP8266AVRISP.cpp b/libraries/ESP8266AVRISP/src/ESP8266AVRISP.cpp index 0c8aa8451d..d0aec68d6a 100644 --- a/libraries/ESP8266AVRISP/src/ESP8266AVRISP.cpp +++ b/libraries/ESP8266AVRISP/src/ESP8266AVRISP.cpp @@ -72,7 +72,7 @@ AVRISPState_t ESP8266AVRISP::update() { switch (_state) { case AVRISP_STATE_IDLE: { if (_server.hasClient()) { - _client = _server.available(); + _client = _server.accept(); _client.setNoDelay(true); AVRISP_DEBUG("client connect %s:%d", _client.remoteIP().toString().c_str(), _client.remotePort()); _client.setTimeout(100); // for getch() @@ -121,7 +121,7 @@ AVRISPState_t ESP8266AVRISP::serve() { } inline void ESP8266AVRISP::_reject_incoming(void) { - while (_server.hasClient()) _server.available().stop(); + while (_server.hasClient()) _server.accept().stop(); } uint8_t ESP8266AVRISP::getch() { @@ -189,7 +189,7 @@ void ESP8266AVRISP::get_parameter(uint8_t c) { } void ESP8266AVRISP::set_parameters() { - // call this after reading paramter packet into buff[] + // call this after reading parameter packet into buff[] param.devicecode = buff[0]; param.revision = buff[1]; param.progtype = buff[2]; @@ -359,8 +359,12 @@ uint8_t ESP8266AVRISP::flash_read(uint8_t hilo, int addr) { 0); } -void ESP8266AVRISP::flash_read_page(int length) { +bool ESP8266AVRISP::flash_read_page(int length) { uint8_t *data = (uint8_t *) malloc(length + 1); + if (!data) + { + return false; + } for (int x = 0; x < length; x += 2) { *(data + x) = flash_read(LOW, here); *(data + x + 1) = flash_read(HIGH, here); @@ -369,12 +373,16 @@ void ESP8266AVRISP::flash_read_page(int length) { *(data + length) = Resp_STK_OK; _client.write((const uint8_t *)data, (size_t)(length + 1)); free(data); - return; + return true; } -void ESP8266AVRISP::eeprom_read_page(int length) { +bool ESP8266AVRISP::eeprom_read_page(int length) { // here again we have a word address uint8_t *data = (uint8_t *) malloc(length + 1); + if (!data) + { + return false; + } int start = here * 2; for (int x = 0; x < length; x++) { int addr = start + x; @@ -384,7 +392,7 @@ void ESP8266AVRISP::eeprom_read_page(int length) { *(data + length) = Resp_STK_OK; _client.write((const uint8_t *)data, (size_t)(length + 1)); free(data); - return; + return true; } void ESP8266AVRISP::read_page() { diff --git a/libraries/ESP8266AVRISP/src/ESP8266AVRISP.h b/libraries/ESP8266AVRISP/src/ESP8266AVRISP.h index 492831d3df..469f29b02c 100644 --- a/libraries/ESP8266AVRISP/src/ESP8266AVRISP.h +++ b/libraries/ESP8266AVRISP/src/ESP8266AVRISP.h @@ -89,8 +89,8 @@ class ESP8266AVRISP { void commit(int addr); void program_page(); uint8_t flash_read(uint8_t hilo, int addr); - void flash_read_page(int length); - void eeprom_read_page(int length); + bool flash_read_page(int length); + bool eeprom_read_page(int length); void read_page(); void read_signature(); diff --git a/libraries/ESP8266HTTPClient/examples/Authorization/Authorization.ino b/libraries/ESP8266HTTPClient/examples/Authorization/Authorization.ino index 9de3077ff4..459bc78e92 100644 --- a/libraries/ESP8266HTTPClient/examples/Authorization/Authorization.ino +++ b/libraries/ESP8266HTTPClient/examples/Authorization/Authorization.ino @@ -33,7 +33,6 @@ void setup() { WiFi.mode(WIFI_STA); WiFiMulti.addAP("SSID", "PASSWORD"); - } void loop() { diff --git a/libraries/ESP8266HTTPClient/examples/BasicHttpClient/BasicHttpClient.ino b/libraries/ESP8266HTTPClient/examples/BasicHttpClient/BasicHttpClient.ino index 21a97172ef..4dc96622ba 100644 --- a/libraries/ESP8266HTTPClient/examples/BasicHttpClient/BasicHttpClient.ino +++ b/libraries/ESP8266HTTPClient/examples/BasicHttpClient/BasicHttpClient.ino @@ -33,7 +33,6 @@ void setup() { WiFi.mode(WIFI_STA); WiFiMulti.addAP("SSID", "PASSWORD"); - } void loop() { @@ -68,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 2d6f49592b..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,28 +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_ptrclient(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 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 @@ -62,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/DigestAuthorization/DigestAuthorization.ino b/libraries/ESP8266HTTPClient/examples/DigestAuthorization/DigestAuthorization.ino index db8fe8cbd4..b71e3baf7a 100644 --- a/libraries/ESP8266HTTPClient/examples/DigestAuthorization/DigestAuthorization.ino +++ b/libraries/ESP8266HTTPClient/examples/DigestAuthorization/DigestAuthorization.ino @@ -13,36 +13,31 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif const char* ssid = STASSID; const char* ssidPassword = STAPSK; -const char *username = "admin"; -const char *password = "admin"; +const char* username = "admin"; +const char* password = "admin"; -const char *server = "http://httpbin.org"; -const char *uri = "/digest-auth/auth/admin/admin/MD5"; +const char* server = "http://httpbin.org"; +const char* uri = "/digest-auth/auth/admin/admin/MD5"; String exractParam(String& authReq, const String& param, const char delimit) { int _begin = authReq.indexOf(param); - if (_begin == -1) { - return ""; - } + if (_begin == -1) { return ""; } return authReq.substring(_begin + param.length(), authReq.indexOf(delimit, _begin + param.length())); } String getCNonce(const int len) { - static const char alphanum[] = - "0123456789" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz"; + static const char alphanum[] = "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz"; String s = ""; - for (int i = 0; i < len; ++i) { - s += alphanum[rand() % (sizeof(alphanum) - 1)]; - } + for (int i = 0; i < len; ++i) { s += alphanum[rand() % (sizeof(alphanum) - 1)]; } return s; } @@ -73,8 +68,7 @@ String getDigestAuth(String& authReq, const String& username, const String& pass md5.calculate(); String response = md5.toString(); - String authorization = "Digest username=\"" + username + "\", realm=\"" + realm + "\", nonce=\"" + nonce + - "\", uri=\"" + uri + "\", algorithm=\"MD5\", qop=auth, nc=" + String(nc) + ", cnonce=\"" + cNonce + "\", response=\"" + response + "\""; + String authorization = "Digest username=\"" + username + "\", realm=\"" + realm + "\", nonce=\"" + nonce + "\", uri=\"" + uri + "\", algorithm=\"MD5\", qop=auth, nc=" + String(nc) + ", cnonce=\"" + cNonce + "\", response=\"" + response + "\""; Serial.println(authorization); return authorization; @@ -100,7 +94,7 @@ void setup() { void loop() { WiFiClient client; - HTTPClient http; //must be declared after WiFiClient for correct destruction order, because used by http.begin(client,...) + HTTPClient http; // must be declared after WiFiClient for correct destruction order, because used by http.begin(client,...) Serial.print("[HTTP] begin...\n"); @@ -108,7 +102,7 @@ void loop() { http.begin(client, String(server) + String(uri)); - const char *keys[] = {"WWW-Authenticate"}; + const char* keys[] = { "WWW-Authenticate" }; http.collectHeaders(keys, 1); Serial.print("[HTTP] GET...\n"); diff --git a/libraries/ESP8266HTTPClient/examples/PostHttpClient/PostHttpClient.ino b/libraries/ESP8266HTTPClient/examples/PostHttpClient/PostHttpClient.ino index 936e235eeb..bb248d35ed 100644 --- a/libraries/ESP8266HTTPClient/examples/PostHttpClient/PostHttpClient.ino +++ b/libraries/ESP8266HTTPClient/examples/PostHttpClient/PostHttpClient.ino @@ -15,12 +15,12 @@ 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 #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif void setup() { @@ -40,7 +40,6 @@ void setup() { Serial.println(""); Serial.print("Connected! IP address: "); Serial.println(WiFi.localIP()); - } void loop() { @@ -52,7 +51,7 @@ void loop() { Serial.print("[HTTP] begin...\n"); // configure traged server and url - http.begin(client, "http://" SERVER_IP "/postplain/"); //HTTP + http.begin(client, "http://" SERVER_IP "/postplain/"); // HTTP http.addHeader("Content-Type", "application/json"); Serial.print("[HTTP] POST...\n"); diff --git a/libraries/ESP8266HTTPClient/examples/ReuseConnection/ReuseConnection.ino b/libraries/ESP8266HTTPClient/examples/ReuseConnection/ReuseConnection.ino deleted file mode 100644 index 2823a2dbb9..0000000000 --- a/libraries/ESP8266HTTPClient/examples/ReuseConnection/ReuseConnection.ino +++ /dev/null @@ -1,67 +0,0 @@ -/** - reuseConnection.ino - - Created on: 22.11.2015 - -*/ - - -#include - -#include -#include - -#include - -ESP8266WiFiMulti WiFiMulti; - -HTTPClient http; - -void setup() { - - Serial.begin(115200); - // Serial.setDebugOutput(true); - - 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); - } - - WiFi.mode(WIFI_STA); - WiFiMulti.addAP("SSID", "PASSWORD"); - - // allow reuse (if server supports it) - http.setReuse(true); -} - -void loop() { - // wait for WiFi connection - if ((WiFiMulti.run() == WL_CONNECTED)) { - - WiFiClient client; - - http.begin(client, "http://jigsaw.w3.org/HTTP/connection.html"); - //http.begin(client, "jigsaw.w3.org", 80, "/HTTP/connection.html"); - - int httpCode = http.GET(); - if (httpCode > 0) { - Serial.printf("[HTTP] GET... code: %d\n", httpCode); - - // file found at server - if (httpCode == HTTP_CODE_OK) { - http.writeToStream(&Serial); - } - } else { - Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); - } - - http.end(); - } - - delay(1000); -} diff --git a/libraries/ESP8266HTTPClient/examples/ReuseConnectionV2/ReuseConnectionV2.ino b/libraries/ESP8266HTTPClient/examples/ReuseConnectionV2/ReuseConnectionV2.ino index 42a89beced..24d7e3a4cf 100644 --- a/libraries/ESP8266HTTPClient/examples/ReuseConnectionV2/ReuseConnectionV2.ino +++ b/libraries/ESP8266HTTPClient/examples/ReuseConnectionV2/ReuseConnectionV2.ino @@ -13,7 +13,7 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif ESP8266WiFiMulti WiFiMulti; @@ -45,7 +45,7 @@ void setup() { http.begin(client, "http://jigsaw.w3.org/HTTP/connection.html"); - //http.begin(client, "jigsaw.w3.org", 80, "/HTTP/connection.html"); + // http.begin(client, "jigsaw.w3.org", 80, "/HTTP/connection.html"); } int pass = 0; @@ -60,15 +60,13 @@ void loop() { Serial.printf("[HTTP] GET... code: %d\n", httpCode); // file found at server - if (httpCode == HTTP_CODE_OK) { - http.writeToStream(&Serial); - } + if (httpCode == HTTP_CODE_OK) { http.writeToStream(&Serial); } } else { Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); // Something went wrong with the connection, try to reconnect http.end(); http.begin(client, "http://jigsaw.w3.org/HTTP/connection.html"); - //http.begin(client, "jigsaw.w3.org", 80, "/HTTP/connection.html"); + // http.begin(client, "jigsaw.w3.org", 80, "/HTTP/connection.html"); } if (pass == 10) { diff --git a/libraries/ESP8266HTTPClient/examples/StreamHttpClient/StreamHttpClient.ino b/libraries/ESP8266HTTPClient/examples/StreamHttpClient/StreamHttpClient.ino index 7ed0cd5294..ca9bcbc5df 100644 --- a/libraries/ESP8266HTTPClient/examples/StreamHttpClient/StreamHttpClient.ino +++ b/libraries/ESP8266HTTPClient/examples/StreamHttpClient/StreamHttpClient.ino @@ -31,7 +31,6 @@ void setup() { WiFi.mode(WIFI_STA); WiFiMulti.addAP("SSID", "PASSWORD"); - } void loop() { @@ -39,13 +38,13 @@ void loop() { if ((WiFiMulti.run() == WL_CONNECTED)) { WiFiClient client; - HTTPClient http; //must be declared after WiFiClient for correct destruction order, because used by http.begin(client,...) + HTTPClient http; // must be declared after WiFiClient for correct destruction order, because used by http.begin(client,...) Serial.print("[HTTP] begin...\n"); // configure server and url http.begin(client, "http://jigsaw.w3.org/HTTP/connection.html"); - //http.begin(client, "jigsaw.w3.org", 80, "/HTTP/connection.html"); + // http.begin(client, "jigsaw.w3.org", 80, "/HTTP/connection.html"); Serial.print("[HTTP] GET...\n"); // start connection and send HTTP header @@ -57,7 +56,7 @@ void loop() { // file found at server if (httpCode == HTTP_CODE_OK) { - // get lenght of document (is -1 when Server sends no Content-Length header) + // get length of document (is -1 when Server sends no Content-Length header) int len = http.getSize(); // create buffer for read @@ -70,23 +69,19 @@ void loop() { // or "by hand" // get tcp stream - WiFiClient * stream = &client; + WiFiClient* stream = &client; // read all data from server while (http.connected() && (len > 0 || len == -1)) { // read up to 128 byte int c = stream->readBytes(buff, std::min((size_t)len, sizeof(buff))); Serial.printf("readBytes: %d\n", c); - if (!c) { - Serial.println("read timeout"); - } + if (!c) { Serial.println("read timeout"); } // write it to Serial Serial.write(buff, c); - if (len > 0) { - len -= c; - } + if (len > 0) { len -= c; } } #endif diff --git a/libraries/ESP8266HTTPClient/examples/StreamHttpsClient/StreamHttpsClient.ino b/libraries/ESP8266HTTPClient/examples/StreamHttpsClient/StreamHttpsClient.ino index a69b9e35c6..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() { @@ -31,32 +30,33 @@ void setup() { WiFi.mode(WIFI_STA); WiFiMulti.addAP("SSID", "PASSWORD"); - } 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(); - bool mfln = client->probeMaxFragmentLength("tls.mbed.org", 443, 1024); - Serial.printf("\nConnecting to https://tls.mbed.org\n"); - Serial.printf("Maximum fragment Length negotiation supported: %s\n", mfln ? "yes" : "no"); - if (mfln) { - client->setBufferSizes(1024, 1024); - } + 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 - Serial.print("[HTTPS] begin...\n"); + // Or, if you are *absolutely* sure it is ok to ignore the SSL certificate: + // client->setInsecure(); - // 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}; + 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); } - client->setFingerprint(fingerprint); + Serial.print("[HTTPS] begin...\n"); 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 @@ -68,7 +68,7 @@ void loop() { // file found at server if (httpCode == HTTP_CODE_OK) { - // get lenght of document (is -1 when Server sends no Content-Length header) + // get length of document (is -1 when Server sends no Content-Length header) int len = https.getSize(); // create buffer for read @@ -86,16 +86,13 @@ void loop() { // write it to Serial Serial.write(buff, c); - if (len > 0) { - len -= c; - } + if (len > 0) { len -= c; } } delay(1); } Serial.println(); Serial.print("[HTTPS] connection closed or file end.\n"); - } } else { Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str()); 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 37cdae0fc1..480699e453 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp @@ -22,31 +22,34 @@ * */ #include +#include #include "ESP8266HTTPClient.h" #include -#include +#include #include -/** - * constructor - */ -HTTPClient::HTTPClient() - : _client(nullptr), _userAgent(F("ESP8266HTTPClient")) -{ -} +// per https://github.com/esp8266/Arduino/issues/8231 +// make sure HTTPClient can be utilized as a movable class member +static_assert(std::is_default_constructible_v, ""); +static_assert(!std::is_copy_constructible_v, ""); +static_assert(std::is_move_constructible_v, ""); +static_assert(std::is_move_assignable_v, ""); -/** - * destructor - */ -HTTPClient::~HTTPClient() +static const char defaultUserAgentPstr[] PROGMEM = "ESP8266HTTPClient"; +const String HTTPClient::defaultUserAgent = defaultUserAgentPstr; + +int HTTPClient::StreamReportToHttpClientReport (Stream::Report streamSendError) { - if(_client) { - _client->stop(); - } - if(_currentHeaders) { - delete[] _currentHeaders; + switch (streamSendError) + { + case Stream::Report::TimedOut: return HTTPC_ERROR_READ_TIMEOUT; + case Stream::Report::ReadError: return HTTPC_ERROR_NO_STREAM; + case Stream::Report::WriteError: return HTTPC_ERROR_STREAM_WRITE; + case Stream::Report::ShortOperation: return HTTPC_ERROR_STREAM_WRITE; + case Stream::Report::Success: return 0; } + return 0; // never reached, keep gcc quiet } void HTTPClient::clear() @@ -67,8 +70,6 @@ void HTTPClient::clear() * @return success bool */ bool HTTPClient::begin(WiFiClient &client, const String& url) { - _client = &client; - // check for : (http: or https:) int index = url.indexOf(':'); if(index < 0) { @@ -77,12 +78,15 @@ bool HTTPClient::begin(WiFiClient &client, const String& url) { } String protocol = url.substring(0, index); + protocol.toLowerCase(); if(protocol != "http" && protocol != "https") { DEBUG_HTTPCLIENT("[HTTP-Client][begin] unknown protocol '%s'\n", protocol.c_str()); return false; } _port = (protocol == "https" ? 443 : 80); + _client = client.clone(); + return beginInternal(url, protocol.c_str()); } @@ -98,9 +102,16 @@ bool HTTPClient::begin(WiFiClient &client, const String& url) { */ bool HTTPClient::begin(WiFiClient &client, const String& host, uint16_t port, const String& uri, bool https) { - _client = &client; + // Disconnect when reusing HTTPClient to talk to a different host + if (!_host.isEmpty() && _host != host) { + _canReuse = false; + disconnect(true); + } + + _client = client.clone(); + + clear(); - clear(); _host = host; _port = port; _uri = uri; @@ -124,6 +135,7 @@ bool HTTPClient::beginInternal(const String& __url, const char* expectedProtocol } _protocol = url.substring(0, index); + _protocol.toLowerCase(); url.remove(0, (index + 3)); // remove http:// or https:// if (_protocol == "http") { @@ -150,6 +162,8 @@ bool HTTPClient::beginInternal(const String& __url, const char* expectedProtocol _base64Authorization = base64::encode(auth, false /* doNewLines */); } + const String oldHost = _host; + // get port index = host.indexOf(':'); if(index >= 0) { @@ -159,6 +173,13 @@ bool HTTPClient::beginInternal(const String& __url, const char* expectedProtocol } else { _host = host; } + + // Disconnect when reusing HTTPClient to talk to a different host + if (!oldHost.isEmpty() && _host != oldHost) { + _canReuse = false; + disconnect(true); + } + _uri = url; if ( expectedProtocol != nullptr && _protocol != expectedProtocol) { @@ -261,17 +282,26 @@ void HTTPClient::setAuthorization(const char * user, const char * password) } /** - * set the Authorizatio for the http request + * set the Authorization for the http request * @param auth const char * base64 */ void HTTPClient::setAuthorization(const char * auth) { - if(auth) { - _base64Authorization = auth; - _base64Authorization.replace(String('\n'), emptyString); + if (auth) { + setAuthorization(String(auth)); } } +/** + * set the Authorization for the http request + * @param auth String base64 + */ +void HTTPClient::setAuthorization(String auth) +{ + _base64Authorization = std::move(auth); + _base64Authorization.replace(String('\n'), emptyString); +} + /** * set the timeout for the TCP connection * @param timeout unsigned int @@ -308,7 +338,7 @@ bool HTTPClient::setURL(const String& url) } /** - * set redirect follow mode. See `followRedirects_t` enum for avaliable modes. + * set redirect follow mode. See `followRedirects_t` enum for available modes. * @param follow */ void HTTPClient::setFollowRedirects(followRedirects_t follow) @@ -339,6 +369,14 @@ int HTTPClient::GET() { return sendRequest("GET"); } +/** + * send a DELETE request + * @return http code + */ +int HTTPClient::DELETE() +{ + return sendRequest("DELETE"); +} /** * sends a post request to the server @@ -429,24 +467,9 @@ int HTTPClient::sendRequest(const char * type, const uint8_t * payload, size_t s return returnError(HTTPC_ERROR_SEND_HEADER_FAILED); } - // send Payload if needed - if (payload && size > 0) { - size_t bytesWritten = 0; - const uint8_t *p = payload; - size_t originalSize = size; - while (bytesWritten < originalSize) { - int written; - int towrite = std::min((int)size, (int)HTTP_TCP_BUFFER_SIZE); - written = _client->write(p + bytesWritten, towrite); - if (written < 0) { - return returnError(HTTPC_ERROR_SEND_PAYLOAD_FAILED); - } else if (written == 0) { - return returnError(HTTPC_ERROR_CONNECTION_LOST); - } - bytesWritten += written; - size -= written; - } - } + // transfer all of it, with send-timeout + if (size && StreamConstPtr(payload, size).sendAll(_client.get()) != size) + return returnError(HTTPC_ERROR_SEND_PAYLOAD_FAILED); // handle Server Response (Header) code = handleHeaderResponse(); @@ -460,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 ) { @@ -473,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; @@ -483,7 +506,7 @@ int HTTPClient::sendRequest(const char * type, const uint8_t * payload, size_t s // no redirection break; } - // redirect using the same request method and payload, diffrent URL + // redirect using the same request method and payload, different URL redirect = true; } break; @@ -545,111 +568,13 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size) return returnError(HTTPC_ERROR_SEND_HEADER_FAILED); } - int buff_size = HTTP_TCP_BUFFER_SIZE; - - int len = size; - int bytesWritten = 0; - - if(len == 0) { - len = -1; - } - - // if possible create smaller buffer then HTTP_TCP_BUFFER_SIZE - if((len > 0) && (len < HTTP_TCP_BUFFER_SIZE)) { - buff_size = len; - } - - // create buffer for read - uint8_t * buff = (uint8_t *) malloc(buff_size); - - if(buff) { - // read all data from stream and send it to server - while(connected() && (stream->available() > 0) && (len > 0 || len == -1)) { - - // get available data size - int sizeAvailable = stream->available(); - - if(sizeAvailable) { - - int readBytes = sizeAvailable; - - // read only the asked bytes - if(len > 0 && readBytes > len) { - readBytes = len; - } - - // not read more the buffer can handle - if(readBytes > buff_size) { - readBytes = buff_size; - } - - // read data - int bytesRead = stream->readBytes(buff, readBytes); - - // write it to Stream - int bytesWrite = _client->write((const uint8_t *) buff, bytesRead); - bytesWritten += bytesWrite; - - // are all Bytes a writen to stream ? - if(bytesWrite != bytesRead) { - DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] short write, asked for %d but got %d retry...\n", bytesRead, bytesWrite); - - // check for write error - if(_client->getWriteError()) { - DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] stream write error %d\n", _client->getWriteError()); - - //reset write error for retry - _client->clearWriteError(); - } - - // some time for the stream - delay(1); - - int leftBytes = (readBytes - bytesWrite); - - // retry to send the missed bytes - bytesWrite = _client->write((const uint8_t *) (buff + bytesWrite), leftBytes); - bytesWritten += bytesWrite; - - if(bytesWrite != leftBytes) { - // failed again - DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] short write, asked for %d but got %d failed.\n", leftBytes, bytesWrite); - free(buff); - return returnError(HTTPC_ERROR_SEND_PAYLOAD_FAILED); - } - } - - // check for write error - if(_client->getWriteError()) { - DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] stream write error %d\n", _client->getWriteError()); - free(buff); - return returnError(HTTPC_ERROR_SEND_PAYLOAD_FAILED); - } - - // count bytes to read left - if(len > 0) { - len -= readBytes; - } - - delay(0); - } else { - delay(1); - } - } - - free(buff); - - if(size && (int) size != bytesWritten) { - DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] Stream payload bytesWritten %d and size %zd mismatch!.\n", bytesWritten, size); - DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] ERROR SEND PAYLOAD FAILED!"); - return returnError(HTTPC_ERROR_SEND_PAYLOAD_FAILED); - } else { - DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] Stream payload written: %d\n", bytesWritten); - } - - } else { - DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] not enough ram! need %d\n", HTTP_TCP_BUFFER_SIZE); - return returnError(HTTPC_ERROR_TOO_LESS_RAM); + // transfer all of it, with timeout + size_t transferred = stream->sendSize(_client.get(), size); + if (transferred != size) + { + DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] short write, asked for %zu but got %zu failed.\n", size, transferred); + esp_yield(); + return returnError(HTTPC_ERROR_SEND_PAYLOAD_FAILED); } // handle Server Response (Header) @@ -695,107 +620,18 @@ WiFiClient& HTTPClient::getStream(void) WiFiClient* HTTPClient::getStreamPtr(void) { if(connected()) { - return _client; + return _client.get(); } DEBUG_HTTPCLIENT("[HTTP-Client] getStreamPtr: not connected\n"); 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) -{ - - if(!stream) { - 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) { - if(len > 0 || len == -1) { - ret = writeToStreamDataBlock(stream, len); - - // have we an error? - if(ret < 0) { - return returnError(ret); - } - } - } 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) { - int r = writeToStreamDataBlock(stream, len); - if(r < 0) { - // error in writeToStreamDataBlock - return returnError(r); - } - 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); - } - - delay(0); - } - } 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; @@ -803,12 +639,12 @@ const String& HTTPClient::getString(void) _payload.reset(new StreamString()); - if(_size > 0) { - // try to reserve needed memmory - 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()); @@ -890,39 +726,36 @@ void HTTPClient::addHeader(const String& name, const String& value, bool first, void HTTPClient::collectHeaders(const char* headerKeys[], const size_t headerKeysCount) { _headerKeysCount = headerKeysCount; - if(_currentHeaders) { - delete[] _currentHeaders; - } - _currentHeaders = new RequestArgument[_headerKeysCount]; + _currentHeaders = std::make_unique(_headerKeysCount); for(size_t i = 0; i < _headerKeysCount; i++) { _currentHeaders[i].key = headerKeys[i]; } } -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() @@ -948,9 +781,11 @@ bool HTTPClient::connect(void) { if(_reuse && _canReuse && connected()) { DEBUG_HTTPCLIENT("[HTTP-Client] connect: already connected, reusing connection\n"); - while(_client->available() > 0) { - _client->read(); - } + +#if defined(NO_GLOBAL_INSTANCES) || defined(NO_GLOBAL_STREAMDEV) + StreamNull devnull; +#endif + _client->sendAvailable(devnull); // clear _client's output (all of it, no timeout) return true; } @@ -1011,8 +846,10 @@ bool HTTPClient::sendHeader(const char * type) header += ':'; header += String(_port); } - header += F("\r\nUser-Agent: "); - header += _userAgent; + if (_userAgent.length()) { + header += F("\r\nUser-Agent: "); + header += _userAgent; + } if (!_useHTTP10) { header += F("\r\nAccept-Encoding: identity;q=1,chunked;q=0.1,*;q=0"); @@ -1032,7 +869,8 @@ bool HTTPClient::sendHeader(const char * type) DEBUG_HTTPCLIENT("[HTTP-Client] sending request header\n-----\n%s-----\n", header.c_str()); - return (_client->write((const uint8_t *) header.c_str(), header.length()) == header.length()); + // transfer all of it, with timeout + return StreamConstPtr(header).sendAll(_client.get()) == header.length(); } /** @@ -1125,141 +963,31 @@ int HTTPClient::handleHeaderResponse() if(transferEncoding.equalsIgnoreCase(F("chunked"))) { _transferEncoding = HTTPC_TE_CHUNKED; } else { - return HTTPC_ERROR_ENCODING; + _returnCode = HTTPC_ERROR_ENCODING; + return _returnCode; } } else { _transferEncoding = HTTPC_TE_IDENTITY; } - if(_returnCode) { - return _returnCode; - } else { + if(_returnCode <= 0) { DEBUG_HTTPCLIENT("[HTTP-Client][handleHeaderResponse] Remote host is not an HTTP Server!"); - return HTTPC_ERROR_NO_HTTP_SERVER; + _returnCode = HTTPC_ERROR_NO_HTTP_SERVER; } + return _returnCode; } } else { if((millis() - lastDataTime) > _tcpTimeout) { return HTTPC_ERROR_READ_TIMEOUT; } - delay(0); + esp_yield(); } } return HTTPC_ERROR_CONNECTION_LOST; } -/** - * write one Data Block to Stream - * @param stream Stream * - * @param size int - * @return < 0 = error >= 0 = size written - */ -int HTTPClient::writeToStreamDataBlock(Stream * stream, int size) -{ - int buff_size = HTTP_TCP_BUFFER_SIZE; - int len = size; // left size to read - int bytesWritten = 0; - - // if possible create smaller buffer then HTTP_TCP_BUFFER_SIZE - if((len > 0) && (len < HTTP_TCP_BUFFER_SIZE)) { - buff_size = len; - } - - // create buffer for read - uint8_t * buff = (uint8_t *) malloc(buff_size); - - if(!buff) { - DEBUG_HTTPCLIENT("[HTTP-Client][writeToStreamDataBlock] not enough ram! need %d\n", HTTP_TCP_BUFFER_SIZE); - return HTTPC_ERROR_TOO_LESS_RAM; - } - - // read all data from server - while(connected() && (len > 0 || len == -1)) - { - int readBytes = len; - - // not read more the buffer can handle - if(readBytes > buff_size) { - readBytes = buff_size; - } - - // len == -1 or len > what is available, read only what is available - int av = _client->available(); - if (readBytes < 0 || readBytes > av) { - readBytes = av; - } - - // read data - int bytesRead = _client->readBytes(buff, readBytes); - if (!bytesRead) - { - DEBUG_HTTPCLIENT("[HTTP-Client][writeToStreamDataBlock] input stream timeout\n"); - free(buff); - return HTTPC_ERROR_READ_TIMEOUT; - } - - // write it to Stream - int bytesWrite = stream->write(buff, bytesRead); - bytesWritten += bytesWrite; - - // are all Bytes a writen to stream ? - if(bytesWrite != bytesRead) { - DEBUG_HTTPCLIENT("[HTTP-Client][writeToStream] short write asked for %d but got %d retry...\n", bytesRead, bytesWrite); - - // check for write error - if(stream->getWriteError()) { - DEBUG_HTTPCLIENT("[HTTP-Client][writeToStreamDataBlock] stream write error %d\n", stream->getWriteError()); - - //reset write error for retry - stream->clearWriteError(); - } - - // some time for the stream - delay(1); - - int leftBytes = (bytesRead - bytesWrite); - - // retry to send the missed bytes - bytesWrite = stream->write((buff + bytesWrite), leftBytes); - bytesWritten += bytesWrite; - - if(bytesWrite != leftBytes) { - // failed again - DEBUG_HTTPCLIENT("[HTTP-Client][writeToStream] short write asked for %d but got %d failed.\n", leftBytes, bytesWrite); - free(buff); - return HTTPC_ERROR_STREAM_WRITE; - } - } - - // check for write error - if(stream->getWriteError()) { - DEBUG_HTTPCLIENT("[HTTP-Client][writeToStreamDataBlock] stream write error %d\n", stream->getWriteError()); - free(buff); - return HTTPC_ERROR_STREAM_WRITE; - } - - // count bytes to read left - if(len > 0) { - len -= bytesRead; - } - - delay(0); - } - - free(buff); - - DEBUG_HTTPCLIENT("[HTTP-Client][writeToStreamDataBlock] end of chunk or data (transferred: %d).\n", bytesWritten); - - if((size > 0) && (size != bytesWritten)) { - DEBUG_HTTPCLIENT("[HTTP-Client][writeToStreamDataBlock] transferred size %d and request size %d mismatch!.\n", bytesWritten, size); - return HTTPC_ERROR_STREAM_WRITE; - } - - return bytesWritten; -} - /** * called to handle error return, may disconnect the connection if still exists * @param error diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h index 9699b92b53..bc8a42d33e 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h @@ -26,11 +26,12 @@ #ifndef ESP8266HTTPClient_H_ #define ESP8266HTTPClient_H_ -#include #include - +#include #include +#include + #ifdef DEBUG_ESP_HTTP_CLIENT #ifdef DEBUG_ESP_PORT #define DEBUG_HTTPCLIENT(fmt, ...) DEBUG_ESP_PORT.printf_P( (PGM_P)PSTR(fmt), ## __VA_ARGS__ ) @@ -148,22 +149,19 @@ typedef enum { class TransportTraits; typedef std::unique_ptr TransportTraitsPtr; -class StreamString; - class HTTPClient { public: - HTTPClient(); - ~HTTPClient(); + HTTPClient() = default; + ~HTTPClient() = default; + HTTPClient(HTTPClient&&) = default; + HTTPClient& operator=(HTTPClient&&) = default; -/* - * Since both begin() functions take a reference to client as a parameter, you need to - * ensure the client object lives the entire time of the HTTPClient - */ + // Note that WiFiClient's underlying connection *will* be captured bool begin(WiFiClient &client, const String& url); bool begin(WiFiClient &client, const String& host, uint16_t port, const String& uri = "/", bool https = false); - // old API is now explicitely forbidden + // old API is now explicitly forbidden bool begin(String url) __attribute__ ((error("obsolete API, use ::begin(WiFiClient, url)"))); bool begin(String host, uint16_t port, String uri = "/") __attribute__ ((error("obsolete API, use ::begin(WiFiClient, host, port, uri)"))); bool begin(String url, const uint8_t httpsFingerprint[20]) __attribute__ ((error("obsolete API, use ::begin(WiFiClientSecure, ...)"))); @@ -178,6 +176,7 @@ class HTTPClient void setUserAgent(const String& userAgent); void setAuthorization(const char * user, const char * password); void setAuthorization(const char * auth); + void setAuthorization(String auth); void setTimeout(uint16_t timeout); // Redirections @@ -189,6 +188,7 @@ class HTTPClient /// request handling int GET(); + int DELETE(); int POST(const uint8_t* payload, size_t size); int POST(const String& payload); int PUT(const uint8_t* payload, size_t size); @@ -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,8 +215,15 @@ class HTTPClient WiFiClient& getStream(void); WiFiClient* getStreamPtr(void); - 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: @@ -233,8 +240,17 @@ 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 + // { + // WiFiClient socket; + // HTTPClient http; + // http.begin(socket, "http://blahblah"); + // } + // Make sure it's not possible to break things in an opposite direction - WiFiClient* _client; + std::unique_ptr _client; /// request handling String _host; @@ -246,12 +262,14 @@ class HTTPClient String _uri; String _protocol; String _headers; - String _userAgent; String _base64Authorization; + static const String defaultUserAgent; + String _userAgent = defaultUserAgent; + /// Response handling - RequestArgument* _currentHeaders = nullptr; - size_t _headerKeysCount = 0; + std::unique_ptr _currentHeaders; + size_t _headerKeysCount = 0; int _returnCode = 0; int _size = -1; @@ -263,6 +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/examples/SecureBearSSLUpdater/SecureBearSSLUpdater.ino b/libraries/ESP8266HTTPUpdateServer/examples/SecureBearSSLUpdater/SecureBearSSLUpdater.ino index a2514e171d..f8829120ef 100644 --- a/libraries/ESP8266HTTPUpdateServer/examples/SecureBearSSLUpdater/SecureBearSSLUpdater.ino +++ b/libraries/ESP8266HTTPUpdateServer/examples/SecureBearSSLUpdater/SecureBearSSLUpdater.ino @@ -21,7 +21,7 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif const char* host = "esp8266-webupdate"; @@ -57,7 +57,7 @@ JfUvYadSYxh3nblvA4OL+iEZiW8NE3hbW6WPXxvS7Euge0uWMPc4uEcnsE0ZVG3m -----END CERTIFICATE----- )EOF"; -static const char serverKey[] PROGMEM = R"EOF( +static const char serverKey[] PROGMEM = R"EOF( -----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEA9UoHBtn4oNKXjRgIOQ/rLxK/iI0a8Q5mDxhfuwa9//FkftSI IFY8UhGk2YNJpnfKOyYWqbqwuJhIZJ2sEIWp2301OnavuGBrpKOgBJJljgH2l/4Z @@ -88,8 +88,7 @@ gz5JWYhbD6c38khSzJb0pNXCo3EuYAVa36kDM96k1BtWuhRS10Q1VXk= )EOF"; -void setup() -{ +void setup() { Serial.begin(115200); Serial.println(); @@ -97,7 +96,7 @@ void setup() WiFi.mode(WIFI_AP_STA); WiFi.begin(ssid, password); - while(WiFi.waitForConnectResult() != WL_CONNECTED){ + while (WiFi.waitForConnectResult() != WL_CONNECTED) { WiFi.begin(ssid, password); Serial.println("WiFi failed, retrying."); } @@ -111,13 +110,13 @@ void setup() httpServer.begin(); MDNS.addService("https", "tcp", 443); - Serial.printf("BearSSLUpdateServer ready!\nOpen https://%s.local%s in "\ - "your browser and login with username '%s' and password "\ - "'%s'\n", host, update_path, update_username, update_password); + Serial.printf("BearSSLUpdateServer ready!\nOpen https://%s.local%s in " + "your browser and login with username '%s' and password " + "'%s'\n", + host, update_path, update_username, update_password); } -void loop() -{ +void loop() { httpServer.handleClient(); MDNS.update(); } diff --git a/libraries/ESP8266HTTPUpdateServer/examples/SecureWebUpdater/SecureWebUpdater.ino b/libraries/ESP8266HTTPUpdateServer/examples/SecureWebUpdater/SecureWebUpdater.ino index 11e23a929d..d37ef3f1f6 100644 --- a/libraries/ESP8266HTTPUpdateServer/examples/SecureWebUpdater/SecureWebUpdater.ino +++ b/libraries/ESP8266HTTPUpdateServer/examples/SecureWebUpdater/SecureWebUpdater.ino @@ -10,7 +10,7 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif const char* host = "esp8266-webupdate"; diff --git a/libraries/ESP8266HTTPUpdateServer/examples/WebUpdater/WebUpdater.ino b/libraries/ESP8266HTTPUpdateServer/examples/WebUpdater/WebUpdater.ino index fea3c4023a..89b5f62a90 100644 --- a/libraries/ESP8266HTTPUpdateServer/examples/WebUpdater/WebUpdater.ino +++ b/libraries/ESP8266HTTPUpdateServer/examples/WebUpdater/WebUpdater.ino @@ -10,7 +10,7 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif const char* host = "esp8266-webupdate"; diff --git a/libraries/ESP8266HTTPUpdateServer/src/ESP8266HTTPUpdateServer-impl.h b/libraries/ESP8266HTTPUpdateServer/src/ESP8266HTTPUpdateServer-impl.h index 966a73eefc..570ecfbb13 100644 --- a/libraries/ESP8266HTTPUpdateServer/src/ESP8266HTTPUpdateServer-impl.h +++ b/libraries/ESP8266HTTPUpdateServer/src/ESP8266HTTPUpdateServer-impl.h @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -58,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()) { @@ -72,7 +89,7 @@ void ESP8266HTTPUpdateServerTemplate::setup(ESP8266WebServerTemplate ESP.restart(); } },[&](){ - // handler for the file upload, get's the sketch bytes, and writes + // handler for the file upload, gets the sketch bytes, and writes // them through the Update object HTTPUpload& upload = _server->upload(); @@ -88,11 +105,10 @@ void ESP8266HTTPUpdateServerTemplate::setup(ESP8266WebServerTemplate return; } - WiFiUDP::stopAll(); if (_serial_output) Serial.printf("Update: %s\n", upload.filename.c_str()); if (upload.name == "filesystem") { - size_t fsSize = ((size_t) &_FS_end - (size_t) &_FS_start); + size_t fsSize = ((size_t)FS_end - (size_t)FS_start); close_all_fs(); if (!Update.begin(fsSize, U_FS)){//start with max available size if (_serial_output) Update.printError(Serial); @@ -110,7 +126,7 @@ void ESP8266HTTPUpdateServerTemplate::setup(ESP8266WebServerTemplate } } else if(_authenticated && upload.status == UPLOAD_FILE_END && !_updaterError.length()){ if(Update.end(true)){ //true to set the size to the current progress - if (_serial_output) Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize); + if (_serial_output) Serial.printf("Update Success: %zu\nRebooting...\n", upload.totalSize); } else { _setUpdaterError(); } @@ -119,7 +135,7 @@ void ESP8266HTTPUpdateServerTemplate::setup(ESP8266WebServerTemplate Update.end(); if (_serial_output) Serial.println("Update was aborted"); } - delay(0); + esp_yield(); }); } diff --git a/libraries/ESP8266LLMNR/ESP8266LLMNR.cpp b/libraries/ESP8266LLMNR/ESP8266LLMNR.cpp index ac8582c579..d5d61ca89c 100644 --- a/libraries/ESP8266LLMNR/ESP8266LLMNR.cpp +++ b/libraries/ESP8266LLMNR/ESP8266LLMNR.cpp @@ -7,7 +7,7 @@ * Version 1.1 * Copyright (c) 2013 Tony DiCola (tony@tonydicola.com) * ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com) - * MDNS-SD Suport 2015 Hristo Gochkov + * MDNS-SD Support 2015 Hristo Gochkov * Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com) * * License (MIT license): diff --git a/libraries/ESP8266LLMNR/ESP8266LLMNR.h b/libraries/ESP8266LLMNR/ESP8266LLMNR.h index 4ad1d4a74d..76c96ab87b 100644 --- a/libraries/ESP8266LLMNR/ESP8266LLMNR.h +++ b/libraries/ESP8266LLMNR/ESP8266LLMNR.h @@ -7,7 +7,7 @@ * Version 1.1 * Copyright (c) 2013 Tony DiCola (tony@tonydicola.com) * ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com) - * MDNS-SD Suport 2015 Hristo Gochkov + * MDNS-SD Support 2015 Hristo Gochkov * Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com) * * License (MIT license): diff --git a/libraries/ESP8266LLMNR/README.md b/libraries/ESP8266LLMNR/README.md index 2ecdf973ef..e24d287462 100644 --- a/libraries/ESP8266LLMNR/README.md +++ b/libraries/ESP8266LLMNR/README.md @@ -25,7 +25,7 @@ Usage file. 2. Include the ESP8266LLMNR library in the sketch. 3. Call the LLMNR.begin() method in the sketch's setup() function, and provide - the hostname to advertize. This should not include any ".local" prefix. + the hostname to advertise. This should not include any ".local" prefix. 4. If ESP8266 AP mode is enabled, disabled, or the WiFi or AP configuration is changed, call LLMNR.notify_ap_change() after the change is made. @@ -70,7 +70,7 @@ ESP8266 Multicast DNS (port of CC3000 Multicast DNS library) Version 1.1 Copyright (c) 2013 Tony DiCola (tony@tonydicola.com) ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com) -MDNS-SD Suport 2015 Hristo Gochkov +MDNS-SD Support 2015 Hristo Gochkov Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com) Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/libraries/ESP8266LLMNR/examples/LLMNR_Web_Server/LLMNR_Web_Server.ino b/libraries/ESP8266LLMNR/examples/LLMNR_Web_Server/LLMNR_Web_Server.ino index b1f4dca186..f36da85aeb 100644 --- a/libraries/ESP8266LLMNR/examples/LLMNR_Web_Server/LLMNR_Web_Server.ino +++ b/libraries/ESP8266LLMNR/examples/LLMNR_Web_Server/LLMNR_Web_Server.ino @@ -7,7 +7,7 @@ Version 1.1 Copyright (c) 2013 Tony DiCola (tony@tonydicola.com) ESP8266 port (c) 2015 Ivan Grokhotkov (ivan@esp8266.com) - MDNS-SD Suport 2015 Hristo Gochkov + MDNS-SD Support 2015 Hristo Gochkov Extended MDNS-SD support 2016 Lars Englund (lars.englund@gmail.com) License (MIT license): @@ -62,7 +62,7 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif const char* ssid = STASSID; 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/ESP8266NetBIOS/examples/ESP_NBNST/ESP_NBNST.ino b/libraries/ESP8266NetBIOS/examples/ESP_NBNST/ESP_NBNST.ino old mode 100755 new mode 100644 index 57f5529850..31d9327883 --- a/libraries/ESP8266NetBIOS/examples/ESP_NBNST/ESP_NBNST.ino +++ b/libraries/ESP8266NetBIOS/examples/ESP_NBNST/ESP_NBNST.ino @@ -4,7 +4,7 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif const char* ssid = STASSID; diff --git a/libraries/ESP8266SSDP/examples/SSDP/SSDP.ino b/libraries/ESP8266SSDP/examples/SSDP/SSDP.ino index 28b0ca977b..c55afacf72 100644 --- a/libraries/ESP8266SSDP/examples/SSDP/SSDP.ino +++ b/libraries/ESP8266SSDP/examples/SSDP/SSDP.ino @@ -4,7 +4,7 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif const char* ssid = STASSID; @@ -46,9 +46,7 @@ void setup() { Serial.printf("Ready!\n"); } else { Serial.printf("WiFi Failed\n"); - while (1) { - delay(100); - } + while (1) { delay(100); } } } diff --git a/libraries/ESP8266SdFat b/libraries/ESP8266SdFat index 0a46e4ebb2..eaab1369d5 160000 --- a/libraries/ESP8266SdFat +++ b/libraries/ESP8266SdFat @@ -1 +1 @@ -Subproject commit 0a46e4ebb2de585c5a64f981dbc2b2223a438984 +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 2fc8508ca0..b96f977373 100644 --- a/libraries/ESP8266WebServer/examples/AdvancedWebServer/AdvancedWebServer.ino +++ b/libraries/ESP8266WebServer/examples/AdvancedWebServer/AdvancedWebServer.ino @@ -32,10 +32,11 @@ #include #include #include +#include #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif const char *ssid = STASSID; @@ -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,10 +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); } @@ -86,9 +85,7 @@ void handleNotFound() { message += server.args(); message += "\n"; - for (uint8_t i = 0; i < server.args(); i++) { - message += " " + server.argName(i) + ": " + server.arg(i) + "\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); @@ -133,9 +130,7 @@ void setup(void) { Serial.print("IP address: "); Serial.println(WiFi.localIP()); - if (MDNS.begin("esp8266")) { - Serial.println("MDNS responder started"); - } + if (MDNS.begin("esp8266")) { Serial.println("MDNS responder started"); } server.on("/", handleRoot); server.on("/test.svg", drawGraph); @@ -151,4 +146,3 @@ void loop(void) { server.handleClient(); MDNS.update(); } - diff --git a/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino b/libraries/ESP8266WebServer/examples/FSBrowser/FSBrowser.ino index f6aff52c7d..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 //////////////////////////////// @@ -72,7 +72,7 @@ SDFSConfig fileSystemConfig = SDFSConfig(); #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif const char* ssid = STASSID; @@ -122,15 +122,9 @@ void replyServerError(String msg) { */ String checkForUnsupportedPath(String filename) { String error = String(); - if (!filename.startsWith("/")) { - error += F("!NO_LEADING_SLASH! "); - } - if (filename.indexOf("//") != -1) { - error += F("!DOUBLE_SLASH! "); - } - if (filename.endsWith("/")) { - error += F("!TRAILING_SLASH! "); - } + if (!filename.startsWith("/")) { error += F("!NO_LEADING_SLASH! "); } + if (filename.indexOf("//") != -1) { error += F("!DOUBLE_SLASH! "); } + if (filename.endsWith("/")) { error += F("!TRAILING_SLASH! "); } return error; } #endif @@ -171,21 +165,15 @@ void handleStatus() { /* Return the list of files in the directory specified by the "dir" query string parameter. - Also demonstrates the use of chuncked responses. + Also demonstrates the use of chunked responses. */ void handleFileList() { - if (!fsOK) { - return replyServerError(FPSTR(FS_INIT_ERROR)); - } + if (!fsOK) { return replyServerError(FPSTR(FS_INIT_ERROR)); } - if (!server.hasArg("dir")) { - return replyBadRequest(F("DIR ARG MISSING")); - } + if (!server.hasArg("dir")) { return replyBadRequest(F("DIR ARG MISSING")); } String path = server.arg("dir"); - if (path != "/" && !fileSystem->exists(path)) { - return replyBadRequest("BAD PATH"); - } + if (path != "/" && !fileSystem->exists(path)) { return replyBadRequest("BAD PATH"); } DBG_OUTPUT_PORT.println(String("handleFileList: ") + path); Dir dir = fileSystem->openDir(path); @@ -253,9 +241,7 @@ bool handleFileRead(String path) { return true; } - if (path.endsWith("/")) { - path += "index.htm"; - } + if (path.endsWith("/")) { path += "index.htm"; } String contentType; if (server.hasArg("download")) { @@ -270,9 +256,7 @@ bool handleFileRead(String path) { } if (fileSystem->exists(path)) { File file = fileSystem->open(path, "r"); - if (server.streamFile(file, contentType) != file.size()) { - DBG_OUTPUT_PORT.println("Sent less data than expected!"); - } + if (server.streamFile(file, contentType) != file.size()) { DBG_OUTPUT_PORT.println("Sent less data than expected!"); } file.close(); return true; } @@ -309,27 +293,17 @@ String lastExistingParent(String path) { Move folder | parent of source folder, or remaining ancestor */ void handleFileCreate() { - if (!fsOK) { - return replyServerError(FPSTR(FS_INIT_ERROR)); - } + if (!fsOK) { return replyServerError(FPSTR(FS_INIT_ERROR)); } String path = server.arg("path"); - if (path.isEmpty()) { - return replyBadRequest(F("PATH ARG MISSING")); - } + if (path.isEmpty()) { return replyBadRequest(F("PATH ARG MISSING")); } #ifdef USE_SPIFFS - if (checkForUnsupportedPath(path).length() > 0) { - return replyServerError(F("INVALID FILENAME")); - } + if (checkForUnsupportedPath(path).length() > 0) { return replyServerError(F("INVALID FILENAME")); } #endif - if (path == "/") { - return replyBadRequest("BAD PATH"); - } - if (fileSystem->exists(path)) { - return replyBadRequest(F("PATH FILE EXISTS")); - } + if (path == "/") { return replyBadRequest("BAD PATH"); } + if (fileSystem->exists(path)) { return replyBadRequest(F("PATH FILE EXISTS")); } String src = server.arg("src"); if (src.isEmpty()) { @@ -338,43 +312,29 @@ void handleFileCreate() { if (path.endsWith("/")) { // Create a folder path.remove(path.length() - 1); - if (!fileSystem->mkdir(path)) { - return replyServerError(F("MKDIR FAILED")); - } + if (!fileSystem->mkdir(path)) { return replyServerError(F("MKDIR FAILED")); } } else { // Create a file File file = fileSystem->open(path, "w"); if (file) { - file.write((const char *)0); + file.write((const char*)0); file.close(); } else { return replyServerError(F("CREATE FAILED")); } } - if (path.lastIndexOf('/') > -1) { - path = path.substring(0, path.lastIndexOf('/')); - } + if (path.lastIndexOf('/') > -1) { path = path.substring(0, path.lastIndexOf('/')); } replyOKWithMsg(path); } else { // Source specified: rename - if (src == "/") { - return replyBadRequest("BAD SRC"); - } - if (!fileSystem->exists(src)) { - return replyBadRequest(F("SRC FILE NOT FOUND")); - } + if (src == "/") { return replyBadRequest("BAD SRC"); } + if (!fileSystem->exists(src)) { return replyBadRequest(F("SRC FILE NOT FOUND")); } DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path + " from " + src); - if (path.endsWith("/")) { - path.remove(path.length() - 1); - } - if (src.endsWith("/")) { - src.remove(src.length() - 1); - } - if (!fileSystem->rename(src, path)) { - return replyServerError(F("RENAME FAILED")); - } + if (path.endsWith("/")) { path.remove(path.length() - 1); } + if (src.endsWith("/")) { src.remove(src.length() - 1); } + if (!fileSystem->rename(src, path)) { return replyServerError(F("RENAME FAILED")); } replyOKWithMsg(lastExistingParent(src)); } } @@ -403,9 +363,7 @@ void deleteRecursive(String path) { // Otherwise delete its contents first Dir dir = fileSystem->openDir(path); - while (dir.next()) { - deleteRecursive(path + '/' + dir.fileName()); - } + while (dir.next()) { deleteRecursive(path + '/' + dir.fileName()); } // Then delete the folder itself fileSystem->rmdir(path); @@ -420,19 +378,13 @@ void deleteRecursive(String path) { Delete folder | parent of deleted folder, or remaining ancestor */ void handleFileDelete() { - if (!fsOK) { - return replyServerError(FPSTR(FS_INIT_ERROR)); - } + if (!fsOK) { return replyServerError(FPSTR(FS_INIT_ERROR)); } String path = server.arg(0); - if (path.isEmpty() || path == "/") { - return replyBadRequest("BAD PATH"); - } + if (path.isEmpty() || path == "/") { return replyBadRequest("BAD PATH"); } DBG_OUTPUT_PORT.println(String("handleFileDelete: ") + path); - if (!fileSystem->exists(path)) { - return replyNotFound(FPSTR(FILE_NOT_FOUND)); - } + if (!fileSystem->exists(path)) { return replyNotFound(FPSTR(FILE_NOT_FOUND)); } deleteRecursive(path); replyOKWithMsg(lastExistingParent(path)); @@ -442,57 +394,41 @@ void handleFileDelete() { Handle a file upload request */ void handleFileUpload() { - if (!fsOK) { - return replyServerError(FPSTR(FS_INIT_ERROR)); - } - if (server.uri() != "/edit") { - return; - } + if (!fsOK) { return replyServerError(FPSTR(FS_INIT_ERROR)); } + if (server.uri() != "/edit") { return; } HTTPUpload& upload = server.upload(); if (upload.status == UPLOAD_FILE_START) { String filename = upload.filename; // Make sure paths always start with "/" - if (!filename.startsWith("/")) { - filename = "/" + filename; - } + if (!filename.startsWith("/")) { filename = "/" + filename; } DBG_OUTPUT_PORT.println(String("handleFileUpload Name: ") + filename); uploadFile = fileSystem->open(filename, "w"); - if (!uploadFile) { - return replyServerError(F("CREATE FAILED")); - } + if (!uploadFile) { return replyServerError(F("CREATE FAILED")); } DBG_OUTPUT_PORT.println(String("Upload: START, filename: ") + filename); } else if (upload.status == UPLOAD_FILE_WRITE) { if (uploadFile) { size_t bytesWritten = uploadFile.write(upload.buf, upload.currentSize); - if (bytesWritten != upload.currentSize) { - return replyServerError(F("WRITE FAILED")); - } + if (bytesWritten != upload.currentSize) { return replyServerError(F("WRITE FAILED")); } } DBG_OUTPUT_PORT.println(String("Upload: WRITE, Bytes: ") + upload.currentSize); } else if (upload.status == UPLOAD_FILE_END) { - if (uploadFile) { - uploadFile.close(); - } + if (uploadFile) { uploadFile.close(); } DBG_OUTPUT_PORT.println(String("Upload: END, Size: ") + upload.totalSize); } } /* - The "Not Found" handler catches all URI not explicitely declared in code + The "Not Found" handler catches all URI not explicitly declared in code First try to find and return the requested file from the filesystem, and if it fails, return a 404 page with debug information */ void handleNotFound() { - if (!fsOK) { - return replyServerError(FPSTR(FS_INIT_ERROR)); - } + if (!fsOK) { return replyServerError(FPSTR(FS_INIT_ERROR)); } - String uri = ESP8266WebServer::urlDecode(server.uri()); // required to read paths with blanks + String uri = ESP8266WebServer::urlDecode(server.uri()); // required to read paths with blanks - if (handleFileRead(uri)) { - return; - } + if (handleFileRead(uri)) { return; } // Dump debug data String message; @@ -526,9 +462,7 @@ void handleNotFound() { Otherwise, fails with a 404 page with debug information */ void handleGetEdit() { - if (handleFileRead(F("/edit/index.htm"))) { - return; - } + if (handleFileRead(F("/edit/index.htm"))) { return; } #ifdef INCLUDE_FALLBACK_INDEX_HTM server.sendHeader(F("Content-Encoding"), "gzip"); @@ -536,7 +470,6 @@ void handleGetEdit() { #else replyNotFound(FPSTR(FILE_NOT_FOUND)); #endif - } void setup(void) { @@ -555,16 +488,14 @@ void setup(void) { DBG_OUTPUT_PORT.println(fsOK ? F("Filesystem initialized.") : F("Filesystem init failed!")); #ifdef USE_SPIFFS - // Debug: dump on console contents of filessytem with no filter and check filenames validity + // Debug: dump on console contents of filesystem with no filter and check filenames validity Dir dir = fileSystem->openDir(""); DBG_OUTPUT_PORT.println(F("List of files at root of filesystem:")); while (dir.next()) { String error = checkForUnsupportedPath(dir.fileName()); String fileInfo = dir.fileName() + (dir.isDirectory() ? " [DIR]" : String(" (") + dir.fileSize() + "b)"); DBG_OUTPUT_PORT.println(error + fileInfo); - if (error.length() > 0) { - unsupportedFiles += error + fileInfo + '\n'; - } + if (error.length() > 0) { unsupportedFiles += error + fileInfo + '\n'; } } DBG_OUTPUT_PORT.println(); @@ -609,15 +540,15 @@ void setup(void) { server.on("/edit", HTTP_GET, handleGetEdit); // Create file - server.on("/edit", HTTP_PUT, handleFileCreate); + server.on("/edit", HTTP_PUT, handleFileCreate); // Delete file - server.on("/edit", HTTP_DELETE, handleFileDelete); + server.on("/edit", HTTP_DELETE, handleFileDelete); // Upload file // - first callback is called after the request has ended with all parsed arguments // - second callback handles file upload at that location - server.on("/edit", HTTP_POST, replyOK, handleFileUpload); + server.on("/edit", HTTP_POST, replyOK, handleFileUpload); // Default handler for all URIs not defined above // Use it to read files from filesystem 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 4f73455676..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 //////////////////////////////// @@ -58,7 +58,7 @@ SDFSConfig fileSystemConfig = SDFSConfig(); #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif // Indicate which digital I/Os should be displayed on the chart. @@ -110,9 +110,7 @@ void replyServerError(String msg) { bool handleFileRead(String path) { DBG_OUTPUT_PORT.println(String("handleFileRead: ") + path); - if (path.endsWith("/")) { - path += "index.htm"; - } + if (path.endsWith("/")) { path += "index.htm"; } String contentType = mime::getContentType(path); @@ -122,9 +120,7 @@ bool handleFileRead(String path) { } if (fileSystem->exists(path)) { File file = fileSystem->open(path, "r"); - if (server.streamFile(file, contentType) != file.size()) { - DBG_OUTPUT_PORT.println("Sent less data than expected!"); - } + if (server.streamFile(file, contentType) != file.size()) { DBG_OUTPUT_PORT.println("Sent less data than expected!"); } file.close(); return true; } @@ -134,16 +130,14 @@ bool handleFileRead(String path) { /* - The "Not Found" handler catches all URI not explicitely declared in code + The "Not Found" handler catches all URI not explicitly declared in code First try to find and return the requested file from the filesystem, and if it fails, return a 404 page with debug information */ void handleNotFound() { - String uri = ESP8266WebServer::urlDecode(server.uri()); // required to read paths with blanks + String uri = ESP8266WebServer::urlDecode(server.uri()); // required to read paths with blanks - if (handleFileRead(uri)) { - return; - } + if (handleFileRead(uri)) { return; } // Dump debug data String message; @@ -218,7 +212,7 @@ void setup(void) { //////////////////////////////// // WEB SERVER INIT - //get heap status, analog input value and all GPIO statuses in one json call + // get heap status, analog input value and all GPIO statuses in one json call server.on("/espData", HTTP_GET, []() { String json; json.reserve(88); @@ -249,21 +243,18 @@ void setup(void) { DBG_OUTPUT_PORT.println(" 0 (OFF): outputs are off and hidden from chart"); DBG_OUTPUT_PORT.println(" 1 (AUTO): outputs are rotated automatically every second"); DBG_OUTPUT_PORT.println(" 2 (MANUAL): outputs can be toggled from the web page"); - } // Return default GPIO mask, that is all I/Os except SD card ones unsigned int defaultMask() { unsigned int mask = 0b11111111111111111; for (auto pin = 0; pin <= 16; pin++) { - if (isFlashInterfacePin(pin)) { - mask &= ~(1 << pin); - } + if (isFlashInterfacePin(pin)) { mask &= ~(1 << pin); } } return mask; } -int rgbMode = 1; // 0=off - 1=auto - 2=manual +int rgbMode = 1; // 0=off - 1=auto - 2=manual int rgbValue = 0; esp8266::polledTimeout::periodicMs timeToChange(1000); bool modeChangeRequested = false; @@ -278,28 +269,24 @@ void loop(void) { } // see if one second has passed since last change, otherwise stop here - if (!timeToChange) { - return; - } + if (!timeToChange) { return; } // see if a mode change was requested if (modeChangeRequested) { // increment mode (reset after 2) rgbMode++; - if (rgbMode > 2) { - rgbMode = 0; - } + if (rgbMode > 2) { rgbMode = 0; } modeChangeRequested = false; } // act according to mode switch (rgbMode) { - case 0: // off + case 0: // off gpioMask = defaultMask(); - gpioMask &= ~(1 << 12); // Hide GPIO 12 - gpioMask &= ~(1 << 13); // Hide GPIO 13 - gpioMask &= ~(1 << 15); // Hide GPIO 15 + gpioMask &= ~(1 << 12); // Hide GPIO 12 + gpioMask &= ~(1 << 13); // Hide GPIO 13 + gpioMask &= ~(1 << 15); // Hide GPIO 15 // reset outputs digitalWrite(12, 0); @@ -307,14 +294,12 @@ void loop(void) { digitalWrite(15, 0); break; - case 1: // auto + case 1: // auto gpioMask = defaultMask(); // increment value (reset after 7) rgbValue++; - if (rgbValue > 7) { - rgbValue = 0; - } + if (rgbValue > 7) { rgbValue = 0; } // output new values digitalWrite(12, rgbValue & 0b001); @@ -322,11 +307,10 @@ void loop(void) { digitalWrite(15, rgbValue & 0b100); break; - case 2: // manual + case 2: // manual gpioMask = defaultMask(); // keep outputs unchanged break; } } - diff --git a/libraries/ESP8266WebServer/examples/HelloServer/HelloServer.ino b/libraries/ESP8266WebServer/examples/HelloServer/HelloServer.ino index 6715f9ee41..105ce6e947 100644 --- a/libraries/ESP8266WebServer/examples/HelloServer/HelloServer.ino +++ b/libraries/ESP8266WebServer/examples/HelloServer/HelloServer.ino @@ -5,7 +5,7 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif const char* ssid = STASSID; @@ -13,6 +13,8 @@ const char* password = STAPSK; ESP8266WebServer server(80); +String bigChunk; + const int led = 13; void handleRoot() { @@ -31,13 +33,21 @@ void handleNotFound() { message += "\nArguments: "; message += server.args(); message += "\n"; - for (uint8_t i = 0; i < server.args(); i++) { - message += " " + server.argName(i) + ": " + server.arg(i) + "\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 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); @@ -57,9 +67,7 @@ void setup(void) { Serial.print("IP address: "); Serial.println(WiFi.localIP()); - if (MDNS.begin("esp8266")) { - Serial.println("MDNS responder started"); - } + if (MDNS.begin("esp8266")) { Serial.println("MDNS responder started"); } server.on("/", handleRoot); @@ -84,22 +92,24 @@ void setup(void) { server.send(200, "image/gif", gif_colored, sizeof(gif_colored)); }); + server.on("/chunks", handleChunked); + server.onNotFound(handleNotFound); ///////////////////////////////////////////////////////// // Hook examples - server.addHook([](const String & method, const String & url, WiFiClient * client, ESP8266WebServer::ContentTypeFunction contentType) { - (void)method; // GET, PUT, ... - (void)url; // example: /root/myfile.html - (void)client; // the webserver tcp client connection - (void)contentType; // contentType(".html") => "text/html" + server.addHook([](const String& method, const String& url, WiFiClient* client, ESP8266WebServer::ContentTypeFunction contentType) { + (void)method; // GET, PUT, ... + (void)url; // example: /root/myfile.html + (void)client; // the webserver tcp client connection + (void)contentType; // contentType(".html") => "text/html" Serial.printf("A useless web hook has passed\n"); Serial.printf("(this hook is in 0x%08x area (401x=IRAM 402x=FLASH))\n", esp_get_program_counter()); return ESP8266WebServer::CLIENT_REQUEST_CAN_CONTINUE; }); - server.addHook([](const String&, const String & url, WiFiClient*, ESP8266WebServer::ContentTypeFunction) { + server.addHook([](const String&, const String& url, WiFiClient*, ESP8266WebServer::ContentTypeFunction) { if (url.startsWith("/fail")) { Serial.printf("An always failing web hook has been triggered\n"); return ESP8266WebServer::CLIENT_MUST_STOP; @@ -107,7 +117,7 @@ void setup(void) { return ESP8266WebServer::CLIENT_REQUEST_CAN_CONTINUE; }); - server.addHook([](const String&, const String & url, WiFiClient * client, ESP8266WebServer::ContentTypeFunction) { + server.addHook([](const String&, const String& url, WiFiClient* client, ESP8266WebServer::ContentTypeFunction) { if (url.startsWith("/dump")) { Serial.printf("The dumper web hook is on the run\n"); @@ -115,9 +125,9 @@ void setup(void) { // swallow the exact amount matching the full request+content, // hence the tcp connection cannot be handled anymore by the // webserver. -#ifdef STREAMTO_API +#ifdef STREAMSEND_API // we are lucky - client->toWithTimeout(Serial, 500); + client->sendAll(Serial, 500); #else auto last = millis(); while ((millis() - last) < 500) { @@ -137,7 +147,7 @@ void setup(void) { // check the client connection: it should not immediately be closed // (make another '/dump' one to close the first) Serial.printf("\nTelling server to forget this connection\n"); - static WiFiClient forgetme = *client; // stop previous one if present and transfer client refcounter + static WiFiClient forgetme = *client; // stop previous one if present and transfer client refcounter return ESP8266WebServer::CLIENT_IS_GIVEN; } return ESP8266WebServer::CLIENT_REQUEST_CAN_CONTINUE; @@ -146,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 8e9f4cd5b2..db93a80021 100644 --- a/libraries/ESP8266WebServer/examples/HelloServerBearSSL/HelloServerBearSSL.ino +++ b/libraries/ESP8266WebServer/examples/HelloServerBearSSL/HelloServerBearSSL.ino @@ -18,7 +18,7 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif const char* ssid = STASSID; @@ -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; @@ -89,24 +40,32 @@ void handleRoot() { digitalWrite(led, 0); } -void handleNotFound(){ +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 += (server.method() == HTTP_GET) ? "GET" : "POST"; message += "\nArguments: "; message += server.args(); message += "\n"; - for (uint8_t i=0; i")); out.println(F(" h - Free Heap Report;")); out.println(F(" i - iRAM umm_info(null, true);")); out.println(F(" d - dRAM umm_info(null, true);")); +#ifdef DEBUG_ESP_PORT out.println(F(" p - call stack_thunk_dump_stack();")); +#endif out.println(F(" R - Restart, ESP.restart();")); out.println(F(" ? - Print Help")); out.println(); @@ -205,7 +180,7 @@ void processKey(Print& out, int hotKey) { } -void loop(void){ +void loop(void) { server.handleClient(); MDNS.update(); if (Serial.available() > 0) { diff --git a/libraries/ESP8266WebServer/examples/HttpAdvancedAuth/HttpAdvancedAuth.ino b/libraries/ESP8266WebServer/examples/HttpAdvancedAuth/HttpAdvancedAuth.ino index 857048cf0b..8fc4b0f5b7 100644 --- a/libraries/ESP8266WebServer/examples/HttpAdvancedAuth/HttpAdvancedAuth.ino +++ b/libraries/ESP8266WebServer/examples/HttpAdvancedAuth/HttpAdvancedAuth.ino @@ -11,7 +11,7 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif const char* ssid = STASSID; @@ -39,13 +39,13 @@ void setup() { server.on("/", []() { if (!server.authenticate(www_username, www_password)) - //Basic Auth Method with Custom realm and Failure Response - //return server.requestAuthentication(BASIC_AUTH, www_realm, authFailResponse); - //Digest Auth Method with realm="Login Required" and empty Failure Response - //return server.requestAuthentication(DIGEST_AUTH); - //Digest Auth Method with Custom realm and empty Failure Response - //return server.requestAuthentication(DIGEST_AUTH, www_realm); - //Digest Auth Method with Custom realm and Failure Response + // Basic Auth Method with Custom realm and Failure Response + // return server.requestAuthentication(BASIC_AUTH, www_realm, authFailResponse); + // Digest Auth Method with realm="Login Required" and empty Failure Response + // return server.requestAuthentication(DIGEST_AUTH); + // Digest Auth Method with Custom realm and empty Failure Response + // return server.requestAuthentication(DIGEST_AUTH, www_realm); + // Digest Auth Method with Custom realm and Failure Response { return server.requestAuthentication(DIGEST_AUTH, www_realm, authFailResponse); } diff --git a/libraries/ESP8266WebServer/examples/HttpBasicAuth/HttpBasicAuth.ino b/libraries/ESP8266WebServer/examples/HttpBasicAuth/HttpBasicAuth.ino index 7c06637caf..b7d5c16f41 100644 --- a/libraries/ESP8266WebServer/examples/HttpBasicAuth/HttpBasicAuth.ino +++ b/libraries/ESP8266WebServer/examples/HttpBasicAuth/HttpBasicAuth.ino @@ -5,7 +5,7 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif const char* ssid = STASSID; diff --git a/libraries/ESP8266WebServer/examples/HttpHashCredAuth/HttpHashCredAuth.ino b/libraries/ESP8266WebServer/examples/HttpHashCredAuth/HttpHashCredAuth.ino index c2ceaca53d..86cbf1f369 100644 --- a/libraries/ESP8266WebServer/examples/HttpHashCredAuth/HttpHashCredAuth.ino +++ b/libraries/ESP8266WebServer/examples/HttpHashCredAuth/HttpHashCredAuth.ino @@ -15,21 +15,21 @@ #include #include -//Unfortunately it is not possible to have persistent WiFi credentials stored as anything but plain text. Obfuscation would be the only feasible barrier. +// Unfortunately it is not possible to have persistent WiFi credentials stored as anything but plain text. Obfuscation would be the only feasible barrier. #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif const char* ssid = STASSID; const char* wifi_pw = STAPSK; -const String file_credentials = R"(/credentials.txt)"; // LittleFS file name for the saved credentials -const String change_creds = "changecreds"; // Address for a credential change +const String file_credentials = R"(/credentials.txt)"; // LittleFS file name for the saved credentials +const String change_creds = "changecreds"; // Address for a credential change -//The ESP8266WebServerSecure requires an encryption certificate and matching key. -//These can generated with the bash script available in the ESP8266 Arduino repository. -//These values can be used for testing but are available publicly so should not be used in production. +// The ESP8266WebServerSecure requires an encryption certificate and matching key. +// These can generated with the bash script available in the ESP8266 Arduino repository. +// These values can be used for testing but are available publicly so should not be used in production. static const char serverCert[] PROGMEM = R"EOF( -----BEGIN CERTIFICATE----- MIIDSzCCAjMCCQD2ahcfZAwXxDANBgkqhkiG9w0BAQsFADCBiTELMAkGA1UEBhMC @@ -52,7 +52,7 @@ JfUvYadSYxh3nblvA4OL+iEZiW8NE3hbW6WPXxvS7Euge0uWMPc4uEcnsE0ZVG3m 5tAF1D5vAAwA8nfPysumlLsIjohJZo4lgnhB++AlOg== -----END CERTIFICATE----- )EOF"; -static const char serverKey[] PROGMEM = R"EOF( +static const char serverKey[] PROGMEM = R"EOF( -----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEA9UoHBtn4oNKXjRgIOQ/rLxK/iI0a8Q5mDxhfuwa9//FkftSI IFY8UhGk2YNJpnfKOyYWqbqwuJhIZJ2sEIWp2301OnavuGBrpKOgBJJljgH2l/4Z @@ -84,7 +84,7 @@ gz5JWYhbD6c38khSzJb0pNXCo3EuYAVa36kDM96k1BtWuhRS10Q1VXk= ESP8266WebServerSecure server(443); -//These are temporary credentials that will only be used if none are found saved in LittleFS. +// These are temporary credentials that will only be used if none are found saved in LittleFS. String login = "admin"; const String realm = "global"; String H1 = ""; @@ -93,16 +93,16 @@ String authentication_failed = "User authentication has failed."; void setup() { Serial.begin(115200); - //Initialize LittleFS to save credentials - if(!LittleFS.begin()){ - Serial.println("LittleFS initialization error, programmer flash configured?"); + // Initialize LittleFS to save credentials + if (!LittleFS.begin()) { + Serial.println("LittleFS initialization error, programmer flash configured?"); ESP.restart(); - } + } - //Attempt to load credentials. If the file does not yet exist, they will be set to the default values above + // Attempt to load credentials. If the file does not yet exist, they will be set to the default values above loadcredentials(); - //Initialize wifi + // Initialize wifi WiFi.mode(WIFI_STA); WiFi.begin(ssid, wifi_pw); if (WiFi.waitForConnectResult() != WL_CONNECTED) { @@ -112,8 +112,8 @@ void setup() { } server.getServer().setRSACert(new BearSSL::X509List(serverCert), new BearSSL::PrivateKey(serverKey)); - server.on("/",showcredentialpage); //for this simple example, just show a simple page for changing credentials at the root - server.on("/" + change_creds,handlecredentialchange); //handles submission of credentials from the client + server.on("/", showcredentialpage); // for this simple example, just show a simple page for changing credentials at the root + server.on("/" + change_creds, handlecredentialchange); // handles submission of credentials from the client server.onNotFound(redirect); server.begin(); @@ -127,49 +127,48 @@ void loop() { server.handleClient(); } -//This function redirects home -void redirect(){ +// This function redirects home +void redirect() { String url = "https://" + WiFi.localIP().toString(); Serial.println("Redirect called. Redirecting to " + url); server.sendHeader("Location", url, true); Serial.println("Header sent."); - server.send( 302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves. + server.send(302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves. Serial.println("Empty page sent."); - server.client().stop(); // Stop is needed because we sent no content length + server.client().stop(); // Stop is needed because we sent no content length Serial.println("Client stopped."); } -//This function checks whether the current session has been authenticated. If not, a request for credentials is sent. +// This function checks whether the current session has been authenticated. If not, a request for credentials is sent. bool session_authenticated() { Serial.println("Checking authentication."); - if (server.authenticateDigest(login,H1)) { + if (server.authenticateDigest(login, H1)) { Serial.println("Authentication confirmed."); return true; - } else { + } else { Serial.println("Not authenticated. Requesting credentials."); - server.requestAuthentication(DIGEST_AUTH,realm.c_str(),authentication_failed); + server.requestAuthentication(DIGEST_AUTH, realm.c_str(), authentication_failed); redirect(); return false; } } -//This function sends a simple webpage for changing login credentials to the client -void showcredentialpage(){ +// This function sends a simple webpage for changing login credentials to the client +void showcredentialpage() { Serial.println("Show credential page called."); - if(!session_authenticated()){ - return; - } + if (!session_authenticated()) { return; } Serial.println("Forming credential modification page."); String page; page = R"()"; - page+= - R"( + page += + R"(

Login Credentials


-
+ Login:

Password:
@@ -178,8 +177,7 @@ void showcredentialpage(){


- )" - ; + )"; page += R"()"; @@ -188,17 +186,16 @@ void showcredentialpage(){ server.send(200, "text/html", page); } -//Saves credentials to LittleFS -void savecredentials(String new_login, String new_password) -{ - //Set global variables to new values - login=new_login; - H1=ESP8266WebServer::credentialHash(new_login,realm,new_password); +// Saves credentials to LittleFS +void savecredentials(String new_login, String new_password) { + // Set global variables to new values + login = new_login; + H1 = ESP8266WebServer::credentialHash(new_login, realm, new_password); - //Save new values to LittleFS for loading on next reboot + // Save new values to LittleFS for loading on next reboot Serial.println("Saving credentials."); - File f=LittleFS.open(file_credentials,"w"); //open as a brand new file, discard old contents - if(f){ + File f = LittleFS.open(file_credentials, "w"); // open as a brand new file, discard old contents + if (f) { Serial.println("Modifying credentials in file system."); f.println(login); f.println(H1); @@ -209,19 +206,18 @@ void savecredentials(String new_login, String new_password) Serial.println("Credentials saved."); } -//loads credentials from LittleFS -void loadcredentials() -{ +// loads credentials from LittleFS +void loadcredentials() { Serial.println("Searching for credentials."); File f; - f=LittleFS.open(file_credentials,"r"); - if(f){ + f = LittleFS.open(file_credentials, "r"); + if (f) { Serial.println("Loading credentials from file system."); - String mod=f.readString(); //read the file to a String - int index_1=mod.indexOf('\n',0); //locate the first line break - int index_2=mod.indexOf('\n',index_1+1); //locate the second line break - login=mod.substring(0,index_1-1); //get the first line (excluding the line break) - H1=mod.substring(index_1+1,index_2-1); //get the second line (excluding the line break) + String mod = f.readString(); // read the file to a String + int index_1 = mod.indexOf('\n', 0); // locate the first line break + int index_2 = mod.indexOf('\n', index_1 + 1); // locate the second line break + login = mod.substring(0, index_1 - 1); // get the first line (excluding the line break) + H1 = mod.substring(index_1 + 1, index_2 - 1); // get the second line (excluding the line break) f.close(); } else { String default_login = "admin"; @@ -229,17 +225,15 @@ void loadcredentials() Serial.println("None found. Setting to default credentials."); Serial.println("user:" + default_login); Serial.println("password:" + default_password); - login=default_login; - H1=ESP8266WebServer::credentialHash(default_login,realm,default_password); + login = default_login; + H1 = ESP8266WebServer::credentialHash(default_login, realm, default_password); } } -//This function handles a credential change from a client. +// This function handles a credential change from a client. void handlecredentialchange() { Serial.println("Handle credential change called."); - if(!session_authenticated()){ - return; - } + if (!session_authenticated()) { return; } Serial.println("Handling credential change request from client."); @@ -247,9 +241,9 @@ void handlecredentialchange() { String pw1 = server.arg("password"); String pw2 = server.arg("password_duplicate"); - if(login != "" && pw1 != "" && pw1 == pw2){ + if (login != "" && pw1 != "" && pw1 == pw2) { - savecredentials(login,pw1); + savecredentials(login, pw1); server.send(200, "text/plain", "Credentials updated"); redirect(); } else { diff --git a/libraries/ESP8266WebServer/examples/PathArgServer/PathArgServer.ino b/libraries/ESP8266WebServer/examples/PathArgServer/PathArgServer.ino index dc998986e5..4e2ef12ce0 100644 --- a/libraries/ESP8266WebServer/examples/PathArgServer/PathArgServer.ino +++ b/libraries/ESP8266WebServer/examples/PathArgServer/PathArgServer.ino @@ -8,7 +8,7 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif const char *ssid = STASSID; @@ -33,9 +33,7 @@ void setup(void) { Serial.print("IP address: "); Serial.println(WiFi.localIP()); - if (MDNS.begin("esp8266")) { - Serial.println("MDNS responder started"); - } + if (MDNS.begin("esp8266")) { Serial.println("MDNS responder started"); } server.on(F("/"), []() { server.send(200, "text/plain", "hello from esp8266!"); diff --git a/libraries/ESP8266WebServer/examples/PostServer/PostServer.ino b/libraries/ESP8266WebServer/examples/PostServer/PostServer.ino index da1703807a..2785e37979 100644 --- a/libraries/ESP8266WebServer/examples/PostServer/PostServer.ino +++ b/libraries/ESP8266WebServer/examples/PostServer/PostServer.ino @@ -5,10 +5,10 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif -const char* ssid = STASSID; +const char* ssid = STASSID; const char* password = STAPSK; ESP8266WebServer server(80); @@ -62,9 +62,7 @@ void handleForm() { } else { digitalWrite(led, 1); String message = "POST form was:\n"; - for (uint8_t i = 0; i < server.args(); i++) { - message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; - } + for (uint8_t i = 0; i < server.args(); i++) { message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; } server.send(200, "text/plain", message); digitalWrite(led, 0); } @@ -80,9 +78,7 @@ void handleNotFound() { message += "\nArguments: "; message += server.args(); message += "\n"; - for (uint8_t i = 0; i < server.args(); i++) { - message += " " + server.argName(i) + ": " + server.arg(i) + "\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); } @@ -105,9 +101,7 @@ void setup(void) { Serial.print("IP address: "); Serial.println(WiFi.localIP()); - if (MDNS.begin("esp8266")) { - Serial.println("MDNS responder started"); - } + if (MDNS.begin("esp8266")) { Serial.println("MDNS responder started"); } server.on("/", handleRoot); diff --git a/libraries/ESP8266WebServer/examples/ServerSentEvents/ServerSentEvents.ino b/libraries/ESP8266WebServer/examples/ServerSentEvents/ServerSentEvents.ino index 77ae1e958e..ced3548b3c 100644 --- a/libraries/ESP8266WebServer/examples/ServerSentEvents/ServerSentEvents.ino +++ b/libraries/ESP8266WebServer/examples/ServerSentEvents/ServerSentEvents.ino @@ -42,11 +42,11 @@ extern "C" { #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif -const char* ssid = STASSID; -const char* password = STAPSK; +const char *ssid = STASSID; +const char *password = STAPSK; const unsigned int port = 80; ESP8266WebServer server(port); @@ -76,20 +76,16 @@ void handleNotFound() { message += "\nArguments: "; message += server.args(); message += "\n"; - for (uint8_t i = 0; i < server.args(); i++) { - message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; - } + for (uint8_t i = 0; i < server.args(); i++) { message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; } server.send(404, "text/plain", message); } void SSEKeepAlive() { for (uint8_t i = 0; i < SSE_MAX_CHANNELS; i++) { - if (!(subscription[i].clientIP)) { - continue; - } + if (!(subscription[i].clientIP)) { continue; } if (subscription[i].client.connected()) { Serial.printf_P(PSTR("SSEKeepAlive - client is still listening on channel %d\n"), i); - subscription[i].client.println(F("event: event\ndata: { \"TYPE\":\"KEEP-ALIVE\" }\n")); // Extra newline required by SSE standard + subscription[i].client.println(F("event: event\ndata: { \"TYPE\":\"KEEP-ALIVE\" }\n")); // Extra newline required by SSE standard } else { Serial.printf_P(PSTR("SSEKeepAlive - client not listening on channel %d, remove subscription\n"), i); subscription[i].keepAliveTimer.detach(); @@ -106,15 +102,15 @@ void SSEKeepAlive() { void SSEHandler(uint8_t channel) { WiFiClient client = server.client(); SSESubscription &s = subscription[channel]; - if (s.clientIP != client.remoteIP()) { // IP addresses don't match, reject this client + if (s.clientIP != client.remoteIP()) { // IP addresses don't match, reject this client Serial.printf_P(PSTR("SSEHandler - unregistered client with IP %s tries to listen\n"), server.client().remoteIP().toString().c_str()); return handleNotFound(); } client.setNoDelay(true); client.setSync(true); Serial.printf_P(PSTR("SSEHandler - registered client with IP %s is listening\n"), IPAddress(s.clientIP).toString().c_str()); - s.client = client; // capture SSE server client connection - server.setContentLength(CONTENT_LENGTH_UNKNOWN); // the payload can go on forever + s.client = client; // capture SSE server client connection + server.setContentLength(CONTENT_LENGTH_UNKNOWN); // the payload can go on forever server.sendContent_P(PSTR("HTTP/1.1 200 OK\nContent-Type: text/event-stream;\nConnection: keep-alive\nCache-Control: no-cache\nAccess-Control-Allow-Origin: *\n\n")); s.keepAliveTimer.attach_scheduled(30.0, SSEKeepAlive); // Refresh time every 30s for demo } @@ -122,28 +118,20 @@ void SSEHandler(uint8_t channel) { void handleAll() { const char *uri = server.uri().c_str(); const char *restEvents = PSTR("/rest/events/"); - if (strncmp_P(uri, restEvents, strlen_P(restEvents))) { - return handleNotFound(); - } - uri += strlen_P(restEvents); // Skip the "/rest/events/" and get to the channel number + if (strncmp_P(uri, restEvents, strlen_P(restEvents))) { return handleNotFound(); } + uri += strlen_P(restEvents); // Skip the "/rest/events/" and get to the channel number unsigned int channel = atoi(uri); - if (channel < SSE_MAX_CHANNELS) { - return SSEHandler(channel); - } + if (channel < SSE_MAX_CHANNELS) { return SSEHandler(channel); } handleNotFound(); }; void SSEBroadcastState(const char *sensorName, unsigned short prevSensorValue, unsigned short sensorValue) { for (uint8_t i = 0; i < SSE_MAX_CHANNELS; i++) { - if (!(subscription[i].clientIP)) { - continue; - } + if (!(subscription[i].clientIP)) { continue; } String IPaddrstr = IPAddress(subscription[i].clientIP).toString(); if (subscription[i].client.connected()) { - Serial.printf_P(PSTR("broadcast status change to client IP %s on channel %d for %s with new state %d\n"), - IPaddrstr.c_str(), i, sensorName, sensorValue); - subscription[i].client.printf_P(PSTR("event: event\ndata: {\"TYPE\":\"STATE\", \"%s\":{\"state\":%d, \"prevState\":%d}}\n\n"), - sensorName, sensorValue, prevSensorValue); + Serial.printf_P(PSTR("broadcast status change to client IP %s on channel %d for %s with new state %d\n"), IPaddrstr.c_str(), i, sensorName, sensorValue); + subscription[i].client.printf_P(PSTR("event: event\ndata: {\"TYPE\":\"STATE\", \"%s\":{\"state\":%d, \"prevState\":%d}}\n\n"), sensorName, sensorValue, prevSensorValue); } else { Serial.printf_P(PSTR("SSEBroadcastState - client %s registered on channel %d but not listening\n"), IPaddrstr.c_str(), i); } @@ -152,13 +140,15 @@ void SSEBroadcastState(const char *sensorName, unsigned short prevSensorValue, u // Simulate sensors void updateSensor(sensorType &sensor) { - unsigned short newVal = (unsigned short)RANDOM_REG32; // (not so good) random value for the sensor + unsigned short newVal = (unsigned short)RANDOM_REG32; // (not so good) random value for the sensor Serial.printf_P(PSTR("update sensor %s - previous state: %d, new state: %d\n"), sensor.name, sensor.value, newVal); if (sensor.value != newVal) { 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() { @@ -167,7 +157,7 @@ void handleSubscribe() { } uint8_t channel; - IPAddress clientIP = server.client().remoteIP(); // get IP address of client + IPAddress clientIP = server.client().remoteIP(); // get IP address of client String SSEurl = F("http://"); SSEurl += WiFi.localIP().toString(); SSEurl += F(":"); @@ -176,14 +166,12 @@ void handleSubscribe() { SSEurl += F("/rest/events/"); ++subscriptionCount; - for (channel = 0; channel < SSE_MAX_CHANNELS; channel++) // Find first free slot - if (!subscription[channel].clientIP) { - break; - } - subscription[channel] = {clientIP, server.client(), Ticker()}; + for (channel = 0; channel < SSE_MAX_CHANNELS; channel++) // Find first free slot + if (!subscription[channel].clientIP) { break; } + subscription[channel] = { clientIP, server.client(), Ticker() }; SSEurl += channel; Serial.printf_P(PSTR("Allocated channel %d, on uri %s\n"), channel, SSEurl.substring(offset).c_str()); - //server.on(SSEurl.substring(offset), std::bind(SSEHandler, &(subscription[channel]))); + // server.on(SSEurl.substring(offset), std::bind(SSEHandler, &(subscription[channel]))); Serial.printf_P(PSTR("subscription for client IP %s: event bus location: %s\n"), clientIP.toString().c_str(), SSEurl.c_str()); server.send_P(200, "text/plain", SSEurl.c_str()); } @@ -200,16 +188,14 @@ void setup(void) { WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); Serial.println(""); - while (WiFi.status() != WL_CONNECTED) { // Wait for connection + while (WiFi.status() != WL_CONNECTED) { // Wait for connection delay(500); Serial.print("."); } Serial.printf_P(PSTR("\nConnected to %s with IP address: %s\n"), ssid, WiFi.localIP().toString().c_str()); - if (MDNS.begin("esp8266")) { - Serial.println("MDNS responder started"); - } + if (MDNS.begin("esp8266")) { Serial.println("MDNS responder started"); } - startServers(); // start web and SSE servers + startServers(); // start web and SSE servers sensor[0].name = "sensorA"; sensor[1].name = "sensorB"; updateSensor(sensor[0]); diff --git a/libraries/ESP8266WebServer/examples/SimpleAuthentication/SimpleAuthentication.ino b/libraries/ESP8266WebServer/examples/SimpleAuthentication/SimpleAuthentication.ino index 38e68a2275..aaea090ed9 100644 --- a/libraries/ESP8266WebServer/examples/SimpleAuthentication/SimpleAuthentication.ino +++ b/libraries/ESP8266WebServer/examples/SimpleAuthentication/SimpleAuthentication.ino @@ -4,7 +4,7 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif const char* ssid = STASSID; @@ -12,7 +12,7 @@ const char* password = STAPSK; ESP8266WebServer server(80); -//Check if header is present and correct +// Check if header is present and correct bool is_authenticated() { Serial.println("Enter is_authenticated"); if (server.hasHeader("Cookie")) { @@ -28,7 +28,7 @@ bool is_authenticated() { return false; } -//login page, also called for disconnect +// login page, also called for disconnect void handleLogin() { String msg; if (server.hasHeader("Cookie")) { @@ -45,7 +45,7 @@ void handleLogin() { return; } if (server.hasArg("USERNAME") && server.hasArg("PASSWORD")) { - if (server.arg("USERNAME") == "admin" && server.arg("PASSWORD") == "admin") { + if (server.arg("USERNAME") == "admin" && server.arg("PASSWORD") == "admin") { server.sendHeader("Location", "/"); server.sendHeader("Cache-Control", "no-cache"); server.sendHeader("Set-Cookie", "ESPSESSIONID=1"); @@ -64,7 +64,7 @@ void handleLogin() { server.send(200, "text/html", content); } -//root page can be accessed only if authentication is ok +// root page can be accessed only if authentication is ok void handleRoot() { Serial.println("Enter handleRoot"); String header; @@ -75,14 +75,12 @@ void handleRoot() { return; } String content = "

hello, you successfully connected to esp8266!


"; - if (server.hasHeader("User-Agent")) { - content += "the user agent used is : " + server.header("User-Agent") + "

"; - } + if (server.hasHeader("User-Agent")) { content += "the user agent used is : " + server.header("User-Agent") + "

"; } content += "You can access this page until you disconnect"; server.send(200, "text/html", content); } -//no need authentication +// no need authentication void handleNotFound() { String message = "File Not Found\n\n"; message += "URI: "; @@ -92,9 +90,7 @@ void handleNotFound() { message += "\nArguments: "; message += server.args(); message += "\n"; - for (uint8_t i = 0; i < server.args(); i++) { - message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; - } + for (uint8_t i = 0; i < server.args(); i++) { message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; } server.send(404, "text/plain", message); } @@ -123,11 +119,8 @@ void setup(void) { }); server.onNotFound(handleNotFound); - //here the list of headers to be recorded - const char * headerkeys[] = {"User-Agent", "Cookie"} ; - size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*); - //ask server to track these headers - server.collectHeaders(headerkeys, headerkeyssize); + // ask server to track these headers + server.collectHeaders("User-Agent", "Cookie"); server.begin(); Serial.println("HTTP server started"); } diff --git a/libraries/ESP8266WebServer/examples/WebServer/README.md b/libraries/ESP8266WebServer/examples/WebServer/README.md new file mode 100644 index 0000000000..0c7b1143da --- /dev/null +++ b/libraries/ESP8266WebServer/examples/WebServer/README.md @@ -0,0 +1,197 @@ +# WebServer example documentation and hints + +This example shows different techniques on how to use and extend the ESP8266WebServer for specific purposes + +It is a small project in it's own and has some files to use on the web server to show how to use simple REST based services. + +It requires some space for a filesystem and runs fine on ESP8266 NodeMCU board with 4 MByte flash using the following options: +* Flash Size Option 4MB (FS:2MB) +* Debug Port Serial +* MMU 32+32 balanced + +It features + +* http access to the web server +* deliver all files from the file system +* deliver special built-in files +* implement services (list files, sysinfo) +* uploading files using drag & drop +* listing and deleting files using a SPA application +* Example of SPA and Web Service application +* 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. + +The advantage on using the ESP8266WebServer instead of the plain simple WiFiServer is that the ESP8266WebServer +takes much care about the http protocol conventions and features and allows easily access to parameters. +It offers plug-in capabilities by registering specific functionalities that will be outlined below. + + +### Initialization + +In the setup() function in the webserver.ino sketch file the following steps are implemented to make the webserver available on the local network. + +* Create a webserver listening to port 80 for http requests. +* Initialize the access to the filesystem in the free flash memory (typically 2MByte). +* Connect to the local WiFi network. Here is only a straight-forward implementation hard-coding network name and passphrase. You may consider to use something like the WiFiManager library. +* Register the device in DNS using a known hostname. +* Registering several plug-ins (see below). +* Starting the web server. + + +### Running + +In the loop() function the web server will be given time to receive and send network packages by calling +`server.handleClient();`. + + + +## Registering simple functions to implement RESTful services + +Registering function is the simplest integration mechanism available to add functionality. The server offers the `on(path, function)` methods that take the URL and the function as parameters. + +There are 2 functions implemented that get registered to handle incoming GET requests for given URLs. + +The JSON data format is used often for such services as it is the "natural" data format of the browser using javascript. + +When the **handleSysInfo()** function is registered and a browser requests for the function will be called and can collect the requested information. + +> ```CPP +> server.on("/$sysinfo", handleSysInfo); +> ``` + +The result in this case is a JSON object that is assembled in the result String variable and the returned as a response to the client also giving the information about the data format. + +You can try this request in a browser by opening in the address bar. + +> ```CPP +> server.on("/$sysinfo", handleList); +> ``` + +The function **handleList()** is registered the same way to return the list of files in the file system also returning a JSON object including name, size and the last modification timestamp. + +You can try this request in a browser by opening in the address bar. + + +## Registering a function to send out some static content from a String + +This is an example of registering a inline function in the web server. +The 2. parameter of the on() method is a so called CPP lamda function (without a name) +that actually has only one line of functionality by sending a string as result to the client. + +> ```CPP +> server.on("/$upload.htm", []() { +> server.send(200, "text/html", FPSTR(uploadContent)); +> }); +> ``` + +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. + +Just open and drag some files from the data folder on the drop area. + + +## 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 `handleRedirect()` function is registered: + +> ```CPP +> server.on("/", HTTP_GET, handleRedirect); +> ``` + +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. + + + +## Using the serveStatic plug-in + +The **serveStatic** plug in is part of the library and handles delivering files from the filesystem to the client. It can be customized in some ways. + +> ```CPP +> server.enableCORS(true); +> server.enableETag(true); +> server.serveStatic("/", LittleFS, "/"); +> ``` + + +### 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. + +The feature is disabled by default (in the current version) and when you like to disable this then you should call `enableCORS(false)` during setup. + +* Web sites providing high sensitive information like online banking this is disabled most of the times. +* Web sites providing advertising information or reusable scripts / images this is enabled. + + +### ETag support + +The `enableETag(true)` function adds a ETag http header to the responses to the client that come from files from the filesystem +to enable better use of the cache in the browser. + +When enabled by default the server reads the file content and creates a checksum using the md5 and base64 algorithm her called the ETag value +that changes whenever the file contains something different. + +Once a browser has got the content of a file from the server including the ETag information it will add that ETag value is added in the following requests for the same resource. +Now the server can answer with a 'use the version from the cache' when the new calculated ETag value is equal to the ETag value in the request. + +The calculation of the ETag value requires some time and processing but sending content is always slower. +So when you have the situation that a browser will use a web server multiple times this mechanism saves network and computing and makes web pages more responsive. + +In the source code you can find another version of an algorithm to calculate a ETag value that uses the date&time from the filesystem. +This is a simpler and faster way but with a low risk of dismissing a file update as the timestamp is based on seconds and local time. +This can be enabled on demand, see inline comments. + + +## Registering a full-featured handler as plug-in + +The example also implements the class `FileServerHandler` derived from the class `RequestHandler` to plug in functionality +that can handle more complex requests without giving a fixed URL. +It implements uploading and deleting files in the file system that is not implemented by the standard server.serveStatic functionality. + +This class has to implements several functions and works in a more detailed way: + +* The `canHandle()` method can inspect the given http method and url to decide weather the RequestFileHandler can handle the incoming request or not. + + In this case the RequestFileHandler will return true when the request method is an POST for upload or a DELETE for deleting files. + + The regular GET requests will be ignored and therefore handled by the also registered server.serveStatic handler. + +* The function `handle()` then implements the real deletion of the file. + +* The `canUpload()`and `upload()` methods work similar while the `upload()` method is called multiple times to create, append data and close the new file. + + +## Registering a special handler for "file not found" + +Any other incoming request that was not handled by the registered plug-ins above can be detected by registering + +> ```CPP +> // handle cases when file is not found +> server.onNotFound([]() { +> // standard not found in browser. +> server.send(404, "text/html", FPSTR(notFoundContent)); +> }); +> ``` + +This allows sending back an "friendly" result for the browser. Here a sim ple html page is created from a static string. +You can easily change the html code in the file `builtinfiles.h`. + + +## customizations + +You may like to change the hostname and the timezone in the lines: + +> ```CPP +> #define HOSTNAME "webserver" +> #define TIMEZONE "CET-1CEST,M3.5.0,M10.5.0/3" +> ``` + + diff --git a/libraries/ESP8266WebServer/examples/WebServer/WebServer.ino b/libraries/ESP8266WebServer/examples/WebServer/WebServer.ino new file mode 100644 index 0000000000..9e88e96f1f --- /dev/null +++ b/libraries/ESP8266WebServer/examples/WebServer/WebServer.ino @@ -0,0 +1,244 @@ +// @file WebServer.ino +// @brief Example implementation using the ESP8266 WebServer. +// +// See also README.md for instructions and hints. +// +// Changelog: +// 21.07.2021 creation, first version + +#include +#include + +#include "secrets.h" // add WLAN Credentials in here. + +#include // File System for Web Server Files +#include // This file system is used. + +// mark parameters not used in example +#define UNUSED __attribute__((unused)) + +// TRACE output simplified, can be deactivated here +#define TRACE(...) Serial.printf(__VA_ARGS__) + +// name of the server. You reach it using http://webserver +#define HOSTNAME "webserver" + +// local time zone definition (Berlin) +#define TIMEZONE "CET-1CEST,M3.5.0,M10.5.0/3" + +// need a WebServer for http access on port 80. +ESP8266WebServer server(80); + +// The text of builtin files are in this header file +#include "builtinfiles.h" + + +// ===== Simple functions used to answer simple GET requests ===== + +// This function is called when the WebServer was requested without giving a filename. +// This will redirect to the file index.htm when it is existing otherwise to the built-in $upload.htm page +void handleRedirect() { + TRACE("Redirect..."); + String url = "/index.htm"; + + if (!LittleFS.exists(url)) { url = "/$upload.htm"; } + + server.redirect(url); +} // handleRedirect() + + +// This function is called when the WebServer was requested to list all existing files in the filesystem. +// a JSON array with file information is returned. +void handleListFiles() { + Dir dir = LittleFS.openDir("/"); + String result; + + result += "[\n"; + while (dir.next()) { + if (result.length() > 4) { result += ","; } + result += " {"; + result += " \"name\": \"" + dir.fileName() + "\", "; + result += " \"size\": " + String(dir.fileSize()) + ", "; + result += " \"time\": " + String(dir.fileTime()); + result += " }\n"; + // jc.addProperty("size", dir.fileSize()); + } // while + result += "]"; + server.sendHeader("Cache-Control", "no-cache"); + server.send(200, "text/javascript; charset=utf-8", result); +} // handleListFiles() + + +// This function is called when the sysInfo service was requested. +void handleSysInfo() { + String result; + + FSInfo fs_info; + LittleFS.info(fs_info); + + result += "{\n"; + result += " \"flashSize\": " + String(ESP.getFlashChipSize()) + ",\n"; + result += " \"freeHeap\": " + String(ESP.getFreeHeap()) + ",\n"; + result += " \"fsTotalBytes\": " + String(fs_info.totalBytes) + ",\n"; + result += " \"fsUsedBytes\": " + String(fs_info.usedBytes) + ",\n"; + result += "}"; + + server.sendHeader("Cache-Control", "no-cache"); + server.send(200, "text/javascript; charset=utf-8", result); +} // handleSysInfo() + + +// ===== Request Handler class used to answer more complex requests ===== + +// The FileServerHandler is registered to the web server to support DELETE and UPLOAD of files into the filesystem. +class FileServerHandler : public RequestHandler { +public: + // @brief Construct a new File Server Handler object + // @param fs The file system to be used. + // @param path Path to the root folder in the file system that is used for serving static data down and upload. + // @param cache_header Cache Header to be used in replies. + FileServerHandler() { + TRACE("FileServerHandler is registered\n"); + } + + + // @brief check incoming request. Can handle POST for uploads and DELETE. + // @param requestMethod method of 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)); + } // canHandle() + + + bool canUpload(const String &uri) override { + // only allow upload on root fs level. + return (uri == "/"); + } // canUpload() + + + bool handle(ESP8266WebServer &server, HTTPMethod requestMethod, const String &requestUri) override { + // ensure that filename starts with '/' + String fName = requestUri; + if (!fName.startsWith("/")) { fName = "/" + fName; } + + if (requestMethod == HTTP_POST) { + // all done in upload. no other forms. + + } else if (requestMethod == HTTP_DELETE) { + if (LittleFS.exists(fName)) { LittleFS.remove(fName); } + } // if + + server.send(200); // all done. + return (true); + } // handle() + + + // uploading process + void upload(ESP8266WebServer UNUSED &server, const String UNUSED &_requestUri, HTTPUpload &upload) override { + // ensure that filename starts with '/' + String fName = upload.filename; + if (!fName.startsWith("/")) { fName = "/" + fName; } + + if (upload.status == UPLOAD_FILE_START) { + // Open the file + if (LittleFS.exists(fName)) { LittleFS.remove(fName); } // if + _fsUploadFile = LittleFS.open(fName, "w"); + + } else if (upload.status == UPLOAD_FILE_WRITE) { + // Write received bytes + if (_fsUploadFile) { _fsUploadFile.write(upload.buf, upload.currentSize); } + + } else if (upload.status == UPLOAD_FILE_END) { + // Close the file + if (_fsUploadFile) { _fsUploadFile.close(); } + } // if + } // upload() + +protected: + File _fsUploadFile; +}; + + +// Setup everything to make the webserver work. +void setup(void) { + delay(3000); // wait for serial monitor to start completely. + + // Use Serial port for some trace information from the example + Serial.begin(115200); + Serial.setDebugOutput(false); + + TRACE("Starting WebServer example...\n"); + + TRACE("Mounting the filesystem...\n"); + if (!LittleFS.begin()) { + TRACE("could not mount the filesystem...\n"); + delay(2000); + ESP.restart(); + } + + // start WiFI + WiFi.mode(WIFI_STA); + if (strlen(ssid) == 0) { + WiFi.begin(); + } else { + WiFi.begin(ssid, passPhrase); + } + + // allow to address the device by the given name e.g. http://webserver + WiFi.setHostname(HOSTNAME); + + TRACE("Connect to WiFi...\n"); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + TRACE("."); + } + TRACE("connected.\n"); + + // Ask for the current time using NTP request builtin into ESP firmware. + TRACE("Setup ntp...\n"); + configTime(TIMEZONE, "pool.ntp.org"); + + TRACE("Register service handlers...\n"); + + // serve a built-in htm page + server.on("/$upload.htm", []() { + server.send(200, "text/html", FPSTR(uploadContent)); + }); + + // register a redirect handler when only domain name is given. + server.on("/", HTTP_GET, handleRedirect); + + // register some REST services + server.on("/$list", HTTP_GET, handleListFiles); + server.on("/$sysinfo", HTTP_GET, handleSysInfo); + + // UPLOAD and DELETE of files in the file system using a request handler. + server.addHandler(new FileServerHandler()); + + // enable CORS header in webserver results + server.enableCORS(true); + + // enable ETAG header in webserver results from serveStatic handler + server.enableETag(true); + + // serve all static files + server.serveStatic("/", LittleFS, "/"); + + // handle cases when file is not found + server.onNotFound([]() { + // standard not found in browser. + server.send(404, "text/html", FPSTR(notFoundContent)); + }); + + server.begin(); + TRACE("hostname=%s\n", WiFi.getHostname()); +} // setup + + +// run the server... +void loop(void) { + server.handleClient(); +} // loop() + +// end. diff --git a/libraries/ESP8266WebServer/examples/WebServer/builtinfiles.h b/libraries/ESP8266WebServer/examples/WebServer/builtinfiles.h new file mode 100644 index 0000000000..2daf9e3819 --- /dev/null +++ b/libraries/ESP8266WebServer/examples/WebServer/builtinfiles.h @@ -0,0 +1,63 @@ +/** + * @file builtinfiles.h + * @brief This file is part of the WebServer example for the ESP8266WebServer. + * + * This file contains long, multiline text variables for all builtin resources. + */ + +// used for $upload.htm +static const char uploadContent[] PROGMEM = +R"==( + + + + + + + Upload + + + +

Upload

+ +
+
Drop files here...
+ + + +)=="; + +// used for $upload.htm +static const char notFoundContent[] PROGMEM = R"==( + + + Resource not found + + +

The resource was not found.

+

Start again

+ +)=="; diff --git a/libraries/ESP8266WebServer/examples/WebServer/data/files.htm b/libraries/ESP8266WebServer/examples/WebServer/data/files.htm new file mode 100644 index 0000000000..c05c22fc78 --- /dev/null +++ b/libraries/ESP8266WebServer/examples/WebServer/data/files.htm @@ -0,0 +1,65 @@ + + + + Files + + + + +

Files on Server

+ +

These files are available on the server to be opened or delete:

+
+
+ + + + + \ No newline at end of file diff --git a/libraries/ESP8266WebServer/examples/WebServer/data/index.htm b/libraries/ESP8266WebServer/examples/WebServer/data/index.htm new file mode 100644 index 0000000000..89188f5203 --- /dev/null +++ b/libraries/ESP8266WebServer/examples/WebServer/data/index.htm @@ -0,0 +1,24 @@ + + + + HomePage + + + + +

Homepage of the WebServer Example

+ +

The following pages are available:

+ + +

The following REST services are available:

+
    +
  • /$sysinfo - Some system level information
  • +
  • /$list - Array of all files
  • +
+ + \ No newline at end of file diff --git a/libraries/ESP8266WebServer/examples/WebServer/data/style.css b/libraries/ESP8266WebServer/examples/WebServer/data/style.css new file mode 100644 index 0000000000..95ac48e727 --- /dev/null +++ b/libraries/ESP8266WebServer/examples/WebServer/data/style.css @@ -0,0 +1,10 @@ +html, body { + color: #111111; font-family: Arial, ui-sans-serif, sans-serif; font-size: 1em; background-color: #f0f0f0; +} + +#list > div { + margin: 0 0 0.5rem 0; +} + +a { color: inherit; cursor: pointer; } + diff --git a/libraries/ESP8266WebServer/examples/WebServer/secrets.h b/libraries/ESP8266WebServer/examples/WebServer/secrets.h new file mode 100644 index 0000000000..e1d39a5146 --- /dev/null +++ b/libraries/ESP8266WebServer/examples/WebServer/secrets.h @@ -0,0 +1,18 @@ +// Secrets for your local home network + +// This is a "hard way" to configure your local WiFi network name and passphrase +// into the source code and the uploaded sketch. +// +// Using the WiFi Manager is preferred and avoids reprogramming when your network changes. +// See https://homeding.github.io/#page=/wifimanager.md + +// ssid and passPhrase can be used when compiling for a specific environment as a 2. option. + +// add you wifi network name and PassPhrase or use WiFi Manager +#ifndef STASSID +#define STASSID "ssid" +#define STAPSK "psk" +#endif + +const char *ssid = STASSID; +const char *passPhrase = STAPSK; diff --git a/libraries/ESP8266WebServer/examples/WebUpdate/WebUpdate.ino b/libraries/ESP8266WebServer/examples/WebUpdate/WebUpdate.ino index 17646fd172..728889a916 100644 --- a/libraries/ESP8266WebServer/examples/WebUpdate/WebUpdate.ino +++ b/libraries/ESP8266WebServer/examples/WebUpdate/WebUpdate.ino @@ -9,7 +9,7 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif const char* host = "esp8266-webupdate"; @@ -31,34 +31,36 @@ void setup(void) { server.sendHeader("Connection", "close"); server.send(200, "text/html", serverIndex); }); - server.on("/update", HTTP_POST, []() { - server.sendHeader("Connection", "close"); - server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK"); - ESP.restart(); - }, []() { - HTTPUpload& upload = server.upload(); - if (upload.status == UPLOAD_FILE_START) { - Serial.setDebugOutput(true); - WiFiUDP::stopAll(); - Serial.printf("Update: %s\n", upload.filename.c_str()); - uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; - if (!Update.begin(maxSketchSpace)) { //start with max available size - Update.printError(Serial); - } - } else if (upload.status == UPLOAD_FILE_WRITE) { - if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) { - Update.printError(Serial); + server.on( + "/update", HTTP_POST, []() { + server.sendHeader("Connection", "close"); + server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK"); + ESP.restart(); + }, + []() { + HTTPUpload& upload = server.upload(); + if (upload.status == UPLOAD_FILE_START) { + Serial.setDebugOutput(true); + WiFiUDP::stopAll(); + Serial.printf("Update: %s\n", upload.filename.c_str()); + uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; + if (!Update.begin(maxSketchSpace)) { // start with max available size + Update.printError(Serial); + } + } else if (upload.status == UPLOAD_FILE_WRITE) { + if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) { + Update.printError(Serial); + } + } else if (upload.status == UPLOAD_FILE_END) { + if (Update.end(true)) { // true to set the size to the current progress + Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize); + } else { + Update.printError(Serial); + } + Serial.setDebugOutput(false); } - } else if (upload.status == UPLOAD_FILE_END) { - if (Update.end(true)) { //true to set the size to the current progress - Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize); - } else { - Update.printError(Serial); - } - Serial.setDebugOutput(false); - } - yield(); - }); + yield(); + }); server.begin(); MDNS.addService("http", "tcp", 80); diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h b/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h index 9ea77718f8..dbcb251135 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h @@ -28,6 +28,7 @@ #include "FS.h" #include "base64.h" #include "detail/RequestHandlersImpl.h" +#include static const char AUTHORIZATION_HEADER[] PROGMEM = "Authorization"; static const char qop_auth[] PROGMEM = "qop=auth"; @@ -67,6 +68,13 @@ template void ESP8266WebServerTemplate::enableCORS(bool enable) { _corsEnabled = enable; } + +template +void ESP8266WebServerTemplate::enableETag(bool enable, ETagFunction fn) { + _eTagEnabled = enable; + _eTagFunction = fn; +} + template void ESP8266WebServerTemplate::begin() { close(); @@ -94,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; } @@ -222,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 +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) { - on(uri, method, fn, _fileUploadHandler); +bool ESP8266WebServerTemplate::removeRoute(const String &uri) { + return removeRoute(uri, HTTP_ANY); } 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, 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 @@ -241,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) { @@ -253,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; @@ -263,23 +346,23 @@ void ESP8266WebServerTemplate::serveStatic(const char* uri, FS& fs, file.close(); } - if(is_file) + if(is_file) { _addRequestHandler(new StaticFileRequestHandler(fs, path, uri, cache_header)); - else + } else { _addRequestHandler(new StaticDirectoryRequestHandler(fs, path, uri, cache_header)); + } } template void ESP8266WebServerTemplate::handleClient() { if (_currentStatus == HC_NONE) { - ClientType client = _server.available(); - if (!client) { + _currentClient = _server.accept(); + if (!_currentClient) { return; } DBGWS("New client\n"); - _currentClient = client; _currentStatus = HC_WAIT_READ; _statusChange = millis(); } @@ -287,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) { @@ -334,11 +440,18 @@ void ESP8266WebServerTemplate::handleClient() { } // switch _parseRequest() } else { // !_currentClient.available(): waiting for more data - if (millis() - _statusChange <= HTTP_MAX_DATA_WAIT) { - keepCurrentClient = true; + unsigned long timeSinceChange = millis() - _statusChange; + // Use faster connection drop timeout if any other client has data + // or the buffer of pending clients is full + if ((_server.hasClientData() || _server.hasMaxPendingClients()) + && timeSinceChange > HTTP_MAX_DATA_AVAILABLE_WAIT) + DBGWS("webserver: closing since there's another connection to read from\n"); + else { + if (timeSinceChange > HTTP_MAX_DATA_WAIT) + DBGWS("webserver: closing after read timeout\n"); + else + keepCurrentClient = true; } - else - DBGWS("webserver: closing after read timeout\n"); callYield = true; } break; @@ -372,7 +485,7 @@ void ESP8266WebServerTemplate::close() { _server.close(); _currentStatus = HC_NONE; if(!_headerKeysCount) - collectHeaders(0, 0); + collectHeaders(); } template @@ -435,77 +548,75 @@ void ESP8266WebServerTemplate::_prepareHeader(String& response, int sendHeader(String(F("Keep-Alive")), String(F("timeout=")) + HTTP_MAX_CLOSE_WAIT); } + response += _responseHeaders; response += "\r\n"; _responseHeaders = ""; } template -void ESP8266WebServerTemplate::send(int code, const char* content_type, const String& content) { - String header; - // Can we asume the following? - //if(code == 200 && content.length() == 0 && _contentLength == CONTENT_LENGTH_NOT_SET) - // _contentLength = CONTENT_LENGTH_UNKNOWN; - _prepareHeader(header, code, content_type, content.length()); - _currentClient.write((const uint8_t *)header.c_str(), header.length()); - if(content.length()) - sendContent(content); +void ESP8266WebServerTemplate::send(int code, char* content_type, const String& content) { + return send(code, (const char*)content_type, content); } template -void ESP8266WebServerTemplate::send_P(int code, PGM_P content_type, PGM_P content) { - size_t contentLength = 0; +void ESP8266WebServerTemplate::send(int code, const char* content_type, const String& content) { + return send(code, content_type, content.c_str(), content.length()); +} - if (content != NULL) { - contentLength = strlen_P(content); - } +template +void ESP8266WebServerTemplate::send(int code, const String& content_type, const String& content) { + return send(code, (const char*)content_type.c_str(), content); +} - String header; - char type[64]; - memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type)); - _prepareHeader(header, code, (const char* )type, contentLength); - _currentClient.write((const uint8_t *)header.c_str(), header.length()); - if (contentLength) { - sendContent_P(content); - } +template +void ESP8266WebServerTemplate::sendContent(const String& content) { + StreamConstPtr ref(content.c_str(), content.length()); + sendContent(&ref); } template -void ESP8266WebServerTemplate::send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength) { - String header; - char type[64]; - memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type)); - _prepareHeader(header, code, (const char* )type, contentLength); - _currentClient.write((const uint8_t *)header.c_str(), header.length()); - if (contentLength) { - sendContent_P(content, contentLength); - } +void ESP8266WebServerTemplate::send(int code, const char* content_type, Stream* stream, size_t content_length /*= 0*/) { + String header; + if (content_length == 0) + content_length = std::max((ssize_t)0, stream->streamRemaining()); + _prepareHeader(header, code, content_type, content_length); + size_t sent = StreamConstPtr(header).sendAll(&_currentClient); + if (sent != header.length()) + DBGWS("HTTPServer: error: sent %zd on %u bytes\n", sent, header.length()); + if (content_length) + return sendContent(stream, content_length); } template -void ESP8266WebServerTemplate::send(int code, char* content_type, const String& content) { - send(code, (const char*)content_type, content); +void ESP8266WebServerTemplate::send_P(int code, PGM_P content_type, PGM_P content) { + StreamConstPtr ref(content, strlen_P(content)); + return send(code, String(content_type).c_str(), &ref); } template -void ESP8266WebServerTemplate::send(int code, const String& content_type, const String& content) { - send(code, (const char*)content_type.c_str(), content); +void ESP8266WebServerTemplate::send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength) { + StreamConstPtr ref(content, contentLength); + return send(code, String(content_type).c_str(), &ref); } template -void ESP8266WebServerTemplate::sendContent(const String& content) { - if (_currentMethod == HTTP_HEAD) return; - const char * footer = "\r\n"; - size_t len = content.length(); +void ESP8266WebServerTemplate::sendContent(Stream* content, ssize_t content_length /* = 0*/) { + if (_currentMethod == HTTP_HEAD) + return; + if (content_length <= 0) + content_length = std::max((ssize_t)0, content->streamRemaining()); + if(_chunked) { + _currentClient.printf("%zx\r\n", content_length); + } + ssize_t sent = content->sendSize(&_currentClient, content_length); + if (sent != content_length) + { + DBGWS("HTTPServer: error: short send after timeout (%zu < %zu)\n", sent, content_length); + } if(_chunked) { - char chunkSize[11]; - sprintf(chunkSize, "%zx\r\n", len); - _currentClient.write((const uint8_t *)chunkSize, strlen(chunkSize)); - } - _currentClient.write((const uint8_t *)content.c_str(), len); - if(_chunked){ - _currentClient.write((const uint8_t *)footer, 2); - if (len == 0) { + _currentClient.printf_P(PSTR("\r\n")); + if (content_length == 0) { _chunked = false; } } @@ -518,19 +629,8 @@ void ESP8266WebServerTemplate::sendContent_P(PGM_P content) { template void ESP8266WebServerTemplate::sendContent_P(PGM_P content, size_t size) { - const char * footer = "\r\n"; - if(_chunked) { - char chunkSize[11]; - sprintf(chunkSize, "%zx\r\n", size); - _currentClient.write((const uint8_t *)chunkSize, strlen(chunkSize)); - } - _currentClient.write_P(content, size); - if(_chunked){ - _currentClient.write((const uint8_t *)footer, 2); - if (size == 0) { - _chunked = false; - } - } + StreamConstPtr ptr(content, size); + return sendContent(&ptr, size); } template @@ -557,7 +657,7 @@ void ESP8266WebServerTemplate::_streamFileCore(const size_t fileSize } template -const String& ESP8266WebServerTemplate::pathArg(unsigned int i) const { +const String& ESP8266WebServerTemplate::pathArg(unsigned int i) const { if (_currentHandler != nullptr) return _currentHandler->pathArg(i); return emptyString; @@ -565,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; @@ -597,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; @@ -608,7 +700,6 @@ bool ESP8266WebServerTemplate::hasArg(const String& name) const { return false; } - template const String& ESP8266WebServerTemplate::header(const String& name) const { for (int i = 0; i < _headerKeysCount; ++i) { @@ -618,21 +709,30 @@ const String& ESP8266WebServerTemplate::header(const String& name) c return emptyString; } - template void ESP8266WebServerTemplate::collectHeaders(const char* headerKeys[], const size_t headerKeysCount) { - _headerKeysCount = headerKeysCount + 2; - if (_currentHeaders){ + if (_currentHeaders) delete[] _currentHeaders; - } - _currentHeaders = new RequestArgument[_headerKeysCount]; + _currentHeaders = new RequestArgument[_headerKeysCount = headerKeysCount + 2]; _currentHeaders[0].key = FPSTR(AUTHORIZATION_HEADER); _currentHeaders[1].key = FPSTR(ETAG_HEADER); for (int i = 2; i < _headerKeysCount; i++){ - _currentHeaders[i].key = headerKeys[i-2]; + _currentHeaders[i].key = headerKeys[i - 2]; } } +template +template +void ESP8266WebServerTemplate::collectHeaders(const Args&... args) { + if (_currentHeaders) + delete[] _currentHeaders; + _currentHeaders = new RequestArgument[_headerKeysCount = sizeof...(args) + 2] { + { .key = FPSTR(AUTHORIZATION_HEADER), .value = emptyString }, + { .key = FPSTR(ETAG_HEADER), .value = emptyString }, + { .key = args, .value = emptyString } ... + }; +} + template const String& ESP8266WebServerTemplate::header(int i) const { if (i < _headerKeysCount) @@ -694,16 +794,18 @@ void ESP8266WebServerTemplate::_handleRequest() { } if (!handled) { using namespace mime; - send(404, String(FPSTR(mimeTable[html].mimeType)), String(F("Not found: ")) + _currentUri); + send(404, FPSTR(mimeTable[html].mimeType), String(F("Not found: ")) + _currentUri); handled = true; } if (handled) { _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 a8453b42a2..f9eca1c8bc 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.h @@ -59,6 +59,7 @@ enum HTTPAuthMethod { BASIC_AUTH, DIGEST_AUTH }; #endif #define HTTP_MAX_DATA_WAIT 5000 //ms to wait for the client to send the request +#define HTTP_MAX_DATA_AVAILABLE_WAIT 30 //ms to wait for the client to send the request when there is another client with data available #define HTTP_MAX_POST_WAIT 5000 //ms to wait for POST data to arrive #define HTTP_MAX_SEND_WAIT 5000 //ms to wait for data chunk to be ACKed #define HTTP_MAX_CLOSE_WAIT 2000 //ms to wait for the client to close the connection @@ -114,14 +115,23 @@ class ESP8266WebServerTemplate void requestAuthentication(HTTPAuthMethod mode = BASIC_AUTH, const char* realm = NULL, const String& authFailMsg = String("") ); typedef std::function THandlerFunction; - 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 ETagFunction; + 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 void enableCORS(bool enable); + void enableETag(bool enable, ETagFunction fn = nullptr); const String& uri() const { return _currentUri; } HTTPMethod method() const { return _currentMethod; } @@ -138,6 +148,8 @@ class ESP8266WebServerTemplate int args() const; // get arguments count bool hasArg(const String& name) const; // check if argument exists void collectHeaders(const char* headerKeys[], const size_t headerKeysCount); // set the request headers to collect + template + void collectHeaders(const Args&... args); // set the request headers to collect (variadic template version) const String& header(const String& name) const; // get request header value by name const String& header(int i) const; // get request header value by number const String& headerName(int i) const; // get request header name by number @@ -149,7 +161,7 @@ class ESP8266WebServerTemplate // code - HTTP response code, can be 200 or 404 // content_type - HTTP content type, like "text/plain" or "image/png" // content - actual content body - void send(int code, const char* content_type = NULL, const String& content = String("")); + void send(int code, const char* content_type = NULL, const String& content = emptyString); void send(int code, char* content_type, const String& content); void send(int code, const String& content_type, const String& content); void send(int code, const char *content_type, const char *content) { @@ -164,14 +176,25 @@ class ESP8266WebServerTemplate void send_P(int code, PGM_P content_type, PGM_P content); 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) { + send(code, content_type, &stream, content_length); + } + void setContentLength(const size_t contentLength); void sendHeader(const String& name, const String& value, bool first = false); void sendContent(const String& content); + void sendContent(String& content) { + sendContent((const String&)content); + } void sendContent_P(PGM_P content); void sendContent_P(PGM_P content, size_t size); void sendContent(const char *content) { sendContent_P(content); } void sendContent(const char *content, size_t size) { sendContent_P(content, size); } + void sendContent(Stream* content, ssize_t content_length = 0); + void sendContent(Stream& content, ssize_t content_length = 0) { sendContent(&content, content_length); } + bool chunkedResponseModeStart_P (int code, PGM_P content_type) { if (_currentVersion == 0) // no chunk mode in HTTP/1.0 @@ -190,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. @@ -215,11 +262,35 @@ class ESP8266WebServerTemplate size_t contentLength = 0; _streamFileCore(file.size(), file.name(), contentType); if (requestMethod == HTTP_GET) { - contentLength = _currentClient.write(file); + contentLength = file.sendAll(_currentClient); } return contentLength; } + // Implement GET and HEAD requests for stream + // Stream body on HTTP_GET but not on HTTP_HEAD requests. + template + size_t stream(T &aStream, const String& contentType, HTTPMethod requestMethod, ssize_t size) { + setContentLength(size); + send(200, contentType, emptyString); + if (requestMethod == HTTP_GET) + size = aStream.sendSize(_currentClient, size); + return size; + } + + // Implement GET and HEAD requests for stream + // Stream body on HTTP_GET but not on HTTP_HEAD requests. + template + size_t stream(T& aStream, const String& contentType, HTTPMethod requestMethod = HTTP_GET) { + ssize_t size = aStream.size(); + if (size < 0) + { + send(500, F("text/html"), F("input stream: undetermined size")); + return 0; + } + return stream(aStream, contentType, requestMethod, size); + } + static String responseCodeToString(const int code); void addHook (HookFunction hook) { @@ -236,8 +307,12 @@ class ESP8266WebServerTemplate } } + bool _eTagEnabled = false; + ETagFunction _eTagFunction = nullptr; + protected: void _addRequestHandler(RequestHandlerType* handler); + bool _removeRequestHandler(RequestHandlerType *handler); void _handleRequest(); void _finalizeResponse(); ClientFuture _parseRequest(ClientType& client); @@ -279,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; @@ -308,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 e50ea727cc..672682706e 100644 --- a/libraries/ESP8266WebServer/src/Parsing-impl.h +++ b/libraries/ESP8266WebServer/src/Parsing-impl.h @@ -37,22 +37,8 @@ namespace esp8266webserver { template static bool readBytesWithTimeout(typename ServerType::ClientType& client, size_t maxLength, String& data, int timeout_ms) { - if (!data.reserve(maxLength + 1)) - return false; - data[0] = 0; // data.clear()?? - while (data.length() < maxLength) { - int tries = timeout_ms; - size_t avail; - while (!(avail = client.available()) && tries--) - delay(1); - if (!avail) - break; - if (data.length() + avail > maxLength) - avail = maxLength - data.length(); - while (avail--) - data += (char)client.read(); - } - return data.length() == maxLength; + S2Stream dataStream(data); + return client.sendSize(dataStream, maxLength, timeout_ms) == maxLength; } template @@ -120,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; @@ -196,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; @@ -338,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; @@ -372,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; @@ -422,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; @@ -440,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; @@ -478,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(), @@ -502,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()); @@ -562,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 c88c830320..fb36c5aba4 100644 --- a/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h +++ b/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h @@ -9,6 +9,26 @@ namespace esp8266webserver { +// calculate an ETag for a file in filesystem based on md5 checksum +// that can be used in the http headers - include quotes. +static String calcETag(FS &fs, const String &path) { + String result; + + // calculate eTag using md5 checksum + uint8_t md5_buf[16]; + File f = fs.open(path, "r"); + MD5Builder calcMD5; + calcMD5.begin(); + calcMD5.addStream(f, f.size()); + calcMD5.calculate(); + calcMD5.getBytes(md5_buf); + f.close(); + // create a minimal-length eTag using base64 byte[]->text encoding. + result = "\"" + base64::encode(md5_buf, 16, false) + "\""; + return(result); +} // calcETag + + template class FunctionRequestHandler : public RequestHandler { using WebServerType = ESP8266WebServerTemplate; @@ -39,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(); @@ -49,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; }; @@ -92,9 +132,9 @@ class StaticRequestHandler : public RequestHandler { }; +// serve all files within a given directory template class StaticDirectoryRequestHandler : public StaticRequestHandler { - using SRH = StaticRequestHandler; using WebServerType = ESP8266WebServerTemplate; @@ -109,14 +149,18 @@ 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()); String path; + String eTagCode; path.reserve(SRH::_path.length() + requestUri.length() + 32); path = SRH::_path; @@ -156,17 +200,43 @@ class StaticDirectoryRequestHandler : public StaticRequestHandler { return false; } + if (server._eTagEnabled) { + if (server._eTagFunction) { + eTagCode = (server._eTagFunction)(SRH::_fs, path); + } else { + eTagCode = esp8266webserver::calcETag(SRH::_fs, path); + } + + if (server.header("If-None-Match") == eTagCode) { + server.send(304); + return true; + } + } + if (SRH::_cache_header.length() != 0) server.sendHeader("Cache-Control", SRH::_cache_header); + if ((server._eTagEnabled) && (eTagCode.length() > 0)) { + server.sendHeader("ETag", eTagCode); + } + server.streamFile(f, contentType, requestMethod); + return true; } + StaticDirectoryRequestHandler& setFilter(typename WebServerType::FilterFunction filter) { + _filter = filter; + return *this; + } + protected: size_t _baseUriLength; + typename WebServerType::FilterFunction _filter; }; + +// Serve a specific, single file template class StaticFileRequestHandler : @@ -180,28 +250,31 @@ public StaticRequestHandler { : StaticRequestHandler{fs, path, uri, cache_header} { - File f = SRH::_fs.open(path, "r"); - MD5Builder calcMD5; - calcMD5.begin(); - calcMD5.addStream(f, f.size()); - calcMD5.calculate(); - calcMD5.getBytes(_ETag_md5); - f.close(); } bool canHandle(HTTPMethod requestMethod, const String& requestUri) override { 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; - const String etag = "\"" + base64::encode(_ETag_md5, 16, false) + "\""; - - if(server.header("If-None-Match") == etag){ - server.send(304); - return true; + if (server._eTagEnabled) { + if (server._eTagFunction) { + _eTagCode = (server._eTagFunction)(SRH::_fs, SRH::_path); + } else if (_eTagCode.isEmpty()) { + _eTagCode = esp8266webserver::calcETag(SRH::_fs, SRH::_path); + } + + if (server.header("If-None-Match") == _eTagCode) { + server.send(304); + return true; + } } File f = SRH::_fs.open(SRH::_path, "r"); @@ -217,14 +290,22 @@ public StaticRequestHandler { if (SRH::_cache_header.length() != 0) server.sendHeader("Cache-Control", SRH::_cache_header); - server.sendHeader("ETag", etag); + if ((server._eTagEnabled) && (_eTagCode.length() > 0)) { + server.sendHeader("ETag", _eTagCode); + } server.streamFile(f, mime::getContentType(SRH::_path), requestMethod); return true; } + StaticFileRequestHandler& setFilter(typename WebServerType::FilterFunction filter) { + _filter = filter; + return *this; + } + protected: - uint8_t _ETag_md5[16]; + 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 a599a0395a..f96a4ccf83 100644 --- a/libraries/ESP8266WiFi/examples/BearSSL_CertStore/BearSSL_CertStore.ino +++ b/libraries/ESP8266WiFi/examples/BearSSL_CertStore/BearSSL_CertStore.ino @@ -41,7 +41,7 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif const char *ssid = STASSID; @@ -72,13 +72,10 @@ void setClock() { // Try and connect using a WiFiClientBearSSL to specified host:port and dump URL void fetchURL(BearSSL::WiFiClientSecure *client, const char *host, const uint16_t port, const char *path) { - if (!path) { - path = "/"; - } + 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; } @@ -90,15 +87,13 @@ 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); - int rlen = client->read((uint8_t*)tmp, sizeof(tmp) - 1); + int rlen = client->read((uint8_t *)tmp, sizeof(tmp) - 1); yield(); - if (rlen < 0) { - break; - } + if (rlen < 0) { break; } // Only print out first line up to \r, then abort connection char *nl = strchr(tmp, '\r'); if (nl) { @@ -136,13 +131,13 @@ void setup() { Serial.println("IP address: "); Serial.println(WiFi.localIP()); - setClock(); // Required for X.509 validation + setClock(); // Required for X.509 validation int numCerts = certStore.initCertStore(LittleFS, PSTR("/certs.idx"), PSTR("/certs.ar")); Serial.printf("Number of CA certs read: %d\n", numCerts); if (numCerts == 0) { Serial.printf("No certs found. Did you run certs-from-mozilla.py and upload the LittleFS directory before running?\n"); - return; // Can't connect to anything w/o certs! + return; // Can't connect to anything w/o certs! } BearSSL::WiFiClientSecure *bear = new BearSSL::WiFiClientSecure(); @@ -156,9 +151,7 @@ void setup() { void loop() { Serial.printf("\nPlease enter a website address (www.blah.com) to connect to: "); String site; - do { - site = Serial.readString(); - } while (site == ""); + do { site = Serial.readString(); } while (site == ""); // Strip newline if present site.replace(String("\r"), emptyString); site.replace(String("\n"), emptyString); @@ -170,4 +163,3 @@ void loop() { fetchURL(bear, site.c_str(), 443, "/"); delete bear; } - 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 62b5f03343..84579d549d 100644 --- a/libraries/ESP8266WiFi/examples/BearSSL_MaxFragmentLength/BearSSL_MaxFragmentLength.ino +++ b/libraries/ESP8266WiFi/examples/BearSSL_MaxFragmentLength/BearSSL_MaxFragmentLength.ino @@ -9,26 +9,24 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif 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); do { char tmp[32]; - int rlen = client->read((uint8_t*)tmp, sizeof(tmp) - 1); + int rlen = client->read((uint8_t *)tmp, sizeof(tmp) - 1); yield(); - if (rlen < 0) { - break; - } + if (rlen < 0) { break; } if (rlen == 0) { - delay(10); // Give background processes some time + delay(10); // Give background processes some time continue; } tmp[rlen] = '\0'; @@ -41,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); @@ -73,20 +70,17 @@ int fetchMaxFragmentLength() { // returns true then you can use the ::setBufferSizes(rx, tx) to shrink // the needed BearSSL memory while staying within protocol limits. // - // If MFLN is not supported, you may still be able to mimimize the buffer + // If MFLN is not supported, you may still be able to minimize the buffer // sizes assuming you can ensure the server never transmits fragments larger // than the size (i.e. by using HTTP GET RANGE methods, etc.). 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 (mfln) { client.setBufferSizes(512, 512); } + 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 972f0ebabd..598a0adc77 100644 --- a/libraries/ESP8266WiFi/examples/BearSSL_Server/BearSSL_Server.ino +++ b/libraries/ESP8266WiFi/examples/BearSSL_Server/BearSSL_Server.ino @@ -39,7 +39,7 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif const char *ssid = STASSID; @@ -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"; +#define USING_INSECURE_CERTS_AND_KEYS_AND_CAS 1 +#include -#endif - -#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 developper tools of your browser. -//#define DYNAMIC_CACHE // Whether to dynamically allocate the cache. +#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. #if defined(USE_CACHE) && defined(DYNAMIC_CACHE) // Dynamically allocated cache. @@ -181,7 +96,7 @@ void setup() { #ifndef USE_EC server.setRSACert(serverCertList, serverPrivKey); #else - server.setECCert(serverCertList, BR_KEYTYPE_KEYX|BR_KEYTYPE_SIGN, serverPrivKey); + server.setECCert(serverCertList, BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN, serverPrivKey); #endif // Set the server's cache @@ -193,31 +108,28 @@ void setup() { server.begin(); } -static const char *HTTP_RES = - "HTTP/1.0 200 OK\r\n" - "Connection: close\r\n" - "Content-Length: 62\r\n" - "Content-Type: text/html; charset=iso-8859-1\r\n" - "\r\n" - "\r\n" - "\r\n" - "

Hello from ESP8266!

\r\n" - "\r\n" - "\r\n"; +static const char *HTTP_RES = "HTTP/1.0 200 OK\r\n" + "Connection: close\r\n" + "Content-Length: 62\r\n" + "Content-Type: text/html; charset=iso-8859-1\r\n" + "\r\n" + "\r\n" + "\r\n" + "

Hello from ESP8266!

\r\n" + "\r\n" + "\r\n"; void loop() { static int cnt; - BearSSL::WiFiClientSecure incoming = server.available(); - if (!incoming) { - return; - } - Serial.printf("Incoming connection...%d\n",cnt++); - + BearSSL::WiFiClientSecure incoming = server.accept(); + if (!incoming) { return; } + Serial.printf("Incoming connection...%d\n", cnt++); + // Ugly way to wait for \r\n (i.e. end of HTTP request which we don't actually parse here) - uint32_t timeout=millis() + 1000; + uint32_t timeout = millis() + 1000; int lcwn = 0; for (;;) { - unsigned char x=0; + unsigned char x = 0; if ((millis() > timeout) || (incoming.available() && incoming.read(&x, 1) < 0)) { incoming.stop(); Serial.printf("Connection error, closed\n"); @@ -228,14 +140,12 @@ void loop() { } else if (x == 0x0D) { continue; } else if (x == 0x0A) { - if (lcwn) { - break; - } + if (lcwn) { break; } lcwn = 1; } else lcwn = 0; } - incoming.write((uint8_t*)HTTP_RES, strlen(HTTP_RES)); + incoming.write((uint8_t *)HTTP_RES, strlen(HTTP_RES)); incoming.flush(); incoming.stop(); Serial.printf("Connection closed.\n"); 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 a50f115283..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. */ @@ -67,92 +68,23 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif 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 @@ -160,8 +92,7 @@ seoK24dHmt6tWmn/sbxX7Aa6TL/4mVlFoOgcaTJyVaY/BrY= // head of the app. // Set time via NTP, as required for x.509 validation -void setClock() -{ +void setClock() { configTime(3 * 3600, 0, "pool.ntp.org", "time.nist.gov"); Serial.print("Waiting for NTP time sync: "); @@ -199,7 +130,7 @@ void setup() { Serial.println("IP address: "); Serial.println(WiFi.localIP()); - setClock(); // Required for X.509 validation + setClock(); // Required for X.509 validation // Attach the server private cert/key combo BearSSL::X509List *serverCertList = new BearSSL::X509List(server_cert); @@ -212,32 +143,33 @@ 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" - "Connection: close\r\n" - "Content-Length: 59\r\n" - "Content-Type: text/html; charset=iso-8859-1\r\n" - "\r\n" - "\r\n" - "\r\n" - "

Hello my friend!

\r\n" - "\r\n" - "\r\n"; +static const char *HTTP_RES = "HTTP/1.0 200 OK\r\n" + "Connection: close\r\n" + "Content-Length: 59\r\n" + "Content-Type: text/html; charset=iso-8859-1\r\n" + "\r\n" + "\r\n" + "\r\n" + "

Hello my friend!

\r\n" + "\r\n" + "\r\n"; void loop() { - BearSSL::WiFiClientSecure incoming = server.available(); - if (!incoming) { - return; - } + BearSSL::WiFiClientSecure incoming = server.accept(); + if (!incoming) { return; } Serial.println("Incoming connection...\n"); - + // Ugly way to wait for \r\n (i.e. end of HTTP request which we don't actually parse here) - uint32_t timeout=millis() + 1000; + uint32_t timeout = millis() + 1000; int lcwn = 0; for (;;) { - unsigned char x=0; + unsigned char x = 0; if ((millis() > timeout) || (incoming.available() && incoming.read(&x, 1) < 0)) { incoming.stop(); Serial.printf("Connection error, closed\n"); @@ -248,14 +180,12 @@ void loop() { } else if (x == 0x0D) { continue; } else if (x == 0x0A) { - if (lcwn) { - break; - } + if (lcwn) { break; } lcwn = 1; } else lcwn = 0; } - incoming.write((uint8_t*)HTTP_RES, strlen(HTTP_RES)); + incoming.write((uint8_t *)HTTP_RES, strlen(HTTP_RES)); incoming.flush(); incoming.stop(); Serial.printf("Connection closed.\n"); diff --git a/libraries/ESP8266WiFi/examples/BearSSL_Sessions/BearSSL_Sessions.ino b/libraries/ESP8266WiFi/examples/BearSSL_Sessions/BearSSL_Sessions.ino index d615497073..cf227403f3 100644 --- a/libraries/ESP8266WiFi/examples/BearSSL_Sessions/BearSSL_Sessions.ino +++ b/libraries/ESP8266WiFi/examples/BearSSL_Sessions/BearSSL_Sessions.ino @@ -1,22 +1,28 @@ // Example of using SSL sessions to speed up SSL connection initiation // +// Note that sessions are a function of individual HTTPS servers, so if you +// are connecting to a service through a load abalncer (i.e. Azure, AWS, GitHub) +// two connections to the same IP address will generally connect to two +// different web servers, meaning that sessions won't work. If you are +// connecting to a single server not behind a load balancer/etc., however, +// there should be a significant speedup. +// // September 2018 by Earle F. Philhower, III // Released to the public domain #include #include +#include "certs.h" #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif const char *ssid = STASSID; const char *pass = STAPSK; -const char * host = "api.github.com"; -const uint16_t port = 443; -const char * path = "/"; +const char *path = "/"; void setup() { Serial.begin(115200); @@ -54,13 +60,10 @@ void setup() { // Try and connect using a WiFiClientBearSSL to specified host:port and dump HTTP response void fetchURL(BearSSL::WiFiClientSecure *client, const char *host, const uint16_t port, const char *path) { - if (!path) { - path = "/"; - } + 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; } @@ -72,15 +75,13 @@ 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); - int rlen = client->read((uint8_t*)tmp, sizeof(tmp) - 1); + int rlen = client->read((uint8_t *)tmp, sizeof(tmp) - 1); yield(); - if (rlen < 0) { - break; - } + if (rlen < 0) { break; } // Only print out first line up to \r, then abort connection char *nl = strchr(tmp, '\r'); if (nl) { @@ -97,34 +98,11 @@ void fetchURL(BearSSL::WiFiClientSecure *client, const char *host, const uint16_ void loop() { - static const char digicert[] PROGMEM = R"EOF( ------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 ------END CERTIFICATE----- -)EOF"; uint32_t start, finish; BearSSL::WiFiClientSecure client; - BearSSL::X509List cert(digicert); + BearSSL::X509List cert(certForum); + const char *host = "esp8266.com"; + const int port = 443; Serial.printf("Connecting without sessions..."); start = millis(); @@ -135,7 +113,7 @@ vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep BearSSL::Session session; client.setSession(&session); - Serial.printf("Connecting with an unitialized session..."); + Serial.printf("Connecting with an uninitialized session..."); start = millis(); client.setTrustAnchors(&cert); fetchURL(&client, host, port, path); @@ -156,6 +134,5 @@ vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep finish = millis(); Serial.printf("Total time: %dms\n", finish - start); - delay(10000); // Avoid DDOSing github + delay(10000); // Avoid DDOSing github } - diff --git a/libraries/ESP8266WiFi/examples/BearSSL_Sessions/certs.h b/libraries/ESP8266WiFi/examples/BearSSL_Sessions/certs.h new file mode 100644 index 0000000000..2d21efdf3a --- /dev/null +++ b/libraries/ESP8266WiFi/examples/BearSSL_Sessions/certs.h @@ -0,0 +1,34 @@ +const char certForum [] PROGMEM = R"EOF( +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- +)EOF"; + diff --git a/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino b/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino index 9b02afd986..2f6c08a51a 100644 --- a/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino +++ b/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino @@ -8,18 +8,21 @@ #include #include #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" +#define STAPSK "your-password" #endif const char *ssid = STASSID; const char *pass = STAPSK; -const char * host = "api.github.com"; -const uint16_t port = 443; -const char * path = "/"; +const char *path = "/"; // Set time via NTP, as required for x.509 validation void setClock() { @@ -41,15 +44,12 @@ void setClock() { // Try and connect using a WiFiClientBearSSL to specified host:port and dump HTTP response void fetchURL(BearSSL::WiFiClientSecure *client, const char *host, const uint16_t port, const char *path) { - if (!path) { - path = "/"; - } + if (!path) { path = "/"; } 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; } @@ -61,15 +61,13 @@ 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); - int rlen = client->read((uint8_t*)tmp, sizeof(tmp) - 1); + int rlen = client->read((uint8_t *)tmp, sizeof(tmp) - 1); yield(); - if (rlen < 0) { - break; - } + if (rlen < 0) { break; } // Only print out first line up to \r, then abort connection char *nl = strchr(tmp, '\r'); if (nl) { @@ -92,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, host, port, path); + fetchURL(&client, SSL_host, SSL_port, path); } void fetchInsecure() { @@ -103,7 +101,7 @@ which is subject to man-in-the-middle (MITM) attacks. )EOF"); BearSSL::WiFiClientSecure client; client.setInsecure(); - fetchURL(&client, host, port, path); + fetchURL(&client, SSL_host, SSL_port, path); } void fetchFingerprint() { @@ -116,9 +114,8 @@ fingerprints will change if anything changes in the certificate chain the root authorities, etc.). )EOF"); BearSSL::WiFiClientSecure client; - static const char fp[] PROGMEM = "59:74:61:88:13:CA:12:34:15:4D:11:0A:C1:7F:E6:67:07:69:42:F5"; - client.setFingerprint(fp); - fetchURL(&client, host, port, path); + client.setFingerprint(FINGERPRINT); + fetchURL(&client, SSL_host, SSL_port, path); } void fetchSelfSigned() { @@ -142,51 +139,13 @@ needs to be paired with the private key of the site, which is obviously private and not shared. A MITM without the private key would not be able to establish communications. )EOF"); - // Extracted by: openssl x509 -pubkey -noout -in servercert.pem - static const char pubkey[] PROGMEM = R"KEY( ------BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy+3Up8qBkIn/7S9AfWlH -Od8SdXmnWx+JCIHvnWzjFcLeLvQb2rMqqCDL5XDlvkyC5SZ8ZyLITemej5aJYuBv -zcKPzyZ0QfYZiskU9nzL2qBQj8alzJJ/Cc32AWuuWrPrzVxBmOEW9gRCGFCD3m0z -53y6GjcmBS2wcX7RagqbD7g2frEGko4G7kmW96H6dyh2j9Rou8TwAK6CnbiXPAM/ -5Q6dyfdYlHOCgP75F7hhdKB5gpprm9A/OnQsmZjUPzy4u0EKCxE8MfhBerZrZdod -88ZdDG3CvTgm050bc+lGlbsT+s09lp0dgxSZIeI8+syV2Owt4YF/PdjeeymtzQdI -wQIDAQAB ------END PUBLIC KEY----- -)KEY"; BearSSL::WiFiClientSecure client; - BearSSL::PublicKey key(pubkey); + BearSSL::PublicKey key(PUBKEY); client.setKnownKey(&key); - fetchURL(&client, host, port, path); + fetchURL(&client, SSL_host, SSL_port, path); } void fetchCertAuthority() { - static const char digicert[] PROGMEM = R"EOF( ------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 ------END CERTIFICATE----- -)EOF"; - Serial.printf(R"EOF( A specific certification authority can be passed in and used to validate a chain of certificates from a given server. These will be validated @@ -197,14 +156,14 @@ BearSSL does verify the notValidBefore/After fields. )EOF"); BearSSL::WiFiClientSecure client; - BearSSL::X509List cert(digicert); + BearSSL::X509List cert(CERT); client.setTrustAnchors(&cert); Serial.printf("Try validating without setting the time (should fail)\n"); - fetchURL(&client, host, port, path); + fetchURL(&client, SSL_host, SSL_port, path); Serial.printf("Try again after setting NTP time (should pass)\n"); setClock(); - fetchURL(&client, host, port, path); + fetchURL(&client, SSL_host, SSL_port, path); } void fetchFaster() { @@ -215,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, host, 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, host, 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, host, 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 new file mode 100755 index 0000000000..58468ae7a7 --- /dev/null +++ b/libraries/ESP8266WiFi/examples/BearSSL_Validation/certUpdate @@ -0,0 +1,2 @@ +cd ${0%/*} 2>/dev/null +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 new file mode 100644 index 0000000000..c6b99041a4 --- /dev/null +++ b/libraries/ESP8266WiFi/examples/BearSSL_Validation/certs.h @@ -0,0 +1,98 @@ + +// this file is autogenerated - any modification will be overwritten +// unused symbols will not be linked in the final binary +// generated on 2024-07-30 23:28:11 +// by ['../../../../tools/cert.py', '-s', 'www.example.com', '-n', 'SSL'] + +#pragma once + +//////////////////////////////////////////////////////////// +// certificate chain for www.example.com:443 + +const char* SSL_host = "www.example.com"; +const uint16_t SSL_port = 443; + +// 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----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhoUPuw75yl/Z9eAKMiwz +2aoOBymoLwiteL3CBr/3LSumpyc9U6ZMw0uyJ3cg1sFUSbgI2vlwqWH2skmdaVfa ++20kNHIuR/AEP52xW+K8ZjFZMuapfr/UsNRk9WvKe/9yW16a2D/UBrLzyNyPZlpG +hGaoGBV5pwjOBTz7OYnvbfpOcVJ7t+SgpJyWwGE9pApwTcOOzW6zMmzyx0QJBN2g +Vf0jpSB4soVe2DutF/+Fxbl0jTO5uFdutbxpZdsLPJJVmfRztGQkymdMKJnM3Gc9 +eccWnCvmq6qqNXI39oEqSOg/Thmav55GqjKT/6WyWrSxLx5phJIdsLmNr/IxbJWG +8wIDAQAB +-----END PUBLIC KEY----- +)PUBKEY"; + +// http://cacerts.digicert.com/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----- +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://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----- +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.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/EarlyDisableWiFi/EarlyDisableWiFi.ino b/libraries/ESP8266WiFi/examples/EarlyDisableWiFi/EarlyDisableWiFi.ino deleted file mode 100644 index 2025246f3d..0000000000 --- a/libraries/ESP8266WiFi/examples/EarlyDisableWiFi/EarlyDisableWiFi.ino +++ /dev/null @@ -1,59 +0,0 @@ - -#include -#include - -#ifndef STASSID -#define STASSID "your-ssid" -#define STAPSK "your-password" -#endif - -// preinit() is called before system startup -// from nonos-sdk's user entry point user_init() - -void preinit() { - // Global WiFi constructors are not called yet - // (global class instances like WiFi, Serial... are not yet initialized).. - // No global object methods or C++ exceptions can be called in here! - //The below is a static class method, which is similar to a function, so it's ok. - ESP8266WiFiClass::preinitWiFiOff(); -} - -void setup() { - Serial.begin(115200); - Serial.setDebugOutput(true); - Serial.println("sleeping 5s"); - - // during this period, a simple amp meter shows - // an average of 20mA with a Wemos D1 mini - // a DSO is needed to check #2111 - delay(5000); - - Serial.println("waking WiFi up, sleeping 5s"); - WiFi.forceSleepWake(); - - // amp meter raises to 75mA - delay(5000); - - Serial.println("connecting to AP " STASSID); - WiFi.mode(WIFI_STA); - WiFi.begin(STASSID, STAPSK); - - for (bool configured = false; !configured;) { - for (auto addr : addrList) - if ((configured = !addr.isLocal() && addr.ifnumber() == STATION_IF)) { - Serial.printf("STA: IF='%s' hostname='%s' addr= %s\n", - addr.ifname().c_str(), - addr.ifhostname(), - addr.toString().c_str()); - break; - } - Serial.print('.'); - delay(500); - } - - // amp meter cycles within 75-80 mA - -} - -void loop() { -} diff --git a/libraries/ESP8266WiFi/examples/HTTPSRequest/HTTPSRequest.ino b/libraries/ESP8266WiFi/examples/HTTPSRequest/HTTPSRequest.ino index 82374b40cc..750abfa733 100644 --- a/libraries/ESP8266WiFi/examples/HTTPSRequest/HTTPSRequest.ino +++ b/libraries/ESP8266WiFi/examples/HTTPSRequest/HTTPSRequest.ino @@ -14,44 +14,17 @@ #include #include +#include "certs.h" + #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif const char* ssid = STASSID; const char* password = STAPSK; -const char* host = "api.github.com"; -const int httpsPort = 443; - -// DigiCert High Assurance EV Root CA -const char trustRoot[] PROGMEM = R"EOF( ------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 ------END CERTIFICATE----- -)EOF"; -X509List cert(trustRoot); +X509List cert(cert_Sectigo_ECC_Domain_Validation_Secure_Server_CA); void setup() { Serial.begin(115200); @@ -88,12 +61,12 @@ void setup() { // Use WiFiClientSecure class to create TLS connection WiFiClientSecure client; Serial.print("Connecting to "); - Serial.println(host); + Serial.println(github_host); - Serial.printf("Using certificate: %s\n", trustRoot); + Serial.printf("Using certificate: %s\n", cert_Sectigo_ECC_Domain_Validation_Secure_Server_CA); client.setTrustAnchors(&cert); - if (!client.connect(host, httpsPort)) { + if (!client.connect(github_host, github_port)) { Serial.println("Connection failed"); return; } @@ -102,13 +75,10 @@ void setup() { Serial.print("Requesting URL: "); Serial.println(url); - client.print(String("GET ") + url + " HTTP/1.1\r\n" + - "Host: " + host + "\r\n" + - "User-Agent: BuildFailureDetectorESP8266\r\n" + - "Connection: close\r\n\r\n"); + 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"); @@ -117,7 +87,7 @@ void setup() { } String line = client.readStringUntil('\n'); if (line.startsWith("{\"state\":\"success\"")) { - Serial.println("esp8266/Arduino CI successfull!"); + Serial.println("esp8266/Arduino CI successful!"); } else { Serial.println("esp8266/Arduino CI has failed"); } @@ -128,5 +98,4 @@ void setup() { Serial.println("Closing connection"); } -void loop() { -} +void loop() {} diff --git a/libraries/ESP8266WiFi/examples/HTTPSRequest/certUpdate b/libraries/ESP8266WiFi/examples/HTTPSRequest/certUpdate new file mode 100755 index 0000000000..ba08b87c32 --- /dev/null +++ b/libraries/ESP8266WiFi/examples/HTTPSRequest/certUpdate @@ -0,0 +1,2 @@ +cd ${0%/*} 2>/dev/null +python3 ../../../../tools/cert.py -s api.github.com -n github > certs.h diff --git a/libraries/ESP8266WiFi/examples/HTTPSRequest/certs.h b/libraries/ESP8266WiFi/examples/HTTPSRequest/certs.h new file mode 100644 index 0000000000..97012d7a59 --- /dev/null +++ b/libraries/ESP8266WiFi/examples/HTTPSRequest/certs.h @@ -0,0 +1,88 @@ + +// this file is autogenerated - any modification will be overwritten +// unused symbols will not be linked in the final binary +// generated on 2024-07-30 23:28:12 +// by ['../../../../tools/cert.py', '-s', 'api.github.com', '-n', 'github'] + +#pragma once + +//////////////////////////////////////////////////////////// +// certificate chain for api.github.com:443 + +const char* github_host = "api.github.com"; +const uint16_t github_port = 443; + +// CN: *.github.com => name: __github_com +// 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----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEcAMYSUSbAQpBM6MJN5kRD5gVpxvK +QgpD4jQ4jY1CqNOeWP7fOkn+PxdiJq76Qv5bPmv3tTxD6plhoNDYDohvMg== +-----END PUBLIC KEY----- +)PUBKEY"; + +// 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----- +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://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----- +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"; + + +// end of certificate chain for api.github.com:443 +//////////////////////////////////////////////////////////// + diff --git a/libraries/ESP8266WiFi/examples/IPv6/IPv6.ino b/libraries/ESP8266WiFi/examples/IPv6/IPv6.ino index 48bd5a449b..a3d6d58751 100644 --- a/libraries/ESP8266WiFi/examples/IPv6/IPv6.ino +++ b/libraries/ESP8266WiFi/examples/IPv6/IPv6.ino @@ -23,12 +23,12 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif -#define FQDN F("www.google.com") // with both IPv4 & IPv6 addresses -#define FQDN2 F("www.yahoo.com") // with both IPv4 & IPv6 addresses -#define FQDN6 F("ipv6.google.com") // does not resolve in IPv4 +#define FQDN F("www.google.com") // with both IPv4 & IPv6 addresses +#define FQDN2 F("www.yahoo.com") // with both IPv4 & IPv6 addresses +#define FQDN6 F("ipv6.google.com") // does not resolve in IPv4 #define STATUSDELAY_MS 10000 #define TCP_PORT 23 #define UDP_PORT 23 @@ -71,29 +71,17 @@ void status(Print& out) { for (int i = 0; i < DNS_MAX_SERVERS; i++) { IPAddress dns = WiFi.dnsIP(i); - if (dns.isSet()) { - out.printf("dns%d: %s\n", i, dns.toString().c_str()); - } + if (dns.isSet()) { out.printf("dns%d: %s\n", i, dns.toString().c_str()); } } out.println(F("Try me at these addresses:")); out.println(F("(with 'telnet or 'nc -u 23')")); for (auto a : addrList) { - out.printf("IF='%s' IPv6=%d local=%d hostname='%s' addr= %s", - a.ifname().c_str(), - a.isV6(), - a.isLocal(), - a.ifhostname(), - a.toString().c_str()); - - if (a.isLegacy()) { - out.printf(" / mask:%s / gw:%s", - a.netmask().toString().c_str(), - a.gw().toString().c_str()); - } + out.printf("IF='%s' IPv6=%d local=%d hostname='%s' addr= %s", a.ifname().c_str(), a.isV6(), a.isLocal(), a.ifhostname(), a.toString().c_str()); - out.println(); + if (a.isLegacy()) { out.printf(" / mask:%s / gw:%s", a.netmask().toString().c_str(), a.gw().toString().c_str()); } + out.println(); } // lwIP's dns client will ask for IPv4 first (by default) @@ -101,8 +89,8 @@ void status(Print& out) { fqdn(out, FQDN); fqdn(out, FQDN6); #if LWIP_IPV4 && LWIP_IPV6 - fqdn_rt(out, FQDN, DNSResolveType::DNS_AddrType_IPv4_IPv6); // IPv4 before IPv6 - fqdn_rt(out, FQDN2, DNSResolveType::DNS_AddrType_IPv6_IPv4); // IPv6 before IPv4 + fqdn_rt(out, FQDN, DNSResolveType::DNS_AddrType_IPv4_IPv6); // IPv4 before IPv6 + fqdn_rt(out, FQDN2, DNSResolveType::DNS_AddrType_IPv6_IPv4); // IPv6 before IPv4 #endif out.println(F("------------------------------")); } @@ -125,7 +113,7 @@ void setup() { status(Serial); -#if 0 // 0: legacy connecting loop - 1: wait for IPv6 +#if 0 // 0: legacy connecting loop - 1: wait for IPv6 // legacy loop (still valid with IPv4 only) @@ -146,11 +134,9 @@ void setup() { for (bool configured = false; !configured;) { for (auto addr : addrList) if ((configured = !addr.isLocal() - // && addr.isV6() // uncomment when IPv6 is mandatory - // && addr.ifnumber() == STATION_IF - )) { - break; - } + // && addr.isV6() // uncomment when IPv6 is mandatory + // && addr.ifnumber() == STATION_IF + )) { break; } Serial.print('.'); delay(500); } @@ -175,7 +161,7 @@ unsigned long statusTimeMs = 0; void loop() { if (statusServer.hasClient()) { - WiFiClient cli = statusServer.available(); + WiFiClient cli = statusServer.accept(); status(cli); } @@ -188,10 +174,8 @@ void loop() { udp.remoteIP().printTo(Serial); Serial.print(F(" :")); Serial.println(udp.remotePort()); - int c; - while ((c = udp.read()) >= 0) { - Serial.write(c); - } + int c; + while ((c = udp.read()) >= 0) { Serial.write(c); } // send a reply, to the IP address and port that sent us the packet we received udp.beginPacket(udp.remoteIP(), udp.remotePort()); @@ -200,8 +184,5 @@ void loop() { } - if (showStatusOnSerialNow) { - status(Serial); - } - + if (showStatusOnSerialNow) { status(Serial); } } diff --git a/libraries/ESP8266WiFi/examples/NTPClient/NTPClient.ino b/libraries/ESP8266WiFi/examples/NTPClient/NTPClient.ino index 927e78fee9..0ef1203667 100644 --- a/libraries/ESP8266WiFi/examples/NTPClient/NTPClient.ino +++ b/libraries/ESP8266WiFi/examples/NTPClient/NTPClient.ino @@ -23,24 +23,24 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif -const char * ssid = STASSID; // your network SSID (name) -const char * pass = STAPSK; // your network password +const char* ssid = STASSID; // your network SSID (name) +const char* pass = STAPSK; // your network password -unsigned int localPort = 2390; // local port to listen for UDP packets +unsigned int localPort = 2390; // local port to listen for UDP packets /* Don't hardwire the IP address or we won't get the benefits of the pool. Lookup the IP address for the host name instead */ -//IPAddress timeServer(129, 6, 15, 28); // time.nist.gov NTP server -IPAddress timeServerIP; // time.nist.gov NTP server address +// IPAddress timeServer(129, 6, 15, 28); // time.nist.gov NTP server +IPAddress timeServerIP; // time.nist.gov NTP server address const char* ntpServerName = "time.nist.gov"; -const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message +const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message -byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets +byte packetBuffer[NTP_PACKET_SIZE]; // buffer to hold incoming and outgoing packets // A UDP instance to let us send and receive packets over UDP WiFiUDP udp; @@ -73,10 +73,10 @@ void setup() { } void loop() { - //get a random server from the pool + // get a random server from the pool WiFi.hostByName(ntpServerName, timeServerIP); - sendNTPpacket(timeServerIP); // send an NTP packet to a time server + sendNTPpacket(timeServerIP); // send an NTP packet to a time server // wait to see if a reply is available delay(1000); @@ -87,10 +87,10 @@ void loop() { Serial.print("packet received, length="); Serial.println(cb); // We've received a packet, read the data from it - udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer + udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer - //the timestamp starts at byte 40 of the received packet and is four bytes, - // or two words, long. First, esxtract the two words: + // the timestamp starts at byte 40 of the received packet and is four bytes, + // or two words, long. First, esxtract the two words: unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); @@ -112,19 +112,19 @@ void loop() { // print the hour, minute and second: Serial.print("The UTC time is "); // UTC is the time at Greenwich Meridian (GMT) - Serial.print((epoch % 86400L) / 3600); // print the hour (86400 equals secs per day) + Serial.print((epoch % 86400L) / 3600); // print the hour (86400 equals secs per day) Serial.print(':'); if (((epoch % 3600) / 60) < 10) { // In the first 10 minutes of each hour, we'll want a leading '0' Serial.print('0'); } - Serial.print((epoch % 3600) / 60); // print the minute (3600 equals secs per minute) + Serial.print((epoch % 3600) / 60); // print the minute (3600 equals secs per minute) Serial.print(':'); if ((epoch % 60) < 10) { // In the first 10 seconds of each minute, we'll want a leading '0' Serial.print('0'); } - Serial.println(epoch % 60); // print the second + Serial.println(epoch % 60); // print the second } // wait ten seconds before asking for the time again delay(10000); @@ -137,19 +137,19 @@ void sendNTPpacket(IPAddress& address) { memset(packetBuffer, 0, NTP_PACKET_SIZE); // Initialize values needed to form NTP request // (see URL above for details on the packets) - packetBuffer[0] = 0b11100011; // LI, Version, Mode - packetBuffer[1] = 0; // Stratum, or type of clock - packetBuffer[2] = 6; // Polling Interval - packetBuffer[3] = 0xEC; // Peer Clock Precision + packetBuffer[0] = 0b11100011; // LI, Version, Mode + packetBuffer[1] = 0; // Stratum, or type of clock + packetBuffer[2] = 6; // Polling Interval + packetBuffer[3] = 0xEC; // Peer Clock Precision // 8 bytes of zero for Root Delay & Root Dispersion - packetBuffer[12] = 49; - packetBuffer[13] = 0x4E; - packetBuffer[14] = 49; - packetBuffer[15] = 52; + packetBuffer[12] = 49; + packetBuffer[13] = 0x4E; + packetBuffer[14] = 49; + packetBuffer[15] = 52; // all NTP fields have been given values, now // you can send a packet requesting a timestamp: - udp.beginPacket(address, 123); //NTP requests are to port 123 + udp.beginPacket(address, 123); // NTP requests are to port 123 udp.write(packetBuffer, NTP_PACKET_SIZE); udp.endPacket(); } diff --git a/libraries/ESP8266WiFi/examples/PagerServer/PagerServer.ino b/libraries/ESP8266WiFi/examples/PagerServer/PagerServer.ino new file mode 100644 index 0000000000..a19ffa42f7 --- /dev/null +++ b/libraries/ESP8266WiFi/examples/PagerServer/PagerServer.ino @@ -0,0 +1,72 @@ +/* + Pager Server + + The ESP8266WiFi library's WiFiServer and WiFiServerSecure + work differently than WiFiServer and EthernetSever + in Arduino networking libraries. + This example demonstrates the ArduinoWiFiServer, + which enhances the WiFiServer. + ArduinoWiFiServer has available() behaving as documented + and supports send-to-all-clients functionality, which + is not implemented in ESP8266WiFi library's WiFiServer + and WiFiServerSecure. + + The example is a simple server that echoes any incoming + messages to all connected clients. Connect two or more + telnet sessions to see how server.available() and + server.print() work. + + created in September 2020 for ESP8266WiFi library + by Juraj Andrassy https://github.com/jandrassy +*/ + +#include +#include + +#ifndef STASSID +#define STASSID "your-ssid" +#define STAPSK "your-password" +#endif + +const char* ssid = STASSID; +const char* password = STAPSK; + +ArduinoWiFiServer server(2323); + +void setup() { + + Serial.begin(115200); + + Serial.println(); + Serial.print("Connecting to "); + Serial.println(ssid); + + WiFi.begin(ssid, password); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + + server.begin(); + + IPAddress ip = WiFi.localIP(); + Serial.println(); + Serial.println("Connected to WiFi network."); + Serial.print("To access the server, connect with Telnet client to "); + Serial.print(ip); + Serial.println(" 2323"); +} + +void loop() { + + WiFiClient client = server.available(); // returns first client which has data to read or a 'false' client + if (client) { // client is true only if it is connected and has data to read + String s = client.readStringUntil('\n'); // read the message incoming from one of the clients + s.trim(); // trim eventual \r + Serial.println(s); // print the message to Serial Monitor + client.print("echo: "); // this is only for the sending client + server.println(s); // send the message to all connected clients + server.flush(); // flush the buffers + } +} diff --git a/libraries/ESP8266WiFi/examples/RangeExtender-NAPT/RangeExtender-NAPT.ino b/libraries/ESP8266WiFi/examples/RangeExtender-NAPT/RangeExtender-NAPT.ino index a98e894873..c431df572c 100644 --- a/libraries/ESP8266WiFi/examples/RangeExtender-NAPT/RangeExtender-NAPT.ino +++ b/libraries/ESP8266WiFi/examples/RangeExtender-NAPT/RangeExtender-NAPT.ino @@ -7,13 +7,12 @@ #ifndef STASSID #define STASSID "mynetwork" -#define STAPSK "mynetworkpassword" +#define STAPSK "mynetworkpassword" #endif #include #include #include -#include #define NAPT 1000 #define NAPT_PORT 10 @@ -30,7 +29,7 @@ void dump(int netif_idx, const char* data, size_t len, int out, int success) { // optional filter example: if (netDump_is_ARP(data)) { netDump(Serial, data, len); - //netDumpHex(Serial, data, len); + // netDumpHex(Serial, data, len); } } #endif @@ -51,19 +50,18 @@ void setup() { Serial.print('.'); delay(500); } - 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()); + 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)); + IPAddress(172, 217, 28, 254), IPAddress(172, 217, 28, 254), IPAddress(255, 255, 255, 0)); WiFi.softAP(STASSID "extender", STAPSK); Serial.printf("AP: %s\n", WiFi.softAPIP().toString().c_str()); @@ -73,14 +71,10 @@ void setup() { if (ret == ERR_OK) { ret = ip_napt_enable_no(SOFTAP_IF, 1); Serial.printf("ip_napt_enable_no(SOFTAP_IF): ret=%d (OK=%d)\n", (int)ret, (int)ERR_OK); - if (ret == ERR_OK) { - Serial.printf("WiFi Network '%s' with same password is now NATed behind '%s'\n", STASSID "extender", STASSID); - } + if (ret == ERR_OK) { Serial.printf("WiFi Network '%s' with same password is now NATed behind '%s'\n", STASSID "extender", STASSID); } } Serial.printf("Heap after napt init: %d\n", ESP.getFreeHeap()); - if (ret != ERR_OK) { - Serial.printf("NAPT initialization failed\n"); - } + if (ret != ERR_OK) { Serial.printf("NAPT initialization failed\n"); } } #else @@ -92,6 +86,4 @@ void setup() { #endif -void loop() { -} - +void loop() {} 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/Udp/Udp.ino b/libraries/ESP8266WiFi/examples/Udp/Udp.ino index 7ec287b392..23d6af8de1 100644 --- a/libraries/ESP8266WiFi/examples/Udp/Udp.ino +++ b/libraries/ESP8266WiFi/examples/Udp/Udp.ino @@ -20,14 +20,14 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif -unsigned int localPort = 8888; // local port to listen on +unsigned int localPort = 8888; // local port to listen on // buffers for receiving and sending data -char packetBuffer[UDP_TX_PACKET_MAX_SIZE + 1]; //buffer to hold incoming packet, -char ReplyBuffer[] = "acknowledged\r\n"; // a string to send back +char packetBuffer[UDP_TX_PACKET_MAX_SIZE + 1]; // buffer to hold incoming packet, +char ReplyBuffer[] = "acknowledged\r\n"; // a string to send back WiFiUDP Udp; @@ -49,11 +49,7 @@ void loop() { // if there's data available, read a packet int packetSize = Udp.parsePacket(); if (packetSize) { - Serial.printf("Received packet of size %d from %s:%d\n (to %s:%d, free heap = %d B)\n", - packetSize, - Udp.remoteIP().toString().c_str(), Udp.remotePort(), - Udp.destinationIP().toString().c_str(), Udp.localPort(), - ESP.getFreeHeap()); + Serial.printf("Received packet of size %d from %s:%d\n (to %s:%d, free heap = %d B)\n", packetSize, Udp.remoteIP().toString().c_str(), Udp.remotePort(), Udp.destinationIP().toString().c_str(), Udp.localPort(), ESP.getFreeHeap()); // read the packet into packetBufffer int n = Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE); @@ -66,11 +62,10 @@ void loop() { Udp.write(ReplyBuffer); Udp.endPacket(); } - } /* test (shell/netcat): -------------------- - nc -u 192.168.esp.address 8888 + nc -u 192.168.esp.address 8888 */ diff --git a/libraries/ESP8266WiFi/examples/WiFiAccessPoint/WiFiAccessPoint.ino b/libraries/ESP8266WiFi/examples/WiFiAccessPoint/WiFiAccessPoint.ino index 6fea68f590..15dd93c83d 100644 --- a/libraries/ESP8266WiFi/examples/WiFiAccessPoint/WiFiAccessPoint.ino +++ b/libraries/ESP8266WiFi/examples/WiFiAccessPoint/WiFiAccessPoint.ino @@ -36,7 +36,7 @@ #ifndef APSSID #define APSSID "ESPap" -#define APPSK "thereisnospoon" +#define APPSK "thereisnospoon" #endif /* Set these to your desired credentials. */ diff --git a/libraries/ESP8266WiFi/examples/WiFiClient/WiFiClient.ino b/libraries/ESP8266WiFi/examples/WiFiClient/WiFiClient.ino index ef9016063b..cddb262a77 100644 --- a/libraries/ESP8266WiFi/examples/WiFiClient/WiFiClient.ino +++ b/libraries/ESP8266WiFi/examples/WiFiClient/WiFiClient.ino @@ -7,10 +7,10 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif -const char* ssid = STASSID; +const char* ssid = STASSID; const char* password = STAPSK; const char* host = "djxmmx.net"; @@ -61,9 +61,7 @@ void loop() { // This will send a string to the server Serial.println("sending data to server"); - if (client.connected()) { - client.println("hello from ESP8266"); - } + if (client.connected()) { client.println("hello from ESP8266"); } // wait for data to be available unsigned long timeout = millis(); @@ -90,7 +88,7 @@ void loop() { client.stop(); if (wait) { - delay(300000); // execute once every 5 minutes, don't flood remote service + delay(300000); // execute once every 5 minutes, don't flood remote service } wait = true; } diff --git a/libraries/ESP8266WiFi/examples/WiFiClientBasic/WiFiClientBasic.ino b/libraries/ESP8266WiFi/examples/WiFiClientBasic/WiFiClientBasic.ino index e442282e37..4be0d2b9d0 100644 --- a/libraries/ESP8266WiFi/examples/WiFiClientBasic/WiFiClientBasic.ino +++ b/libraries/ESP8266WiFi/examples/WiFiClientBasic/WiFiClientBasic.ino @@ -9,10 +9,10 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif -const char* ssid = STASSID; +const char* ssid = STASSID; const char* password = STAPSK; const char* host = "192.168.1.1"; @@ -64,7 +64,7 @@ void loop() { // This will send the request to the server client.println("hello from ESP8266"); - //read back one line from server + // read back one line from server Serial.println("receiving from remote server"); String line = client.readStringUntil('\r'); Serial.println(line); @@ -75,4 +75,3 @@ void loop() { Serial.println("wait 5 sec..."); delay(5000); } - diff --git a/libraries/ESP8266WiFi/examples/WiFiEcho/WiFiEcho.ino b/libraries/ESP8266WiFi/examples/WiFiEcho/WiFiEcho.ino new file mode 100644 index 0000000000..c4a6cb788d --- /dev/null +++ b/libraries/ESP8266WiFi/examples/WiFiEcho/WiFiEcho.ino @@ -0,0 +1,162 @@ +/* + WiFiEcho - Echo server + + released to public domain +*/ + +#include +#include +#include +#include // std::min + +#ifndef STASSID +#define STASSID "your-ssid" +#define STAPSK "your-password" +#endif + +constexpr int port = 23; + +WiFiServer server(port); +WiFiClient client; + +constexpr size_t sizes[] = { 0, 512, 384, 256, 128, 64, 16, 8, 4 }; +constexpr uint32_t breathMs = 200; +esp8266::polledTimeout::oneShotFastMs enoughMs(breathMs); +esp8266::polledTimeout::periodicFastMs test(2000); +int t = 1; // test (1, 2 or 3, see below) +int s = 0; // sizes[] index + +void setup() { + + Serial.begin(115200); + Serial.println(ESP.getFullVersion()); + + WiFi.mode(WIFI_STA); + WiFi.begin(STASSID, STAPSK); + Serial.print("\nConnecting to "); + Serial.println(STASSID); + while (WiFi.status() != WL_CONNECTED) { + Serial.print('.'); + delay(500); + } + Serial.println(); + Serial.print("connected, address="); + Serial.println(WiFi.localIP()); + + server.begin(); + + MDNS.begin("echo23"); + + Serial.printf("Ready!\n" + "- Use 'telnet/nc echo23.local %d' to try echo\n\n" + "- Use 'python3 echo-client.py' bandwidth meter to compare transfer APIs\n\n" + " and try typing 1, 1, 1, 2, 2, 2, 3, 3, 3 on console during transfers\n\n", + port); +} + + +void loop() { + + MDNS.update(); + + static uint32_t tot = 0; + static uint32_t cnt = 0; + if (test && cnt) { + Serial.printf("measured-block-size=%u min-free-stack=%u", tot / cnt, ESP.getFreeContStack()); + if (t == 2 && sizes[s]) { Serial.printf(" (blocks: at most %d bytes)", sizes[s]); } + if (t == 3 && sizes[s]) { Serial.printf(" (blocks: exactly %d bytes)", sizes[s]); } + if (t == 3 && !sizes[s]) { Serial.printf(" (blocks: any size)"); } + Serial.printf("\n"); + } + + // check if there are any new clients + if (server.hasClient()) { + client = server.accept(); + Serial.println("New client"); + } + + if (Serial.available()) { + s = (s + 1) % (sizeof(sizes) / sizeof(sizes[0])); + switch (Serial.read()) { + case '1': + if (t != 1) s = 0; + t = 1; + Serial.println("byte-by-byte (watch then press 2, 3 or 4)"); + break; + case '2': + if (t != 2) s = 1; + t = 2; + Serial.printf("through buffer (watch then press 2 again, or 1, 3 or 4)\n"); + break; + case '3': + if (t != 3) s = 0; + t = 3; + Serial.printf("direct access (sendAvailable - watch then press 3 again, or 1, 2 or 4)\n"); + break; + case '4': + t = 4; + Serial.printf("direct access (sendAll - close peer to stop, then press 1, 2 or 3 before restarting peer)\n"); + break; + } + tot = cnt = 0; + ESP.resetFreeContStack(); + } + + enoughMs.reset(breathMs); + + if (t == 1) { + // byte by byte + while (client.available() && client.availableForWrite() && !enoughMs) { + // working char by char is not efficient + client.write(client.read()); + cnt++; + tot += 1; + } + } + + else if (t == 2) { + // block by block through a local buffer (2 copies) + while (client.available() && client.availableForWrite() && !enoughMs) { + size_t maxTo = std::min(client.available(), client.availableForWrite()); + maxTo = std::min(maxTo, sizes[s]); + uint8_t buf[maxTo]; + size_t tcp_got = client.read(buf, maxTo); + size_t tcp_sent = client.write(buf, tcp_got); + if (tcp_sent != maxTo) { Serial.printf("len mismatch: available:%zd tcp-read:%zd serial-write:%zd\n", maxTo, tcp_got, tcp_sent); } + tot += tcp_sent; + cnt++; + } + } + + else if (t == 3) { + // stream to print, possibly with only one copy + if (sizes[s]) { + tot += client.sendSize(&client, sizes[s]); + } else { + tot += client.sendAvailable(&client); + } + cnt++; + + switch (client.getLastSendReport()) { + case Stream::Report::Success: break; + case Stream::Report::TimedOut: Serial.println("Stream::send: timeout"); break; + case Stream::Report::ReadError: Serial.println("Stream::send: read error"); break; + case Stream::Report::WriteError: Serial.println("Stream::send: write error"); break; + case Stream::Report::ShortOperation: Serial.println("Stream::send: short transfer"); break; + } + } + + else if (t == 4) { + // stream to print, possibly with only one copy + tot += client.sendAll(&client); // this one might not exit until peer close + cnt++; + + switch (client.getLastSendReport()) { + case Stream::Report::Success: break; + case Stream::Report::TimedOut: Serial.println("Stream::send: timeout"); break; + case Stream::Report::ReadError: Serial.println("Stream::send: read error"); break; + case Stream::Report::WriteError: Serial.println("Stream::send: write error"); break; + case Stream::Report::ShortOperation: Serial.println("Stream::send: short transfer"); break; + } + } +} diff --git a/libraries/ESP8266WiFi/examples/WiFiEcho/echo-client.py b/libraries/ESP8266WiFi/examples/WiFiEcho/echo-client.py new file mode 100755 index 0000000000..55c90073b7 --- /dev/null +++ b/libraries/ESP8266WiFi/examples/WiFiEcho/echo-client.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 + +import os +import asyncio + +# 512 bytes +message = bytearray(512); +bufsize=len(message) +print('message len=', bufsize) + +global recv +recv = 0 + +async def tcp_echo_open (ip, port): + return await asyncio.open_connection(ip, port) + +async def tcp_echo_sender(message, writer): + print('Writer started') + while True: + writer.write(message) + await writer.drain() + +async def tcp_echo_receiver(message, reader): + global recv + print('Reader started') + while True: + data = ''.encode('utf8') + while len(data) < bufsize: + data += await reader.read(bufsize - len(data)) + recv += len(data); + if data != message: + print('error') + +async def tcp_stat(): + global recv + dur = 0 + loopsec = 2 + while True: + last = recv + await asyncio.sleep(loopsec) # drifting + dur += loopsec + print('BW=', (recv - last) * 2 * 8 / 1024 / loopsec, 'Kibits/s avg=', recv * 2 * 8 / 1024 / dur) + +loop = asyncio.get_event_loop() +reader, writer = loop.run_until_complete(tcp_echo_open('echo23.local', 23)) +loop.create_task(tcp_echo_receiver(message, reader)) +loop.create_task(tcp_echo_sender(message, writer)) +loop.create_task(tcp_stat()) +loop.run_forever() diff --git a/libraries/ESP8266WiFi/examples/WiFiEvents/WiFiEvents.ino b/libraries/ESP8266WiFi/examples/WiFiEvents/WiFiEvents.ino index d72985a9c1..7d66553b61 100644 --- a/libraries/ESP8266WiFi/examples/WiFiEvents/WiFiEvents.ino +++ b/libraries/ESP8266WiFi/examples/WiFiEvents/WiFiEvents.ino @@ -18,12 +18,15 @@ #ifndef APSSID #define APSSID "esp8266" -#define APPSK "esp8266" +#define APPSK "esp8266" #endif -const char* ssid = APSSID; +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, @@ -98,7 +101,6 @@ void loop() { String macToString(const unsigned char* mac) { char buf[20]; - snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x", - mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); return String(buf); } diff --git a/libraries/ESP8266WiFi/examples/WiFiManualWebServer/WiFiManualWebServer.ino b/libraries/ESP8266WiFi/examples/WiFiManualWebServer/WiFiManualWebServer.ino index 7360d10640..0526908a34 100644 --- a/libraries/ESP8266WiFi/examples/WiFiManualWebServer/WiFiManualWebServer.ino +++ b/libraries/ESP8266WiFi/examples/WiFiManualWebServer/WiFiManualWebServer.ino @@ -11,7 +11,7 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif const char* ssid = STASSID; @@ -54,13 +54,11 @@ void setup() { void loop() { // Check if a client has connected - WiFiClient client = server.available(); - if (!client) { - return; - } + WiFiClient client = server.accept(); + if (!client) { return; } Serial.println(F("new client")); - client.setTimeout(5000); // default is 1000 + client.setTimeout(5000); // default is 1000 // Read the first line of the request String req = client.readStringUntil('\r'); diff --git a/libraries/ESP8266WiFi/examples/WiFiMulti/WiFiMulti.ino b/libraries/ESP8266WiFi/examples/WiFiMulti/WiFiMulti.ino index 1aa6f25b8e..fe93d98a43 100644 --- a/libraries/ESP8266WiFi/examples/WiFiMulti/WiFiMulti.ino +++ b/libraries/ESP8266WiFi/examples/WiFiMulti/WiFiMulti.ino @@ -7,6 +7,7 @@ - Registering multiple networks (at least 1) - Connect to WiFi with strongest signal (RSSI) - Fall back to connect to next WiFi when a connection failed or lost + - Fall back to connect to hidden SSID's which are not reported by WiFi scan To enable debugging output, select in the Arduino iDE: - Tools | Debug Port: Serial @@ -21,6 +22,9 @@ ESP8266WiFiMulti wifiMulti; const uint32_t connectTimeoutMs = 5000; void setup() { + // Don't save WiFi configuration in flash - optional + WiFi.persistent(false); + Serial.begin(115200); Serial.println("\nESP8266 Multi WiFi example"); diff --git a/libraries/ESP8266WiFi/examples/WiFiScan/WiFiScan.ino b/libraries/ESP8266WiFi/examples/WiFiScan/WiFiScan.ino index 84a9befcf2..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,16 +40,31 @@ 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()); - delay(0); + // 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 { Serial.printf(PSTR("WiFi scan error %d"), scanResult); diff --git a/libraries/ESP8266WiFi/examples/WiFiShutdown/WiFiShutdown.ino b/libraries/ESP8266WiFi/examples/WiFiShutdown/WiFiShutdown.ino new file mode 100644 index 0000000000..69502de862 --- /dev/null +++ b/libraries/ESP8266WiFi/examples/WiFiShutdown/WiFiShutdown.ino @@ -0,0 +1,77 @@ + +// Demonstrate the use of WiFi.shutdown() and WiFi.resumeFromShutdown() +// Released to public domain + +// Current on WEMOS D1 mini (including: LDO, usbserial chip): +// ~85mA during normal operations +// ~30mA during wifi shutdown +// ~5mA during deepsleep + +#ifndef STASSID +#define STASSID "mynetwork" +#define STAPSK "mynetworkpasswd" +#endif + +#ifndef RTC_USER_DATA_SLOT_WIFI_STATE +#define RTC_USER_DATA_SLOT_WIFI_STATE 33u +#endif + +#include +#include // WiFiState structure details + +WiFiState state; + +const char* ssid = STASSID; +const char* password = STAPSK; + +void setup() { + Serial.begin(74880); + // Serial.setDebugOutput(true); // If you need debug output + Serial.println("Trying to resume WiFi connection..."); + + // May be necessary after deepSleep. Otherwise you may get "error: pll_cal exceeds 2ms!!!" when trying to connect + delay(1); + + // --- + // Here you can do whatever you need to do that doesn't need a WiFi connection. + // --- + + ESP.rtcUserMemoryRead(RTC_USER_DATA_SLOT_WIFI_STATE, reinterpret_cast(&state), sizeof(state)); + unsigned long start = millis(); + + if (!WiFi.resumeFromShutdown(state) || (WiFi.waitForConnectResult(10000) != WL_CONNECTED)) { + Serial.println("Cannot resume WiFi connection, connecting via begin..."); + WiFi.persistent(false); + + if (!WiFi.mode(WIFI_STA) || !WiFi.begin(ssid, password) || (WiFi.waitForConnectResult(10000) != WL_CONNECTED)) { + WiFi.mode(WIFI_OFF); + Serial.println("Cannot connect!"); + Serial.flush(); + ESP.deepSleep(10e6, RF_DISABLED); + return; + } + } + + unsigned long duration = millis() - start; + Serial.printf("Duration: %f", duration * 0.001); + Serial.println(); + + // --- + // Here you can do whatever you need to do that needs a WiFi connection. + // --- + + WiFi.shutdown(state); + ESP.rtcUserMemoryWrite(RTC_USER_DATA_SLOT_WIFI_STATE, reinterpret_cast(&state), sizeof(state)); + + // --- + // Here you can do whatever you need to do that doesn't need a WiFi connection anymore. + // --- + + Serial.println("Done."); + Serial.flush(); + ESP.deepSleep(10e6, RF_DISABLED); +} + +void loop() { + // Nothing to do here. +} diff --git a/libraries/ESP8266WiFi/examples/WiFiTelnetToSerial/WiFiTelnetToSerial.ino b/libraries/ESP8266WiFi/examples/WiFiTelnetToSerial/WiFiTelnetToSerial.ino index 46a4f277dd..15c0cd38bd 100644 --- a/libraries/ESP8266WiFi/examples/WiFiTelnetToSerial/WiFiTelnetToSerial.ino +++ b/libraries/ESP8266WiFi/examples/WiFiTelnetToSerial/WiFiTelnetToSerial.ino @@ -20,18 +20,18 @@ */ #include -#include // std::min +#include // std::min #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif /* 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 */ @@ -64,9 +64,9 @@ SoftwareSerial* logger = nullptr; #define logger (&Serial1) #endif -#define STACK_PROTECTOR 512 // bytes +#define STACK_PROTECTOR 512 // bytes -//how many clients should be able to telnet to this ESP8266 +// how many clients should be able to telnet to this ESP8266 #define MAX_SRV_CLIENTS 2 const char* ssid = STASSID; const char* password = STAPSK; @@ -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 @@ -98,7 +100,7 @@ void setup() { logger->printf("Serial receive buffer size: %d bytes\n", RXBUFFERSIZE); #if SERIAL_LOOPBACK - USC0(0) |= (1 << UCLBE); // incomplete HardwareSerial API + USC0(0) |= (1 << UCLBE); // incomplete HardwareSerial API logger->println("Serial Internal Loopback enabled"); #endif @@ -114,7 +116,7 @@ void setup() { logger->print("connected, address="); logger->println(WiFi.localIP()); - //start server + // start server server.begin(); server.setNoDelay(true); @@ -124,22 +126,22 @@ void setup() { } void loop() { - //check if there are any new clients + // check if there are any new clients if (server.hasClient()) { - //find free/disconnected spot + // find free/disconnected spot int i; for (i = 0; i < MAX_SRV_CLIENTS; i++) - if (!serverClients[i]) { // equivalent to !serverClients[i].connected() - serverClients[i] = server.available(); + if (!serverClients[i]) { // equivalent to !serverClients[i].connected() + serverClients[i] = server.accept(); logger->print("New client: index "); logger->print(i); break; } - //no free/disconnected spot so reject + // no free/disconnected spot so reject if (i == MAX_SRV_CLIENTS) { - server.available().println("busy"); - // hints: server.available() is a WiFiClient with short-term scope + server.accept().println("busy"); + // hints: server.accept() is a WiFiClient with short-term scope // when out of scope, a WiFiClient will // - flush() - all data will be sent // - stop() - automatically too @@ -147,9 +149,9 @@ void loop() { } } - //check TCP clients for data + // check TCP clients for data #if 1 - // Incredibly, this code is faster than the bufferred one below - #4620 is needed + // Incredibly, this code is faster than the buffered one below - #4620 is needed // loopback/3000000baud average 348KB/s for (int i = 0; i < MAX_SRV_CLIENTS; i++) while (serverClients[i].available() && Serial.availableForWrite() > 0) { @@ -165,9 +167,7 @@ void loop() { uint8_t buf[maxToSerial]; size_t tcp_got = serverClients[i].read(buf, maxToSerial); size_t serial_sent = Serial.write(buf, tcp_got); - if (serial_sent != maxToSerial) { - logger->printf("len mismatch: available:%zd tcp-read:%zd serial-write:%zd\n", maxToSerial, tcp_got, serial_sent); - } + if (serial_sent != maxToSerial) { logger->printf("len mismatch: available:%zd tcp-read:%zd serial-write:%zd\n", maxToSerial, tcp_got, serial_sent); } } #endif @@ -189,7 +189,7 @@ void loop() { } } - //check UART for data + // check UART for data size_t len = std::min(Serial.available(), maxToTcp); len = std::min(len, (size_t)STACK_PROTECTOR); if (len) { @@ -202,9 +202,7 @@ void loop() { // ensure write space is sufficient: if (serverClients[i].availableForWrite() >= serial_got) { size_t tcp_sent = serverClients[i].write(sbuf, serial_got); - if (tcp_sent != len) { - logger->printf("len mismatch: available:%zd serial-read:%zd tcp-write:%zd\n", len, serial_got, tcp_sent); - } + if (tcp_sent != len) { logger->printf("len mismatch: available:%zd serial-read:%zd tcp-write:%zd\n", len, serial_got, tcp_sent); } } } } diff --git a/libraries/ESP8266WiFi/keywords.txt b/libraries/ESP8266WiFi/keywords.txt index d3c1bfa38e..e5c11a1d94 100644 --- a/libraries/ESP8266WiFi/keywords.txt +++ b/libraries/ESP8266WiFi/keywords.txt @@ -56,6 +56,8 @@ enableSTA KEYWORD2 enableAP KEYWORD2 forceSleepBegin KEYWORD2 forceSleepWake KEYWORD2 +shutdown KEYWORD2 +resumeFromShutdown KEYWORD2 #ESP8266WiFi printDiag KEYWORD2 @@ -156,6 +158,7 @@ loadCertificate KEYWORD2 loadPrivateKey KEYWORD2 loadCACert KEYWORD2 allowSelfSignedCerts KEYWORD2 +setSSLVersion KEYWORD2 #WiFiServer hasClient KEYWORD2 diff --git a/libraries/ESP8266WiFi/src/ArduinoWiFiServer.h b/libraries/ESP8266WiFi/src/ArduinoWiFiServer.h new file mode 100644 index 0000000000..45e5e59d6e --- /dev/null +++ b/libraries/ESP8266WiFi/src/ArduinoWiFiServer.h @@ -0,0 +1,139 @@ +/* + ArduinoWiFiServer.h - Arduino compatible WiFiServer + implementation for ESP8266Wifi library. + Copyright (c) 2020 Juraj Andrassy + + 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 arduinowifiserver_h +#define arduinowifiserver_h + +#include + +#ifndef MAX_MONITORED_CLIENTS +#define MAX_MONITORED_CLIENTS 5 +#endif + +template +class ArduinoCompatibleWiFiServerTemplate : public TServer, public Print { +public: + + ArduinoCompatibleWiFiServerTemplate(const IPAddress& addr, uint16_t port) : TServer(addr, port) {} + ArduinoCompatibleWiFiServerTemplate(uint16_t port = 23) : TServer(port) {} + virtual ~ArduinoCompatibleWiFiServerTemplate() {} + + // https://www.arduino.cc/en/Reference/WiFiServerAvailable + TClient available() { + + acceptClients(); + + // find next client with data available + for (uint8_t i = 0; i < MAX_MONITORED_CLIENTS; i++) { + if (index == MAX_MONITORED_CLIENTS) { + index = 0; + } + TClient& client = connectedClients[index]; + index++; + if (client.available()) + return client; + } + return TClient(); // no client with data found + } + + virtual size_t write(uint8_t b) override { + return write(&b, 1); + } + + virtual size_t write(const uint8_t *buf, size_t size) override { + static uint32_t lastCheck; + uint32_t m = millis(); + if (m - lastCheck > 100) { + lastCheck = m; + acceptClients(); + } + if (size == 0) + return 0; + size_t ret = 0; + size_t a = size; + while (true) { + for (uint8_t i = 0; i < MAX_MONITORED_CLIENTS; i++) { + WiFiClient& client = connectedClients[i]; + if (client.status() == ESTABLISHED && client.availableForWrite() < (int) a) { + a = client.availableForWrite(); + } + } + if (a == 0) + break; + for (uint8_t i = 0; i < MAX_MONITORED_CLIENTS; i++) { + if (connectedClients[i].status() == ESTABLISHED) { + connectedClients[i].write(buf, a); + } + } + ret += a; + if (ret == size) + break; + buf += a; + a = size - ret; + } + return ret; + } + + using Print::write; + + virtual void flush() override { + flush(0); + } + + virtual void flush(unsigned int maxWaitMs) { + for (uint8_t i = 0; i < MAX_MONITORED_CLIENTS; i++) { + if (connectedClients[i].status() == ESTABLISHED) { + connectedClients[i].flush(maxWaitMs); + } + } + } + + operator bool() { + return (TServer::status() == LISTEN); + } + + void close() { + TServer::stop(); + for (uint8_t i = 0; i < MAX_MONITORED_CLIENTS; i++) { + if (connectedClients[i]) { + connectedClients[i].stop(); + } + } + } + void stop() {close();} + void end() {close();} + +private: + TClient connectedClients[MAX_MONITORED_CLIENTS]; + uint8_t index = 0; + + void acceptClients() { + for (uint8_t i = 0; i < MAX_MONITORED_CLIENTS; i++) { + if (!connectedClients[i]) { + connectedClients[i] = TServer::accept(); + } + } + } +}; + +typedef ArduinoCompatibleWiFiServerTemplate ArduinoWiFiServer; +typedef ArduinoCompatibleWiFiServerTemplate ArduinoWiFiServerSecure; + +#endif diff --git a/libraries/ESP8266WiFi/src/BearSSLHelpers.cpp b/libraries/ESP8266WiFi/src/BearSSLHelpers.cpp index 15a09f209d..0b18334cbd 100644 --- a/libraries/ESP8266WiFi/src/BearSSLHelpers.cpp +++ b/libraries/ESP8266WiFi/src/BearSSLHelpers.cpp @@ -457,6 +457,7 @@ namespace brssl { } memcpy(pk->key.ec.q, ek->q, ek->qlen); pk->key.ec.qlen = ek->qlen; + pk->key.ec.curve = ek->curve; return pk; default: @@ -526,6 +527,10 @@ namespace brssl { case BR_KEYTYPE_EC: ek = br_skey_decoder_get_ec(dc.get()); sk = (private_key*)malloc(sizeof * sk); + if (!sk) + { + return nullptr; + } sk->key_type = BR_KEYTYPE_EC; sk->key.ec.curve = ek->curve; sk->key.ec.x = (uint8_t*)malloc(ek->xlen); @@ -932,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 d91c8211d5..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; @@ -161,7 +164,7 @@ class ServerSessions { ServerSessions(ServerSession *sessions, uint32_t size) : ServerSessions(sessions, size, false) {} // Dynamically allocates a cache for the given number of sessions and initializes it. - // If the allocation of the buffer wasn't successfull, the value + // If the allocation of the buffer wasn't successful, the value // returned by size() will be 0. ServerSessions(uint32_t size) : ServerSessions(size > 0 ? new ServerSession[size] : nullptr, size, true) {} @@ -178,7 +181,7 @@ class ServerSessions { // Size of the store in sessions. uint32_t _size; - // Store where the informations for the sessions are stored. + // Store where the information for the sessions are stored. ServerSession *_store; // Whether the store is dynamically allocated. // If this is true, the store needs to be freed in the destructor. diff --git a/libraries/ESP8266WiFi/src/CertStoreBearSSL.cpp b/libraries/ESP8266WiFi/src/CertStoreBearSSL.cpp index 8af9cb8bdb..b16af42aaa 100644 --- a/libraries/ESP8266WiFi/src/CertStoreBearSSL.cpp +++ b/libraries/ESP8266WiFi/src/CertStoreBearSSL.cpp @@ -125,7 +125,7 @@ int CertStore::initCertStore(fs::FS &fs, const char *indexFileName, const char * uint8_t fileHeader[60]; // 0..15 = filename in ASCII // 48...57 = length in decimal ASCII - uint32_t length; + int32_t length; if (data.read(fileHeader, sizeof(fileHeader)) != sizeof(fileHeader)) { break; } @@ -201,7 +201,7 @@ const br_x509_trust_anchor *CertStore::findHashedTA(void *ctx, void *hashed_dn, free(der); return nullptr; } - if (data.read((uint8_t *)der, ci.length) != ci.length) { + if (data.read(der, ci.length) != (int)ci.length) { free(der); return nullptr; } diff --git a/libraries/ESP8266WiFi/src/CertStoreBearSSL.h b/libraries/ESP8266WiFi/src/CertStoreBearSSL.h index dcfcb34653..51dcb07551 100644 --- a/libraries/ESP8266WiFi/src/CertStoreBearSSL.h +++ b/libraries/ESP8266WiFi/src/CertStoreBearSSL.h @@ -31,7 +31,15 @@ namespace BearSSL { -class CertStore { +class CertStoreBase { + public: + virtual ~CertStoreBase() {} + + // Installs the cert store into the X509 decoder (normally via static function callbacks) + virtual void installCertStore(br_x509_minimal_context *ctx) = 0; +}; + +class CertStore: public CertStoreBase { public: CertStore() { }; ~CertStore(); diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFi.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFi.cpp index 8f63a97fa6..6531b3d9df 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFi.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFi.cpp @@ -75,12 +75,12 @@ void ESP8266WiFiClass::printDiag(Print& p) { char ssid[33]; //ssid can be up to 32chars, => plus null term memcpy(ssid, conf.ssid, sizeof(conf.ssid)); ssid[32] = 0; //nullterm in case of 32 char ssid - p.printf_P(PSTR("SSID (%d): %s\n"), strlen(ssid), ssid); + p.printf_P(PSTR("SSID (%zu): %s\n"), strlen(ssid), ssid); char passphrase[65]; memcpy(passphrase, conf.password, sizeof(conf.password)); passphrase[64] = 0; - p.printf_P(PSTR("Passphrase (%d): %s\n"), strlen(passphrase), passphrase); + p.printf_P(PSTR("Passphrase (%zu): %s\n"), strlen(passphrase), passphrase); p.print(F("BSSID set: ")); p.println(conf.bssid_set); diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFi.h b/libraries/ESP8266WiFi/src/ESP8266WiFi.h index 819739eaea..f31ffe002b 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFi.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFi.h @@ -53,6 +53,7 @@ extern "C" { #define DEBUG_WIFI(...) do { (void)0; } while (0) #endif +extern "C" void enableWiFiAtBootTime (void) __attribute__((noinline)); class ESP8266WiFiClass : public ESP8266WiFiGenericClass, public ESP8266WiFiSTAClass, public ESP8266WiFiScanClass, public ESP8266WiFiAPClass { public: diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiAP.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiAP.cpp index 202042c780..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 ------------------------------------------------ @@ -54,10 +55,13 @@ static bool softap_config_equal(const softap_config& lhs, const softap_config& r * @return equal */ static bool softap_config_equal(const softap_config& lhs, const softap_config& rhs) { - if(strcmp(reinterpret_cast(lhs.ssid), reinterpret_cast(rhs.ssid)) != 0) { + if(lhs.ssid_len != rhs.ssid_len) { + return false; + } + if(memcmp(lhs.ssid, rhs.ssid, lhs.ssid_len) != 0) { return false; } - if(strcmp(reinterpret_cast(lhs.password), reinterpret_cast(rhs.password)) != 0) { + if(strncmp(reinterpret_cast(lhs.password), reinterpret_cast(rhs.password), sizeof(softap_config::password)) != 0) { return false; } if(lhs.channel != rhs.channel) { @@ -85,13 +89,14 @@ static bool softap_config_equal(const softap_config& lhs, const softap_config& r /** * Set up an access point - * @param ssid Pointer to the SSID (max 31 char). - * @param passphrase For WPA2 min 8 char, for open use NULL (max 63 char). + * @param ssid Pointer to the SSID (max 32 char). + * @param psk For WPA2 min 8 char max 64 char, for open use "" or NULL. * @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* passphrase, 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 @@ -99,36 +104,43 @@ bool ESP8266WiFiAPClass::softAP(const char* ssid, const char* passphrase, int ch return false; } - if(!ssid || strlen(ssid) == 0 || strlen(ssid) > 31) { - // fail SSID too long or missing! - DEBUG_WIFI("[AP] SSID too long or missing!\n"); + size_t ssid_len = ssid ? strlen(ssid) : 0; + if(ssid_len == 0 || ssid_len > 32) { + DEBUG_WIFI("[AP] SSID length %zu, too long or missing!\n", ssid_len); return false; } - if(passphrase && strlen(passphrase) > 0 && (strlen(passphrase) > 63 || strlen(passphrase) < 8)) { - // fail passphrase to long or short! - DEBUG_WIFI("[AP] fail passphrase too long or short!\n"); + size_t psk_len = psk ? strlen(psk) : 0; + if(psk_len > 0 && (psk_len > 64 || psk_len < 8)) { + DEBUG_WIFI("[AP] fail psk length %zu, too long or short!\n", psk_len); return false; } bool ret = true; struct softap_config conf; - strcpy(reinterpret_cast(conf.ssid), ssid); - conf.channel = channel; - conf.ssid_len = strlen(ssid); - conf.ssid_hidden = ssid_hidden; - conf.max_connection = max_connection; - conf.beacon_interval = 100; + memcpy(reinterpret_cast(conf.ssid), ssid, ssid_len); + if (ssid_len < sizeof(conf.ssid)) { + conf.ssid[ssid_len] = 0; + } + conf.ssid_len = ssid_len; - if(!passphrase || strlen(passphrase) == 0) { - conf.authmode = AUTH_OPEN; - *conf.password = 0; - } else { + if(psk_len) { conf.authmode = AUTH_WPA2_PSK; - strcpy(reinterpret_cast(conf.password), passphrase); + memcpy(reinterpret_cast(conf.password), psk, psk_len); + if (psk_len < sizeof(conf.password)) { + conf.password[psk_len] = 0; + } + } else { + conf.authmode = AUTH_OPEN; + conf.password[0] = 0; } + conf.channel = channel; + conf.ssid_hidden = ssid_hidden; + conf.max_connection = max_connection; + conf.beacon_interval = beacon_interval; + struct softap_config conf_compare; if(WiFi._persistent){ wifi_softap_get_config_default(&conf_compare); @@ -156,33 +168,30 @@ bool ESP8266WiFiAPClass::softAP(const char* ssid, const char* passphrase, int ch 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& passphrase, int channel, int ssid_hidden, int max_connection) { - return softAP(ssid.c_str(), passphrase.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); } /** @@ -214,16 +223,17 @@ 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; } struct dhcps_lease dhcp_lease; + dhcp_lease.enable = true; IPAddress ip = local_ip; ip[3] += 99; dhcp_lease.start_ip.addr = ip.v4(); @@ -233,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)) - { - DEBUG_WIFI("[APConfig] wifi_set_ip_info failed!\n"); - ret = false; - } - - // set lease time to 720min --> 12h - if(!dhcpSoftAP.set_dhcps_lease_time(720)) + auto& server = softAPDhcpServer(); + if(!server.set_dhcps_lease(&dhcp_lease)) { - DEBUG_WIFI("[APConfig] wifi_softap_set_dhcps_lease_time failed!\n"); + DEBUG_WIFI("[APConfig] server set_dhcps_lease failed!\n"); ret = false; } - 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; - } + // send ROUTER option with netif's gateway IP + server.setRouter(true); - 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)) { @@ -264,8 +261,7 @@ bool ESP8266WiFiAPClass::softAPConfig(IPAddress local_ip, IPAddress gateway, IPA DEBUG_WIFI("[APConfig] IP config Invalid?!\n"); ret = false; } else if(local_ip.v4() != info.ip.addr) { - ip = info.ip.addr; - DEBUG_WIFI("[APConfig] IP config not set correct?! new IP: %s\n", ip.toString().c_str()); + DEBUG_WIFI("[APConfig] IP config not set correct?! new IP: %s\n", IPAddress(info.ip.addr).toString().c_str()); ret = false; } } else { @@ -281,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; @@ -327,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. @@ -358,25 +365,32 @@ String ESP8266WiFiAPClass::softAPmacAddress(void) { String ESP8266WiFiAPClass::softAPSSID() const { struct softap_config config; wifi_softap_get_config(&config); - char* name = reinterpret_cast(config.ssid); - char ssid[sizeof(config.ssid) + 1]; - memcpy(ssid, name, sizeof(config.ssid)); - ssid[sizeof(config.ssid)] = '\0'; - return String(ssid); + String ssid; + ssid.concat(reinterpret_cast(config.ssid), config.ssid_len); + + return ssid; } /** - * Get the configured(Not-In-Flash) softAP PSK or PASSWORD. + * Get the configured(Not-In-Flash) softAP PSK. * @return String psk. */ String ESP8266WiFiAPClass::softAPPSK() const { struct softap_config config; wifi_softap_get_config(&config); - char* pass = reinterpret_cast(config.password); - char psk[sizeof(config.password) + 1]; - memcpy(psk, pass, sizeof(config.password)); - psk[sizeof(config.password)] = '\0'; - return String(psk); + char* ptr = reinterpret_cast(config.password); + String psk; + psk.concat(ptr, strnlen(ptr, sizeof(config.password))); + + 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 599c8e0e11..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* passphrase = NULL, int channel = 1, int ssid_hidden = 0, int max_connection = 4); - bool softAP(const String& ssid,const String& passphrase = 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 fb23b35e85..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,9 +52,8 @@ extern "C" { #include "debug.h" #include "include/WiFiState.h" -extern "C" void esp_schedule(); -extern "C" void esp_yield(); - +// see comments on wifi_station_hostname in LwipIntf.cpp +extern "C" char* wifi_station_hostname; // sdk's hostname location // ----------------------------------------------------------------------------------------------------------------------- // ------------------------------------------------- Generic WiFi function ----------------------------------------------- @@ -83,7 +85,7 @@ struct WiFiEventHandlerOpaque static std::list sCbEventList; -bool ESP8266WiFiGenericClass::_persistent = true; +bool ESP8266WiFiGenericClass::_persistent = false; WiFiMode_t ESP8266WiFiGenericClass::_forceSleepLastMode = WIFI_OFF; ESP8266WiFiGenericClass::ESP8266WiFiGenericClass() @@ -105,7 +107,7 @@ WiFiEventHandler ESP8266WiFiGenericClass::onStationModeConnected(std::function(WIFI_EVENT_STAMODE_CONNECTED, [f](System_Event_t* e) { auto& src = e->event_info.connected; WiFiEventStationModeConnected dst; - dst.ssid = String(reinterpret_cast(src.ssid)); + dst.ssid.concat(reinterpret_cast(src.ssid), src.ssid_len); memcpy(dst.bssid, src.bssid, 6); dst.channel = src.channel; f(dst); @@ -119,7 +121,7 @@ WiFiEventHandler ESP8266WiFiGenericClass::onStationModeDisconnected(std::functio WiFiEventHandler handler = std::make_shared(WIFI_EVENT_STAMODE_DISCONNECTED, [f](System_Event_t* e){ auto& src = e->event_info.disconnected; WiFiEventStationModeDisconnected dst; - dst.ssid = String(reinterpret_cast(src.ssid)); + dst.ssid.concat(reinterpret_cast(src.ssid), src.ssid_len); memcpy(dst.bssid, src.bssid, 6); dst.reason = static_cast(src.reason); f(dst); @@ -207,7 +209,10 @@ WiFiEventHandler ESP8266WiFiGenericClass::onSoftAPModeProbeRequestReceived(std:: WiFiEventHandler ESP8266WiFiGenericClass::onWiFiModeChange(std::function f) { WiFiEventHandler handler = std::make_shared(WIFI_EVENT_MODE_CHANGE, [f](System_Event_t* e){ - WiFiEventModeChange& dst = *reinterpret_cast(&e->event_info); + auto& src = e->event_info.opmode_changed; + WiFiEventModeChange dst; + dst.oldMode = (WiFiMode_t)src.old_opmode; + dst.newMode = (WiFiMode_t)src.new_opmode; f(dst); }); sCbEventList.push_back(handler); @@ -223,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) { @@ -296,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) @@ -327,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) { @@ -401,27 +411,10 @@ bool ESP8266WiFiGenericClass::getPersistent(){ * set new mode * @param m WiFiMode_t */ -bool ESP8266WiFiGenericClass::mode(WiFiMode_t m, WiFiState* state) { - if (m == WIFI_SHUTDOWN) { - return shutdown(0, state); - } - else if (m == WIFI_RESUME) { - return resumeFromShutdown(state); - } - else if (m & ~(WIFI_STA | WIFI_AP)) +bool ESP8266WiFiGenericClass::mode(WiFiMode_t m) { + if (m & ~(WIFI_STA | WIFI_AP)) { // any other bits than legacy disallowed return false; - - // m is now WIFI_STA, WIFI_AP or WIFI_AP_STA - if (state) - { - DEBUG_WIFI("core: state is useless without SHUTDOWN or RESUME\n"); - } - - if (wifi_fpm_get_sleep_type() != NONE_SLEEP_T) { - // wifi may have been put asleep by ESP8266WiFiGenericClass::preinitWiFiOff - wifi_fpm_do_wakeup(); - wifi_fpm_close(); } if(_persistent){ @@ -432,6 +425,15 @@ bool ESP8266WiFiGenericClass::mode(WiFiMode_t m, WiFiState* state) { 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(); + } + bool ret = false; ETS_UART_INTR_DISABLE(); if(_persistent) { @@ -449,10 +451,8 @@ bool ESP8266WiFiGenericClass::mode(WiFiMode_t m, WiFiState* state) { //tasks to wait correctly. constexpr unsigned int timeoutValue = 1000; //1 second if(can_yield()) { - using oneShot = esp8266::polledTimeout::oneShotFastMs; - oneShot timeout(timeoutValue); - while(wifi_get_opmode() != (uint8) m && !timeout) - delay(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) { @@ -460,6 +460,9 @@ bool ESP8266WiFiGenericClass::mode(WiFiMode_t m, WiFiState* state) { } } + if (backup_hostname[0]) + memcpy(wifi_station_hostname, backup_hostname, sizeof(backup_hostname)); + return ret; } @@ -529,9 +532,9 @@ bool ESP8266WiFiGenericClass::forceSleepBegin(uint32 sleepUs) { } wifi_fpm_set_sleep_type(MODEM_SLEEP_T); - delay(0); + esp_yield(); wifi_fpm_open(); - delay(0); + esp_yield(); auto ret = wifi_fpm_do_sleep(sleepUs); if (ret != 0) { @@ -570,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 } @@ -582,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 } @@ -594,110 +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; - delay(timeout_ms); - // will resume on timeout or when wifi_dns_found_callback fires - _dns_lookup_pending = false; - // will return here when dns_found_callback fires - 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 != 0) { - DEBUG_WIFI_GENERIC("[hostByName] Host: %s lookup error: %d!\n", aHostname, (int)err); - } else { + if (err == ERR_OK) { DEBUG_WIFI_GENERIC("[hostByName] Host: %s IP: %s\n", aHostname, aResult.toString().c_str()); + return 1; } - return (err == ERR_OK) ? 1 : 0; + 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; - delay(timeout_ms); - // will resume on timeout or when wifi_dns_found_callback fires - _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 != 0) { - DEBUG_WIFI_GENERIC("[hostByName] Host: %s lookup error: %d!\n", aHostname, (int)err); - } else { - DEBUG_WIFI_GENERIC("[hostByName] Host: %s IP: %s\n", aHostname, aResult.toString().c_str()); - } +int ESP8266WiFiGenericClass::hostByName(const char* aHostname, IPAddress& aResult, uint32_t timeout_ms) +{ + return hostByNameImpl(aHostname, aResult, timeout_ms, DNSResolveTypeDefault); +} - return (err == ERR_OK) ? 1 : 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 @@ -707,49 +702,53 @@ 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); } - esp_schedule(); // break delay in hostByName + + result->done = true; + esp_schedule(); } -uint32_t ESP8266WiFiGenericClass::shutdownCRC (const WiFiState* state) +uint32_t ESP8266WiFiGenericClass::shutdownCRC (const WiFiState& state) { - return state? crc32(&state->state, sizeof(state->state)): 0; + return crc32(&state.state, sizeof(state.state)); } -bool ESP8266WiFiGenericClass::shutdownValidCRC (const WiFiState* state) +bool ESP8266WiFiGenericClass::shutdownValidCRC (const WiFiState& state) { - return state && (crc32(&state->state, sizeof(state->state)) == state->crc); + return crc32(&state.state, sizeof(state.state)) == state.crc; } -bool ESP8266WiFiGenericClass::shutdown (uint32 sleepUs, WiFiState* state) +bool ESP8266WiFiGenericClass::shutdown (WiFiState& state, uint32 sleepUs) { bool persistent = _persistent; WiFiMode_t before_off_mode = getMode(); - if ((before_off_mode & WIFI_STA) && state) + if (before_off_mode & WIFI_STA) { - bool ret = wifi_get_ip_info(STATION_IF, &state->state.ip); + bool ret = wifi_get_ip_info(STATION_IF, &state.state.ip); if (!ret) { DEBUG_WIFI("core: error with wifi_get_ip_info(STATION_IF)\n"); return false; } - memset(state->state.fwconfig.bssid, 0xff, 6); - ret = wifi_station_get_config(&state->state.fwconfig); + memset(state.state.fwconfig.bssid, 0xff, 6); + ret = wifi_station_get_config(&state.state.fwconfig); if (!ret) { DEBUG_WIFI("core: error with wifi_station_get_config\n"); return false; } - state->state.channel = wifi_get_channel(); + state.state.channel = wifi_get_channel(); } // disable persistence in FW so in case of power failure @@ -766,57 +765,63 @@ bool ESP8266WiFiGenericClass::shutdown (uint32 sleepUs, WiFiState* state) } // WiFi is now in force-sleep mode + // finish filling state and process crc + + state.state.persistent = persistent; + state.state.mode = before_off_mode; - if (state) + uint8_t i = 0; + for (auto& ntp: state.state.ntp) { - // finish filling state and process crc + ntp = *sntp_getserver(i++); + } + i = 0; - state->state.persistent = persistent; - state->state.mode = before_off_mode; - uint8_t i = 0; - for (auto& ntp: state->state.ntp) - { - ntp = *sntp_getserver(i++); - } - i = 0; - for (auto& dns: state->state.dns) - dns = WiFi.dnsIP(i++); - state->crc = shutdownCRC(state); - DEBUG_WIFI("core: state is saved\n"); + for (auto& dns: state.state.dns) + { + dns = WiFi.dnsIP(i++); } + + state.crc = shutdownCRC(state); + DEBUG_WIFI("core: state is saved\n"); + return true; } -bool ESP8266WiFiGenericClass::resumeFromShutdown (WiFiState* state) +bool ESP8266WiFiGenericClass::shutdown (WiFiState& state) { + return shutdown(state, 0); +} + +bool ESP8266WiFiGenericClass::resumeFromShutdown (WiFiState& state) { if (wifi_fpm_get_sleep_type() != NONE_SLEEP_T) { wifi_fpm_do_wakeup(); wifi_fpm_close(); } - if (!state || shutdownCRC(state) != state->crc) + if (shutdownCRC(state) != state.crc) { - DEBUG_WIFI("core: resume: no state or bad crc\n"); + DEBUG_WIFI("core: resume: bad crc\n"); return false; } - persistent(state->state.persistent); + persistent(state.state.persistent); - if (!mode(state->state.mode)) + if (!mode(state.state.mode)) { - DEBUG_WIFI("core: resume: can't set wifi mode to %d\n", state->state.mode); + DEBUG_WIFI("core: resume: can't set wifi mode to %d\n", state.state.mode); return false; } - if (state->state.mode & WIFI_STA) + if (state.state.mode & WIFI_STA) { - IPAddress local(state->state.ip.ip); + IPAddress local(state.state.ip.ip); if (local) { DEBUG_WIFI("core: resume: static address '%s'\n", local.toString().c_str()); - WiFi.config(state->state.ip.ip, state->state.ip.gw, state->state.ip.netmask, state->state.dns[0], state->state.dns[1]); + WiFi.config(state.state.ip.ip, state.state.ip.gw, state.state.ip.netmask, state.state.dns[0], state.state.dns[1]); uint8_t i = 0; - for (const auto& ntp: state->state.ntp) + for (const auto& ntp: state.state.ntp) { IPAddress ip(ntp); if (ip.isSet()) @@ -826,11 +831,23 @@ bool ESP8266WiFiGenericClass::resumeFromShutdown (WiFiState* state) } } } - // state->state.fwconfig.bssid is not real bssid (it's what user may have provided when bssid_set==1) - auto beginResult = WiFi.begin((const char*)state->state.fwconfig.ssid, - (const char*)state->state.fwconfig.password, - state->state.channel, - nullptr/*(const uint8_t*)state->state.fwconfig.bssid*/, // <- try with gw's mac address? + + String ssid; + { + const char* ptr = reinterpret_cast(state.state.fwconfig.ssid); + ssid.concat(ptr, strnlen(ptr, sizeof(station_config::ssid))); + } + + String pass; + { + const char* ptr = reinterpret_cast(state.state.fwconfig.password); + pass.concat(ptr, strnlen(ptr, sizeof(station_config::password))); + } + + auto beginResult = WiFi.begin(ssid.c_str(), + pass.c_str(), + state.state.channel, + state.state.fwconfig.bssid, true); if (beginResult == WL_CONNECT_FAILED) { @@ -844,37 +861,19 @@ bool ESP8266WiFiGenericClass::resumeFromShutdown (WiFiState* state) } } - if (state->state.mode & WIFI_AP) + if (state.state.mode & WIFI_AP) { DEBUG_WIFI("core: resume AP mode TODO\n"); return false; } // success, invalidate saved state - state->crc++; + state.crc++; return true; } -//meant to be called from user-defined ::preinit() void ESP8266WiFiGenericClass::preinitWiFiOff () { - // https://github.com/esp8266/Arduino/issues/2111#issuecomment-224251391 - // WiFi.persistent(false); - // WiFi.mode(WIFI_OFF); - // WiFi.forceSleepBegin(); - - //WiFi.mode(WIFI_OFF) equivalent: - // datasheet: - // Set Wi-Fi working mode to Station mode, SoftAP - // or Station + SoftAP, and do not update flash - // (not persistent) - wifi_set_opmode_current(WIFI_OFF); - - //WiFi.forceSleepBegin(/*default*/0) equivalent: - // sleep forever until wifi_fpm_do_wakeup() is called - wifi_fpm_set_sleep_type(MODEM_SLEEP_T); - wifi_fpm_open(); - wifi_fpm_do_sleep(0xFFFFFFF); - - // use WiFi.forceSleepWake() to wake WiFi up + // It was meant to be called from user-defined ::preinit() + // It is now deprecated by enableWiFiAtBootTime() and __disableWiFiAtBootTime() } diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h index 6a91001949..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,19 +71,52 @@ 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); bool setSleepMode(WiFiSleepType_t type, uint8_t listenInterval = 0); + /** + * Set modem sleep mode (ESP32 compatibility) + * @param enable true to enable + * @return true if succeeded + */ + bool setSleep(bool enable) + { + if (enable) + { + return setSleepMode(WIFI_MODEM_SLEEP); + } + else + { + return setSleepMode(WIFI_NONE_SLEEP); + } + } + /** + * Set sleep mode (ESP32 compatibility) + * @param mode wifi_ps_type_t + * @return true if succeeded + */ + bool setSleep(wifi_ps_type_t mode) + { + return setSleepMode((WiFiSleepType_t)mode); + } + /** + * Get current sleep state (ESP32 compatibility) + * @return true if modem sleep is enabled + */ + bool getSleep() + { + return getSleepMode() == WIFI_MODEM_SLEEP; + } WiFiSleepType_t getSleepMode(); uint8_t getListenInterval (); @@ -87,9 +127,9 @@ class ESP8266WiFiGenericClass { void setOutputPower(float dBm); - void persistent(bool persistent); + static void persistent(bool persistent); - bool mode(WiFiMode_t, WiFiState* state = nullptr); + bool mode(WiFiMode_t); WiFiMode_t getMode(); bool enableSTA(bool enable); @@ -98,21 +138,23 @@ class ESP8266WiFiGenericClass { bool forceSleepBegin(uint32 sleepUs = 0); bool forceSleepWake(); - static uint32_t shutdownCRC (const WiFiState* state); - static bool shutdownValidCRC (const WiFiState* state); - static void preinitWiFiOff (); //meant to be called in user-defined preinit() + // wrappers around mode() and forceSleepBegin/Wake + // - sleepUs is WiFi.forceSleepBegin() parameter, 0 means forever + // - saveState is the user's state to hold configuration on restore + bool shutdown(WiFiState& stateSave); + bool shutdown(WiFiState& stateSave, uint32 sleepUs); + bool resumeFromShutdown(WiFiState& savedState); + + static bool shutdownValidCRC (const WiFiState& state); + static void preinitWiFiOff () __attribute__((deprecated("WiFi is off by default at boot, use enableWiFiAtBoot() for legacy behavior"))); protected: static bool _persistent; static WiFiMode_t _forceSleepLastMode; - static void _eventCallback(void *event); + static uint32_t shutdownCRC (const WiFiState& state); - // called by WiFi.mode(SHUTDOWN/RESTORE, state) - // - sleepUs is WiFi.forceSleepBegin() parameter, 0 = forever - // - saveState is the user's state to hold configuration on restore - bool shutdown (uint32 sleepUs = 0, WiFiState* stateSave = nullptr); - bool resumeFromShutdown (WiFiState* savedState = nullptr); + static void _eventCallback(void *event); // ---------------------------------------------------------------------------------------------- // ------------------------------------ Generic Network function -------------------------------- diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.cpp index d172486b9a..bcd433e1b1 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.cpp @@ -25,6 +25,7 @@ #include "PolledTimeout.h" #include "ESP8266WiFiMulti.h" +#include #include #include @@ -82,37 +83,29 @@ static void printWiFiStatus(wl_status_t status) */ static wl_status_t waitWiFiConnect(uint32_t connectTimeoutMs) { - wl_status_t status; - - // Set WiFi connect timeout - using esp8266::polledTimeout::oneShotMs; - oneShotMs connectTimeout(connectTimeoutMs); - - // Wait for WiFi status change or timeout - do { - // Refresh watchdog - delay(0); - - // Get WiFi status - status = WiFi.status(); - - // Check status - if (status == WL_CONNECTED) { - // Connected, print WiFi status - printWiFiStatus(status); - - // Return WiFi status - return status; - } else if (status == WL_CONNECT_FAILED) { - DEBUG_WIFI_MULTI("[WIFIM] Connect failed\n"); - - // Return WiFi connect failed - return WL_CONNECT_FAILED; - } - } while (!connectTimeout); + wl_status_t status = WL_CONNECT_FAILED; + // 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; + }, 100); + + // Check status + if (status == WL_CONNECTED) { + // Connected, print WiFi status + printWiFiStatus(status); - DEBUG_WIFI_MULTI("[WIFIM] Connect timeout\n"); + // Return WiFi status + return status; + } else if (status == WL_CONNECT_FAILED) { + DEBUG_WIFI_MULTI("[WIFIM] Connect failed\n"); + } else { + DEBUG_WIFI_MULTI("[WIFIM] Connect timeout\n"); + } + // Return WiFi connect failed return WL_CONNECT_FAILED; } @@ -242,24 +235,18 @@ int8_t ESP8266WiFiMulti::startScan() // Start wifi scan in async mode WiFi.scanNetworks(true); - // Set WiFi scan timeout - using esp8266::polledTimeout::oneShotMs; - oneShotMs scanTimeout(WIFI_SCAN_TIMEOUT_MS); - // Wait for WiFi scan change or timeout - do { - // Refresh watchdog - delay(0); - - // Check scan timeout which may occur when scan does not report completion - if (scanTimeout) { - DEBUG_WIFI_MULTI("[WIFIM] Scan timeout\n"); - return WIFI_SCAN_FAILED; - } - - // Get scan result - scanResult = WiFi.scanComplete(); - } while (scanResult < 0); + // 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; + }, 100); + // Check for scan timeout which may occur when scan does not report completion + if (scanResult < 0) { + DEBUG_WIFI_MULTI("[WIFIM] Scan timeout\n"); + return WIFI_SCAN_FAILED; + } // Print WiFi scan result printWiFiScan(); @@ -273,7 +260,7 @@ int8_t ESP8266WiFiMulti::startScan() * @param connectTimeoutMs * WiFi connect timeout in ms * @return - * WiFi conneciton status + * WiFi connection status */ wl_status_t ESP8266WiFiMulti::connectWiFiMulti(uint32_t connectTimeoutMs) { @@ -326,15 +313,21 @@ wl_status_t ESP8266WiFiMulti::connectWiFiMulti(uint32_t connectTimeoutMs) } DEBUG_WIFI_MULTI("\n"); + // Create indices for AP connection failures + uint8_t connectSkipIndex[_APlist.size()]; + memset(connectSkipIndex, 0, sizeof(connectSkipIndex)); + // Connect to known WiFi AP's sorted by RSSI for (int8_t i = 0; i < numNetworks; i++) { // Get network information WiFi.getNetworkInfo(known[i], ssid, encType, rssi, bssid, channel, hidden); - for (auto entry : _APlist) { + for (uint8_t j = 0; j < _APlist.size(); j++) { + auto &entry = _APlist[j]; + // Check SSID if (ssid == entry.ssid) { - DEBUG_WIFI_MULTI("[WIFIM] Connecting %s\n", ssid); + DEBUG_WIFI_MULTI("[WIFIM] Connecting %s\n", ssid.c_str()); // Connect to WiFi WiFi.begin(ssid, entry.passphrase, channel, bssid); @@ -343,13 +336,33 @@ wl_status_t ESP8266WiFiMulti::connectWiFiMulti(uint32_t connectTimeoutMs) if (waitWiFiConnect(connectTimeoutMs) == WL_CONNECTED) { return WL_CONNECTED; } + + // Failed to connect, skip for hidden SSID connects + connectSkipIndex[j] = true; + } + } + } + + // Try to connect to hidden AP's which are not reported by WiFi scan + for (uint8_t i = 0; i < _APlist.size(); i++) { + auto &entry = _APlist[i]; + + if (!connectSkipIndex[i]) { + DEBUG_WIFI_MULTI("[WIFIM] Try hidden connect %s\n", entry.ssid); + + // Connect to WiFi + WiFi.begin(entry.ssid, entry.passphrase); + + // Wait for status change + if (waitWiFiConnect(connectTimeoutMs) == WL_CONNECTED) { + return WL_CONNECTED; } } } - DEBUG_WIFI_MULTI("[WIFIM] Could not connect\n", ssid); + DEBUG_WIFI_MULTI("[WIFIM] Could not connect\n"); - // Coult not connect to any WiFi network + // Could not connect to any WiFi network return WL_CONNECT_FAILED; } @@ -509,7 +522,7 @@ void ESP8266WiFiMulti::printWiFiScan() rssi, (encryptionType == ENC_TYPE_NONE) ? ' ' : '*', ssid.c_str()); - delay(0); + esp_yield(); } #endif } 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-WPS.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiSTA-WPS.cpp index 99d27ba7a3..b93c77da9c 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiSTA-WPS.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiSTA-WPS.cpp @@ -30,6 +30,8 @@ static void wifi_wps_status_cb(wps_cb_status status); +static bool _wps_config_pending = false; + /** * WPS config * so far only WPS_TYPE_PBC is supported (SDK 1.2.0) @@ -70,8 +72,9 @@ bool ESP8266WiFiSTAClass::beginWPSConfig(void) { return false; } - esp_yield(); + _wps_config_pending = true; // will resume when wifi_wps_status_cb fires + esp_suspend([]() { return _wps_config_pending; }); return true; } @@ -107,5 +110,6 @@ void wifi_wps_status_cb(wps_cb_status status) { } // TODO user function to get status - esp_schedule(); // resume beginWPSConfig + _wps_config_pending = false; // resume beginWPSConfig + esp_schedule(); } diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp index a75c115c26..dfea41298b 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.cpp @@ -28,6 +28,8 @@ #include "PolledTimeout.h" #include "LwipIntf.h" +#include + #include "c_types.h" #include "ets_sys.h" #include "os_type.h" @@ -44,9 +46,6 @@ extern "C" { #include "debug.h" -extern "C" void esp_schedule(); -extern "C" void esp_yield(); - // ----------------------------------------------------------------------------------------------------------------------- // ---------------------------------------------------- Private functions ------------------------------------------------ // ----------------------------------------------------------------------------------------------------------------------- @@ -62,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"); @@ -95,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 @@ -136,7 +145,7 @@ wl_status_t ESP8266WiFiSTAClass::begin(const char* ssid, const char *passphrase, int passphraseLen = passphrase == nullptr ? 0 : strlen(passphrase); if(passphraseLen > 64) { // fail passphrase too long! - return WL_CONNECT_FAILED; + return WL_WRONG_PASSWORD; } struct station_config conf; @@ -157,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; @@ -326,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 @@ -340,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) @@ -356,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); } @@ -431,10 +501,10 @@ int8_t ESP8266WiFiSTAClass::waitForConnectResult(unsigned long timeoutLength) { if((wifi_get_opmode() & 1) == 0) { return WL_DISCONNECTED; } - using esp8266::polledTimeout::oneShot; - oneShot timeout(timeoutLength); // number of milliseconds to wait before returning timeout error + // if probing doesn't trip, this yields + using oneShotYieldMs = esp8266::polledTimeout::timeoutTemplate; + oneShotYieldMs timeout(timeoutLength); // number of milliseconds to wait before returning timeout error while(!timeout) { - yield(); if(status() != WL_DISCONNECTED) { return status(); } @@ -504,6 +574,18 @@ IPAddress ESP8266WiFiSTAClass::dnsIP(uint8_t dns_no) { return IPAddress(dns_getserver(dns_no)); } +/** + * Get the broadcast ip address. + * @return IPAddress Broadcast IP + */ +IPAddress ESP8266WiFiSTAClass::broadcastIP() +{ + struct ip_info ip; + wifi_get_ip_info(STATION_IF, &ip); + + return IPAddress(ip.ip.addr | ~(ip.netmask.addr)); +} + /** * Return Connection status. * @return one of the value defined in wl_status_t @@ -564,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 6dbb6406c1..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(); @@ -70,12 +79,14 @@ class ESP8266WiFiSTAClass: public LwipIntf { IPAddress gatewayIP(); IPAddress dnsIP(uint8_t dns_no = 0); + IPAddress broadcastIP(); // STA WiFi info wl_status_t status(); String SSID() const; 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 65878a3d5b..1fdc6e04c7 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiScan.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiScan.cpp @@ -36,9 +36,7 @@ extern "C" { } #include "debug.h" - -extern "C" void esp_schedule(); -extern "C" void esp_yield(); +#include // ----------------------------------------------------------------------------------------------------------------------- // ---------------------------------------------------- Private functions ------------------------------------------------ @@ -94,11 +92,13 @@ int8_t ESP8266WiFiScanClass::scanNetworks(bool async, bool show_hidden, uint8 ch ESP8266WiFiScanClass::_scanStarted = true; if(ESP8266WiFiScanClass::_scanAsync) { - delay(0); // time for the OS to trigger the scan + esp_yield(); // time for the OS to trigger the scan return WIFI_SCAN_RUNNING; } - esp_yield(); // will resume when _scanDone fires + // will resume when _scanDone fires + esp_suspend([]() { return !ESP8266WiFiScanClass::_scanComplete && ESP8266WiFiScanClass::_scanStarted; }); + return ESP8266WiFiScanClass::_scanCount; } else { return WIFI_SCAN_FAILED; @@ -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 @@ -322,7 +345,7 @@ void ESP8266WiFiScanClass::_scanDone(void* result, int status) { ESP8266WiFiScanClass::_scanStarted = false; ESP8266WiFiScanClass::_scanComplete = true; - if(!ESP8266WiFiScanClass::_scanAsync) { + if (!ESP8266WiFiScanClass::_scanAsync) { esp_schedule(); // resume scanNetworks } else if (ESP8266WiFiScanClass::_onComplete) { ESP8266WiFiScanClass::_onComplete(ESP8266WiFiScanClass::_scanCount); 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/ESP8266WiFiType.h b/libraries/ESP8266WiFi/src/ESP8266WiFiType.h index 86803edce4..9538b132c2 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiType.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiType.h @@ -34,8 +34,7 @@ typedef enum WiFiMode { - WIFI_OFF = 0, WIFI_STA = 1, WIFI_AP = 2, WIFI_AP_STA = 3, - /* these two pseudo modes are experimental: */ WIFI_SHUTDOWN = 4, WIFI_RESUME = 8 + WIFI_OFF = 0, WIFI_STA = 1, WIFI_AP = 2, WIFI_AP_STA = 3 } WiFiMode_t; typedef enum WiFiPhyMode @@ -48,6 +47,13 @@ typedef enum WiFiSleepType WIFI_NONE_SLEEP = 0, WIFI_LIGHT_SLEEP = 1, WIFI_MODEM_SLEEP = 2 } WiFiSleepType_t; +// ESP32 compatibility +typedef enum wifi_ps_type +{ + WIFI_PS_NONE = WIFI_NONE_SLEEP, + WIFI_PS_MIN_MODEM = WIFI_MODEM_SLEEP, + WIFI_PS_MAX_MODEM = WIFI_LIGHT_SLEEP, +} wifi_ps_type_t; typedef enum WiFiEvent { 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 5aa09b8874..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" @@ -40,6 +38,7 @@ extern "C" #include "lwip/netif.h" #include #include "c_types.h" +#include uint16_t WiFiClient::_localPort = 0; @@ -76,14 +75,14 @@ WiFiClient* SList::_s_first = 0; WiFiClient::WiFiClient() -: _client(0) +: _client(0), _owned(0) { _timeout = 5000; WiFiClient::_add(this); } WiFiClient::WiFiClient(ClientContext* client) -: _client(client) +: _client(client), _owned(0) { _timeout = 5000; _client->ref(); @@ -100,11 +99,16 @@ WiFiClient::~WiFiClient() _client->unref(); } +std::unique_ptr WiFiClient::clone() const { + return std::make_unique(*this); +} + WiFiClient::WiFiClient(const WiFiClient& other) { _client = other._client; _timeout = other._timeout; _localPort = other._localPort; + _owned = other._owned; if (_client) _client->ref(); WiFiClient::_add(this); @@ -117,6 +121,7 @@ WiFiClient& WiFiClient::operator=(const WiFiClient& other) _client = other._client; _timeout = other._timeout; _localPort = other._localPort; + _owned = other._owned; if (_client) _client->ref(); return *this; @@ -212,23 +217,19 @@ size_t WiFiClient::write(const uint8_t *buf, size_t size) return 0; } _client->setTimeout(_timeout); - return _client->write(buf, size); -} - -size_t WiFiClient::write(Stream& stream, size_t unused) -{ - (void) unused; - return WiFiClient::write(stream); + return _client->write((const char*)buf, size); } size_t WiFiClient::write(Stream& stream) { + // (this method is deprecated) + if (!_client || !stream.available()) { return 0; } - _client->setTimeout(_timeout); - return _client->write(stream); + // core up to 2.7.4 was equivalent to this + return stream.sendAll(this); } size_t WiFiClient::write_P(PGM_P buf, size_t size) @@ -238,13 +239,14 @@ size_t WiFiClient::write_P(PGM_P buf, size_t size) return 0; } _client->setTimeout(_timeout); - return _client->write_P(buf, size); + StreamConstPtr nopeek(buf, size); + return nopeek.sendAll(this); } int WiFiClient::available() { if (!_client) - return false; + return 0; int result = _client->getSize(); @@ -262,10 +264,14 @@ int WiFiClient::read() return _client->read(); } - int WiFiClient::read(uint8_t* buf, size_t size) { - return (int) _client->read(reinterpret_cast(buf), size); + return (int)_client->read((char*)buf, size); +} + +int WiFiClient::read(char* buf, size_t size) +{ + return (int)_client->read(buf, size); } int WiFiClient::peek() @@ -304,7 +310,7 @@ bool WiFiClient::flush(unsigned int maxWaitMs) if (maxWaitMs == 0) maxWaitMs = WIFICLIENT_MAX_FLUSH_WAIT_MS; - return _client->wait_until_sent(maxWaitMs); + return _client->wait_until_acked(maxWaitMs); } bool WiFiClient::stop(unsigned int maxWaitMs) @@ -370,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) { @@ -380,9 +397,18 @@ void WiFiClient::stopAll() void WiFiClient::stopAllExcept(WiFiClient* except) { + // Stop all will look at the lowest-level wrapper connections only + while (except->_owned) { + except = except->_owned; + } for (WiFiClient* it = _s_first; it; it = it->_next) { - if (it != except) { - it->stop(); + WiFiClient* conn = it; + // Find the lowest-level owner of the current list entry + while (conn->_owned) { + conn = conn->_owned; + } + if (conn != except) { + conn->stop(); } } } @@ -412,3 +438,28 @@ uint8_t WiFiClient::getKeepAliveCount () const { return _client->getKeepAliveCount(); } + +bool WiFiClient::hasPeekBufferAPI () const +{ + return true; +} + +// return a pointer to available data buffer (size = peekAvailable()) +// semantic forbids any kind of read() before calling peekConsume() +const char* WiFiClient::peekBuffer () +{ + return _client? _client->peekBuffer(): nullptr; +} + +// return number of byte accessible by peekBuffer() +size_t WiFiClient::peekAvailable () +{ + return _client? _client->peekAvailable(): 0; +} + +// consume bytes after use (see peekBuffer) +void WiFiClient::peekConsume (size_t consume) +{ + if (_client) + _client->peekConsume(consume); +} diff --git a/libraries/ESP8266WiFi/src/WiFiClient.h b/libraries/ESP8266WiFi/src/WiFiClient.h index 41c099bfc2..711adb6204 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.h +++ b/libraries/ESP8266WiFi/src/WiFiClient.h @@ -52,6 +52,18 @@ class WiFiClient : public Client, public SList { WiFiClient(const WiFiClient&); WiFiClient& operator=(const WiFiClient&); + // b/c this is both a real class and a virtual parent of the secure client, make sure + // there's a safe way to copy from the pointer without 'slicing' it; i.e. only the base + // portion of a derived object will be copied, and the polymorphic behavior will be corrupted. + // + // this class still implements the copy and assignment though, so this is not yet enforced + // (but, *should* be inside the Core itself, see httpclient & server) + // + // ref. + // - https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rc-copy-virtual + // - https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rh-copy + virtual std::unique_ptr clone() const; + virtual uint8_t status(); virtual int connect(IPAddress ip, uint16_t port) override; virtual int connect(const char *host, uint16_t port) override; @@ -59,30 +71,30 @@ class WiFiClient : public Client, public SList { virtual size_t write(uint8_t) override; virtual size_t write(const uint8_t *buf, size_t size) override; virtual size_t write_P(PGM_P buf, size_t size); + [[ deprecated("use stream.sendHow(client...)") ]] size_t write(Stream& stream); - // This one is deprecated, use write(Stream& instead) - size_t write(Stream& stream, size_t unitSize) __attribute__ ((deprecated)); - virtual int available() override; virtual int read() override; - virtual int read(uint8_t *buf, size_t size) override; + virtual int read(uint8_t* buf, size_t size) override; + int read(char* buf, size_t size); + virtual int peek() override; virtual size_t peekBytes(uint8_t *buffer, size_t length); size_t peekBytes(char *buffer, size_t length) { return peekBytes((uint8_t *) buffer, length); } - virtual void flush() override { (void)flush(0); } + virtual void flush() override { (void)flush(0); } // wait for all outgoing characters to be sent, output buffer should be empty after this call virtual void stop() override { (void)stop(0); } bool flush(unsigned int maxWaitMs); bool stop(unsigned int maxWaitMs); 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; } @@ -91,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 @@ -120,6 +132,26 @@ class WiFiClient : public Client, public SList { bool getSync() const; void setSync(bool sync); + // peek buffer API is present + virtual bool hasPeekBufferAPI () const override; + + // return number of byte accessible by peekBuffer() + virtual size_t peekAvailable () override; + + // return a pointer to available data buffer (size = peekAvailable()) + // semantic forbids any kind of read() before calling peekConsume() + virtual const char* peekBuffer () override; + + // consume bytes after use (see peekBuffer) + virtual void peekConsume (size_t consume) override; + + 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); @@ -129,6 +161,7 @@ class WiFiClient : public Client, public SList { void _err(int8_t err); ClientContext* _client; + WiFiClient* _owned; static uint16_t _localPort; }; diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp index 48dc531a5f..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 @@ -44,7 +42,6 @@ extern "C" { #include "lwip/netif.h" #include #include "c_types.h" -#include "coredecls.h" #include #include #include @@ -93,6 +90,8 @@ void WiFiClientSecureCtx::_clear() { _session = nullptr; _cipher_list = nullptr; _cipher_cnt = 0; + _tls_min = BR_TLS10; + _tls_max = BR_TLS12; } void WiFiClientSecureCtx::_clearAuthenticationSettings() { @@ -125,7 +124,7 @@ WiFiClientSecureCtx::~WiFiClientSecureCtx() { WiFiClientSecureCtx::WiFiClientSecureCtx(ClientContext* client, const X509List *chain, const PrivateKey *sk, int iobuf_in_size, int iobuf_out_size, ServerSessions *cache, - const X509List *client_CA_ta) { + const X509List *client_CA_ta, int tls_min, int tls_max) { _clear(); _clearAuthenticationSettings(); stack_thunk_add_ref(); @@ -133,6 +132,8 @@ WiFiClientSecureCtx::WiFiClientSecureCtx(ClientContext* client, _iobuf_out_size = iobuf_out_size; _client = client; _client->ref(); + _tls_min = tls_min; + _tls_max = tls_max; if (!_connectSSLServerRSA(chain, sk, cache, client_CA_ta)) { _client->unref(); _client = nullptr; @@ -144,7 +145,7 @@ WiFiClientSecureCtx::WiFiClientSecureCtx(ClientContext *client, const X509List *chain, unsigned cert_issuer_key_type, const PrivateKey *sk, int iobuf_in_size, int iobuf_out_size, ServerSessions *cache, - const X509List *client_CA_ta) { + const X509List *client_CA_ta, int tls_min, int tls_max) { _clear(); _clearAuthenticationSettings(); stack_thunk_add_ref(); @@ -152,6 +153,8 @@ WiFiClientSecureCtx::WiFiClientSecureCtx(ClientContext *client, _iobuf_out_size = iobuf_out_size; _client = client; _client->ref(); + _tls_min = tls_min; + _tls_max = tls_max; if (!_connectSSLServerEC(chain, cert_issuer_key_type, sk, cache, client_CA_ta)) { _client->unref(); _client = nullptr; @@ -247,20 +250,54 @@ 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 () { + // 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 + if (_run_until(BR_SSL_SENDAPP) < 0) { + return 0; + } + if (br_ssl_engine_current_state(_eng) & BR_SSL_SENDAPP) { + size_t sendapp_len; + (void)br_ssl_engine_sendapp_buf(_eng, &sendapp_len); + // We want to call br_ssl_engine_sendapp_ack(0) but 0 is forbidden (bssl doc). + // After checking br_ssl_engine_sendapp_buf() src code, + // it seems that it is OK to not call ack when the buffer is left untouched. + //forbidden: br_ssl_engine_sendapp_ack(_eng, 0); + return (int)sendapp_len; + } + return 0; } 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; } @@ -306,28 +343,13 @@ size_t WiFiClientSecureCtx::write_P(PGM_P buf, size_t size) { return _write((const uint8_t *)buf, size, true); } -// We have to manually read and send individual chunks. size_t WiFiClientSecureCtx::write(Stream& stream) { - size_t totalSent = 0; - size_t countRead; - size_t countSent; - - 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; } - do { - uint8_t temp[256]; // Temporary chunk size same as ClientContext - countSent = 0; - countRead = stream.readBytes(temp, sizeof(temp)); - if (countRead) { - countSent = _write((const uint8_t*)temp, countRead, true); - totalSent += countSent; - } - yield(); // Feed the WDT - } while ((countSent == countRead) && (countSent > 0)); - return totalSent; + return stream.sendAll(this); } int WiFiClientSecureCtx::read(uint8_t *buf, size_t size) { @@ -335,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; } @@ -355,23 +385,39 @@ 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. } +// return a pointer to available data buffer (size = peekAvailable()) +// semantic forbids any kind of read() before calling peekConsume() +const char* WiFiClientSecureCtx::peekBuffer () +{ + return (const char*)_recvapp_buf; +} + +// consume bytes after use (see peekBuffer) +void WiFiClientSecureCtx::peekConsume (size_t consume) +{ + // according to WiFiClientSecureCtx::read: + br_ssl_engine_recvapp_ack(_eng, consume); + _recvapp_buf = nullptr; + _recvapp_len = 0; +} + int WiFiClientSecureCtx::read() { uint8_t c; 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? } @@ -392,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; } @@ -412,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(); } @@ -920,7 +970,7 @@ extern "C" { uint16_t suites[cipher_cnt]; memcpy_P(suites, cipher_list, cipher_cnt * sizeof(cipher_list[0])); br_ssl_client_zero(cc); - br_ssl_engine_add_flags(&cc->eng, BR_OPT_NO_RENEGOTIATION); // forbid SSL renegociation, as we free the Private Key after handshake + br_ssl_engine_add_flags(&cc->eng, BR_OPT_NO_RENEGOTIATION); // forbid SSL renegotiation, as we free the Private Key after handshake br_ssl_engine_set_versions(&cc->eng, BR_TLS10, BR_TLS12); br_ssl_engine_set_suites(&cc->eng, suites, (sizeof suites) / (sizeof suites[0])); br_ssl_client_set_default_rsapub(cc); @@ -946,7 +996,7 @@ extern "C" { uint16_t suites[cipher_cnt]; memcpy_P(suites, cipher_list, cipher_cnt * sizeof(cipher_list[0])); br_ssl_server_zero(cc); - br_ssl_engine_add_flags(&cc->eng, BR_OPT_NO_RENEGOTIATION); // forbid SSL renegociation, as we free the Private Key after handshake + br_ssl_engine_add_flags(&cc->eng, BR_OPT_NO_RENEGOTIATION); // forbid SSL renegotiation, as we free the Private Key after handshake br_ssl_engine_set_versions(&cc->eng, BR_TLS10, BR_TLS12); br_ssl_engine_set_suites(&cc->eng, suites, (sizeof suites) / (sizeof suites[0])); #ifndef BEARSSL_SSL_BASIC @@ -989,6 +1039,17 @@ bool WiFiClientSecureCtx::setCiphers(const std::vector& list) { return setCiphers(&list[0], list.size()); } +bool WiFiClientSecureCtx::setSSLVersion(uint32_t min, uint32_t max) { + if ( ((min != BR_TLS10) && (min != BR_TLS11) && (min != BR_TLS12)) || + ((max != BR_TLS10) && (max != BR_TLS11) && (max != BR_TLS12)) || + (max < min) ) { + return false; // Invalid options + } + _tls_min = min; + _tls_max = max; + return true; +} + // Installs the appropriate X509 cert validation method for a client connection bool WiFiClientSecureCtx::_installClientX509Validator() { if (_use_insecure || _use_fingerprint || _use_self_signed) { @@ -1045,6 +1106,17 @@ bool WiFiClientSecureCtx::_installClientX509Validator() { return true; } +std::shared_ptr WiFiClientSecureCtx::_alloc_iobuf(size_t sz) +{ // Allocate buffer with preference to IRAM + HeapSelectIram primary; + auto sptr = std::shared_ptr(new (std::nothrow) unsigned char[sz], std::default_delete()); + if (!sptr) { + HeapSelectDram alternate; + sptr = std::shared_ptr(new (std::nothrow) unsigned char[sz], std::default_delete()); + } + return sptr; +} + // Called by connect() to do the actual SSL setup and handshake. // Returns if the SSL handshake succeeded. bool WiFiClientSecureCtx::_connectSSL(const char* hostName) { @@ -1061,17 +1133,12 @@ bool WiFiClientSecureCtx::_connectSSL(const char* hostName) { _sc = std::make_shared(); _eng = &_sc->eng; // Allocation/deallocation taken care of by the _sc shared_ptr - //C This was borrowed from @earlephilhower PoC, to exemplify the use of IRAM. - //C Is this something we want to keep in the final release? - { // ESP.setIramHeap(); would be an alternative to using a class to set a scope for IRAM usage. - HeapSelectIram ephemeral; - _iobuf_in = std::shared_ptr(new (std::nothrow) unsigned char[_iobuf_in_size], std::default_delete()); - _iobuf_out = std::shared_ptr(new (std::nothrow) unsigned char[_iobuf_out_size], std::default_delete()); - DBG_MMU_PRINTF("\n_iobuf_in: %p\n", _iobuf_in.get()); - DBG_MMU_PRINTF( "_iobuf_out: %p\n", _iobuf_out.get()); - DBG_MMU_PRINTF( "_iobuf_in_size: %u\n", _iobuf_in_size); - DBG_MMU_PRINTF( "_iobuf_out_size: %u\n", _iobuf_out_size); - } // ESP.resetHeap(); + _iobuf_in = _alloc_iobuf(_iobuf_in_size); + _iobuf_out = _alloc_iobuf(_iobuf_out_size); + DBG_MMU_PRINTF("\n_iobuf_in: %p\n", _iobuf_in.get()); + DBG_MMU_PRINTF( "_iobuf_out: %p\n", _iobuf_out.get()); + DBG_MMU_PRINTF( "_iobuf_in_size: %u\n", _iobuf_in_size); + DBG_MMU_PRINTF( "_iobuf_out_size: %u\n", _iobuf_out_size); if (!_sc || !_iobuf_in || !_iobuf_out) { _freeSSL(); // Frees _sc, _iobuf* @@ -1094,6 +1161,7 @@ bool WiFiClientSecureCtx::_connectSSL(const char* hostName) { return false; } br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size); + br_ssl_engine_set_versions(_eng, _tls_min, _tls_max); // Apply any client certificates, if supplied. if (_sk && _sk->isRSA()) { @@ -1186,15 +1254,12 @@ bool WiFiClientSecureCtx::_connectSSLServerRSA(const X509List *chain, _oom_err = false; _sc_svr = std::make_shared(); _eng = &_sc_svr->eng; // Allocation/deallocation taken care of by the _sc shared_ptr - { // ESP.setIramHeap(); - HeapSelectIram ephemeral; - _iobuf_in = std::shared_ptr(new (std::nothrow) unsigned char[_iobuf_in_size], std::default_delete()); - _iobuf_out = std::shared_ptr(new (std::nothrow) unsigned char[_iobuf_out_size], std::default_delete()); - DBG_MMU_PRINTF("\n_iobuf_in: %p\n", _iobuf_in.get()); - DBG_MMU_PRINTF( "_iobuf_out: %p\n", _iobuf_out.get()); - DBG_MMU_PRINTF( "_iobuf_in_size: %u\n", _iobuf_in_size); - DBG_MMU_PRINTF( "_iobuf_out_size: %u\n", _iobuf_out_size); - } // ESP.resetHeap(); + _iobuf_in = _alloc_iobuf(_iobuf_in_size); + _iobuf_out = _alloc_iobuf(_iobuf_out_size); + DBG_MMU_PRINTF("\n_iobuf_in: %p\n", _iobuf_in.get()); + DBG_MMU_PRINTF( "_iobuf_out: %p\n", _iobuf_out.get()); + DBG_MMU_PRINTF( "_iobuf_in_size: %u\n", _iobuf_in_size); + DBG_MMU_PRINTF( "_iobuf_out_size: %u\n", _iobuf_out_size); if (!_sc_svr || !_iobuf_in || !_iobuf_out) { _freeSSL(); @@ -1208,6 +1273,7 @@ bool WiFiClientSecureCtx::_connectSSLServerRSA(const X509List *chain, sk ? sk->getRSA() : nullptr, BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN, br_rsa_private_get_default(), br_rsa_pkcs1_sign_get_default()); br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size); + br_ssl_engine_set_versions(_eng, _tls_min, _tls_max); if (cache != nullptr) br_ssl_server_set_cache(_sc_svr.get(), cache->getCache()); if (client_CA_ta && !_installServerX509Validator(client_CA_ta)) { @@ -1232,15 +1298,12 @@ bool WiFiClientSecureCtx::_connectSSLServerEC(const X509List *chain, _oom_err = false; _sc_svr = std::make_shared(); _eng = &_sc_svr->eng; // Allocation/deallocation taken care of by the _sc shared_ptr - { // ESP.setIramHeap(); - HeapSelectIram ephemeral; - _iobuf_in = std::shared_ptr(new (std::nothrow) unsigned char[_iobuf_in_size], std::default_delete()); - _iobuf_out = std::shared_ptr(new (std::nothrow) unsigned char[_iobuf_out_size], std::default_delete()); - DBG_MMU_PRINTF("\n_iobuf_in: %p\n", _iobuf_in.get()); - DBG_MMU_PRINTF( "_iobuf_out: %p\n", _iobuf_out.get()); - DBG_MMU_PRINTF( "_iobuf_in_size: %u\n", _iobuf_in_size); - DBG_MMU_PRINTF( "_iobuf_out_size: %u\n", _iobuf_out_size); - } // ESP.resetHeap(); + _iobuf_in = _alloc_iobuf(_iobuf_in_size); + _iobuf_out = _alloc_iobuf(_iobuf_out_size); + DBG_MMU_PRINTF("\n_iobuf_in: %p\n", _iobuf_in.get()); + DBG_MMU_PRINTF( "_iobuf_out: %p\n", _iobuf_out.get()); + DBG_MMU_PRINTF( "_iobuf_in_size: %u\n", _iobuf_in_size); + DBG_MMU_PRINTF( "_iobuf_out_size: %u\n", _iobuf_out_size); if (!_sc_svr || !_iobuf_in || !_iobuf_out) { _freeSSL(); @@ -1254,6 +1317,7 @@ bool WiFiClientSecureCtx::_connectSSLServerEC(const X509List *chain, sk ? sk->getEC() : nullptr, BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN, cert_issuer_key_type, br_ssl_engine_get_ec(_eng), br_ecdsa_i15_sign_asn1); br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size); + br_ssl_engine_set_versions(_eng, _tls_min, _tls_max); if (cache != nullptr) br_ssl_server_set_cache(_sc_svr.get(), cache->getCache()); if (client_CA_ta && !_installServerX509Validator(client_CA_ta)) { @@ -1271,6 +1335,7 @@ bool WiFiClientSecureCtx::_connectSSLServerEC(const X509List *chain, (void) chain; (void) cert_issuer_key_type; (void) sk; + (void) cache; (void) client_CA_ta; DEBUG_BSSL("_connectSSLServerEC: Attempting to use EC cert in minimal cipher mode (no EC)\n"); return false; diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h index 858f573e2a..e0cb44928f 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h @@ -34,11 +34,18 @@ 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; + // TODO: usage is invalid b/c of deleted copy, but this will only trigger an error when it is actually used by something + // 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 nullptr; + } + int connect(IPAddress ip, uint16_t port) override; int connect(const String& host, uint16_t port) override; int connect(const char* name, uint16_t port) override; @@ -48,6 +55,7 @@ class WiFiClientSecureCtx : public WiFiClient { size_t write_P(PGM_P buf, size_t size) override; size_t write(Stream& stream); // Note this is not virtual int read(uint8_t *buf, size_t size) override; + int read(char *buf, size_t size) { return read((uint8_t*)buf, size); } int available() override; int read() override; int peek() override; @@ -57,6 +65,8 @@ class WiFiClientSecureCtx : public WiFiClient { void flush() override { (void)flush(0); } void stop() override { (void)stop(0); } + int availableForWrite() override; + // Allow sessions to be saved/restored automatically to a memory area void setSession(Session *session) { _session = session; } @@ -110,7 +120,7 @@ class WiFiClientSecureCtx : public WiFiClient { int getLastSSLError(char *dest = NULL, size_t len = 0); // Attach a preconfigured certificate store - void setCertStore(CertStore *certStore) { + void setCertStore(CertStoreBase *certStore) { _certStore = certStore; } @@ -120,6 +130,23 @@ class WiFiClientSecureCtx : public WiFiClient { bool setCiphers(const std::vector& list); bool setCiphersLessSecure(); // Only use the limited set of RSA ciphers without EC + // Limit the TLS versions BearSSL will connect with. Default is + // BR_TLS10...BR_TLS12 + bool setSSLVersion(uint32_t min = BR_TLS10, uint32_t max = BR_TLS12); + + // peek buffer API is present + virtual bool hasPeekBufferAPI () const override { return true; } + + // return number of byte accessible by peekBuffer() + virtual size_t peekAvailable () override { return WiFiClientSecureCtx::available(); } + + // return a pointer to available data buffer (size = peekAvailable()) + // semantic forbids any kind of read() before calling peekConsume() + virtual const char* peekBuffer () override; + + // consume bytes after use (see peekBuffer) + virtual void peekConsume (size_t consume) override; + protected: bool _connectSSL(const char *hostName); // Do initial SSL handshake @@ -140,7 +167,7 @@ class WiFiClientSecureCtx : public WiFiClient { std::shared_ptr _iobuf_out; time_t _now; const X509List *_ta; - CertStore *_certStore; + CertStoreBase *_certStore; int _iobuf_in_size; int _iobuf_out_size; bool _handshake_done; @@ -161,10 +188,21 @@ class WiFiClientSecureCtx : public WiFiClient { std::shared_ptr _cipher_list; uint8_t _cipher_cnt; + // TLS ciphers allowed + uint32_t _tls_min; + uint32_t _tls_max; + 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); size_t _write(const uint8_t *buf, size_t size, bool pmem); @@ -180,10 +218,10 @@ class WiFiClientSecureCtx : public WiFiClient { friend class WiFiClientSecure; // access to private context constructors WiFiClientSecureCtx(ClientContext *client, const X509List *chain, unsigned cert_issuer_key_type, const PrivateKey *sk, int iobuf_in_size, int iobuf_out_size, ServerSessions *cache, - const X509List *client_CA_ta); + const X509List *client_CA_ta, int tls_min, int tls_max); WiFiClientSecureCtx(ClientContext* client, const X509List *chain, const PrivateKey *sk, int iobuf_in_size, int iobuf_out_size, ServerSessions *cache, - const X509List *client_CA_ta); + const X509List *client_CA_ta, int tls_min, int tls_max); // RSA keyed server bool _connectSSLServerRSA(const X509List *chain, const PrivateKey *sk, @@ -206,13 +244,23 @@ class WiFiClientSecure : public WiFiClient { // Instead, all virtual functions call their counterpart in "WiFiClientecureCtx* _ctx" // which also derives from WiFiClient (this parent is the one which is eventually used) + // TODO: notice that this complicates the implementation by having two distinct ways the client connection is managed, consider: + // - implementing the secure connection details in the ClientContext + // (i.e. delegate the write & read functions there) + // - simplify the inheritance chain by implementing base wificlient class and inherit the original wificlient and wificlientsecure from it + // - abstract internals so it's possible to seamlessly =default copy and move with the instance *without* resorting to manual copy and initialization of each member + + // TODO: prefer implementing virtual overrides in the .cpp (or, at least one of them) + public: - WiFiClientSecure():_ctx(new WiFiClientSecureCtx()) { } - WiFiClientSecure(const WiFiClientSecure &rhs): WiFiClient(), _ctx(rhs._ctx) { } + WiFiClientSecure():_ctx(new WiFiClientSecureCtx()) { _owned = _ctx.get(); } + WiFiClientSecure(const WiFiClientSecure &rhs): WiFiClient(), _ctx(rhs._ctx) { if (_ctx) _owned = _ctx.get(); } ~WiFiClientSecure() override { _ctx = nullptr; } - WiFiClientSecure& operator=(const WiFiClientSecure&) = default; // The shared-ptrs handle themselves automatically + WiFiClientSecure& operator=(const WiFiClientSecure&) = default; + + std::unique_ptr clone() const override { return std::unique_ptr(new WiFiClientSecure(*this)); } uint8_t status() override { return _ctx->status(); } int connect(IPAddress ip, uint16_t port) override { return _ctx->connect(ip, port); } @@ -227,6 +275,7 @@ class WiFiClientSecure : public WiFiClient { size_t write(Stream& stream) /* Note this is not virtual */ { return _ctx->write(stream); } int read(uint8_t *buf, size_t size) override { return _ctx->read(buf, size); } int available() override { return _ctx->available(); } + int availableForWrite() override { return _ctx->availableForWrite(); } int read() override { return _ctx->read(); } int peek() override { return _ctx->peek(); } size_t peekBytes(uint8_t *buffer, size_t length) override { return _ctx->peekBytes(buffer, length); } @@ -235,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); } @@ -274,7 +328,7 @@ class WiFiClientSecure : public WiFiClient { int getLastSSLError(char *dest = NULL, size_t len = 0) { return _ctx->getLastSSLError(dest, len); } // Attach a preconfigured certificate store - void setCertStore(CertStore *certStore) { _ctx->setCertStore(certStore); } + void setCertStore(CertStoreBase *certStore) { _ctx->setCertStore(certStore); } // Select specific ciphers (i.e. optimize for speed over security) // These may be in PROGMEM or RAM, either will run properly @@ -282,11 +336,43 @@ class WiFiClientSecure : public WiFiClient { bool setCiphers(const std::vector list) { return _ctx->setCiphers(list); } bool setCiphersLessSecure() { return _ctx->setCiphersLessSecure(); } // Only use the limited set of RSA ciphers without EC + // Limit the TLS versions BearSSL will connect with. Default is + // BR_TLS10...BR_TLS12. Allowed values are: BR_TLS10, BR_TLS11, BR_TLS12 + bool setSSLVersion(uint32_t min = BR_TLS10, uint32_t max = BR_TLS12) { return _ctx->setSSLVersion(min, max); }; + // Check for Maximum Fragment Length support for given len before connection (possibly insecure) static bool probeMaxFragmentLength(IPAddress ip, uint16_t port, uint16_t len); static bool probeMaxFragmentLength(const char *hostname, uint16_t port, uint16_t len); static bool probeMaxFragmentLength(const String& host, uint16_t port, uint16_t len); + // peek buffer API is present + virtual bool hasPeekBufferAPI () const override { return true; } + + // return number of byte accessible by peekBuffer() + virtual size_t peekAvailable () override { return _ctx->available(); } + + // return a pointer to available data buffer (size = peekAvailable()) + // semantic forbids any kind of read() before calling peekConsume() + virtual const char* peekBuffer () override { return _ctx->peekBuffer(); } + + // 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; @@ -294,14 +380,14 @@ class WiFiClientSecure : public WiFiClient { friend class WiFiServerSecure; // Server needs to access these constructors WiFiClientSecure(ClientContext *client, const X509List *chain, unsigned cert_issuer_key_type, const PrivateKey *sk, int iobuf_in_size, int iobuf_out_size, ServerSessions *cache, - const X509List *client_CA_ta): - _ctx(new WiFiClientSecureCtx(client, chain, cert_issuer_key_type, sk, iobuf_in_size, iobuf_out_size, cache, client_CA_ta)) { + const X509List *client_CA_ta, int tls_min, int tls_max): + _ctx(new WiFiClientSecureCtx(client, chain, cert_issuer_key_type, sk, iobuf_in_size, iobuf_out_size, cache, client_CA_ta, tls_min, tls_max)) { } WiFiClientSecure(ClientContext* client, const X509List *chain, const PrivateKey *sk, int iobuf_in_size, int iobuf_out_size, ServerSessions *cache, - const X509List *client_CA_ta): - _ctx(new WiFiClientSecureCtx(client, chain, sk, iobuf_in_size, iobuf_out_size, cache, client_CA_ta)) { + const X509List *client_CA_ta, int tls_min, int tls_max): + _ctx(new WiFiClientSecureCtx(client, chain, sk, iobuf_in_size, iobuf_out_size, cache, client_CA_ta, tls_min, tls_max)) { } }; // class WiFiClientSecure diff --git a/libraries/ESP8266WiFi/src/WiFiServer.cpp b/libraries/ESP8266WiFi/src/WiFiServer.cpp index 644f8b45d1..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" @@ -109,8 +107,31 @@ bool WiFiServer::hasClient() { return false; } +size_t WiFiServer::hasClientData() { + ClientContext *next = _unclaimed; + while (next) { + size_t s = next->getSize(); + // return the amount of data available from the first connection that has any + if (s) return s; + next = next->next(); + } + return 0; +} + +bool WiFiServer::hasMaxPendingClients() { +#if TCP_LISTEN_BACKLOG + return ((struct tcp_pcb_listen *)_listen_pcb)->accepts_pending >= MAX_PENDING_CLIENTS_PER_PORT; +#else + return false; +#endif +} + WiFiClient WiFiServer::available(byte* status) { (void) status; + return accept(); +} + +WiFiClient WiFiServer::accept() { if (_unclaimed) { WiFiClient result(_unclaimed); @@ -152,30 +173,15 @@ void WiFiServer::stop() { close(); } -size_t WiFiServer::write(uint8_t b) { - return write(&b, 1); -} - -size_t WiFiServer::write(const uint8_t *buffer, size_t size) { - // write to all clients - // not implemented - (void) buffer; - (void) size; - return 0; +void WiFiServer::end() { + 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; +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"); @@ -201,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 59aea48faf..b0f51b02f0 100644 --- a/libraries/ESP8266WiFi/src/WiFiServer.h +++ b/libraries/ESP8266WiFi/src/WiFiServer.h @@ -23,18 +23,19 @@ #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 // simultaneously pending clients is defined in WiFiServer.cpp -// (MAX_PENDING_CLIENTS_PER_PORT=5). User can overide it at runtime from +// (MAX_PENDING_CLIENTS_PER_PORT=5). User can override it at runtime from // sketch: // WiFiServer::begin(port, max-simultaneous-pending-clients); // @@ -65,7 +66,7 @@ extern "C" { class ClientContext; class WiFiClient; -class WiFiServer : public Server { +class WiFiServer { // Secure server needs access to all the private entries here protected: uint16_t _port; @@ -78,31 +79,43 @@ class WiFiServer : public Server { public: WiFiServer(const IPAddress& addr, uint16_t port); - WiFiServer(uint16_t port); + WiFiServer(uint16_t port = 23); virtual ~WiFiServer() {} - WiFiClient available(uint8_t* status = NULL); + WiFiClient accept(); // https://www.arduino.cc/en/Reference/EthernetServerAccept + WiFiClient available(uint8_t* status = NULL) __attribute__((deprecated("Renamed to accept()."))); bool hasClient(); + // hasClientData(): + // returns the amount of data available from the first client + // or 0 if there is none + size_t hasClientData(); + // hasMaxPendingClients(): + // returns true if the queue of pending clients is full + bool hasMaxPendingClients(); void begin(); void begin(uint16_t port); void begin(uint16_t port, uint8_t backlog); void setNoDelay(bool nodelay); bool getNoDelay(); - virtual size_t write(uint8_t); - virtual size_t write(const uint8_t *buf, size_t size); uint8_t status(); uint16_t port() const; void close(); void stop(); + void end(); + explicit operator bool(); - using Print::write; 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 5f7bcac072..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" @@ -77,15 +75,22 @@ void WiFiServerSecure::setECCert(const X509List *chain, unsigned cert_issuer_key // then any validation (i.e. client cert checking) will have succeeded. WiFiClientSecure WiFiServerSecure::available(uint8_t* status) { (void) status; // Unused + return accept(); +} + +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); + WiFiClientSecure result(_unclaimed, _chain, _sk, _iobuf_in_size, _iobuf_out_size, _cache, _client_CA_ta, _tls_min, _tls_max); _unclaimed = _unclaimed->next(); result.setNoDelay(_noDelay); DEBUGV("WS:av\r\n"); return result; } else if (_sk && _sk->isEC()) { - WiFiClientSecure result(_unclaimed, _chain, _cert_issuer_key_type, _sk, _iobuf_in_size, _iobuf_out_size, _cache, _client_CA_ta); + WiFiClientSecure result(_unclaimed, _chain, _cert_issuer_key_type, _sk, _iobuf_in_size, _iobuf_out_size, _cache, _client_CA_ta, _tls_min, _tls_max); _unclaimed = _unclaimed->next(); result.setNoDelay(_noDelay); DEBUGV("WS:av\r\n"); @@ -101,4 +106,15 @@ WiFiClientSecure WiFiServerSecure::available(uint8_t* status) { return WiFiClientSecure(); } +bool WiFiServerSecure::setSSLVersion(uint32_t min, uint32_t max) { + if ( ((min != BR_TLS10) && (min != BR_TLS11) && (min != BR_TLS12)) || + ((max != BR_TLS10) && (max != BR_TLS11) && (max != BR_TLS12)) || + (max < min) ) { + return false; // Invalid options + } + _tls_min = min; + _tls_max = max; + return true; +} + }; diff --git a/libraries/ESP8266WiFi/src/WiFiServerSecureBearSSL.h b/libraries/ESP8266WiFi/src/WiFiServerSecureBearSSL.h index 716f00eaad..25d0d380d9 100644 --- a/libraries/ESP8266WiFi/src/WiFiServerSecureBearSSL.h +++ b/libraries/ESP8266WiFi/src/WiFiServerSecureBearSSL.h @@ -60,8 +60,13 @@ class WiFiServerSecure : public WiFiServer { _client_CA_ta = client_CA_ta; } + // Limit the TLS versions BearSSL will connect with. Default is + // BR_TLS10...BR_TLS12 + bool setSSLVersion(uint32_t min = BR_TLS10, uint32_t max = BR_TLS12); + // If awaiting connection available and authenticated (i.e. client cert), return it. - WiFiClientSecure available(uint8_t* status = NULL); + WiFiClientSecure accept(); // https://www.arduino.cc/en/Reference/EthernetServerAccept + WiFiClientSecure available(uint8_t* status = NULL) __attribute__((deprecated("Renamed to accept()."))); WiFiServerSecure& operator=(const WiFiServerSecure&) = default; @@ -76,6 +81,9 @@ class WiFiServerSecure : public WiFiServer { const X509List *_client_CA_ta = nullptr; ServerSessions *_cache = nullptr; + // TLS ciphers allowed + uint32_t _tls_min = BR_TLS10; + uint32_t _tls_max = BR_TLS12; }; }; 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 a1cf42be10..fdf82217bc 100644 --- a/libraries/ESP8266WiFi/src/WiFiUdp.h +++ b/libraries/ESP8266WiFi/src/WiFiUdp.h @@ -44,9 +44,11 @@ class WiFiUDP : public UDP, public SList { // initialize, start listening on specified port. // Returns 1 if successful, 0 if there are no sockets available to use uint8_t begin(uint16_t port) override; - // Finish with the UDP connetion + // 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 @@ -58,7 +60,7 @@ class WiFiUDP : public UDP, public SList { // Returns 1 if successful, 0 if there was a problem resolving the hostname or port int beginPacket(const char *host, uint16_t port) override; // Start building up a packet to send to the multicast address - // multicastAddress - muticast address to send to + // multicastAddress - multicast address to send to // interfaceAddress - the local IP address of the interface that should be used // use WiFi.localIP() or WiFi.softAPIP() depending on the interface you need // ttl - multicast packet TTL (default is 1) @@ -92,7 +94,7 @@ class WiFiUDP : public UDP, public SList { int read(char* buffer, size_t len) override { return read((unsigned char*)buffer, len); }; // Return the next byte from the current packet without moving on to the next byte int peek() override; - void flush() override; // Finish reading the current packet + void flush() override; // wait for all outgoing characters to be sent, output buffer is empty after this call // Return the IP address of the host who sent the current incoming packet IPAddress remoteIP() override; diff --git a/libraries/ESP8266WiFi/src/enable_wifi_at_boot_time.cpp b/libraries/ESP8266WiFi/src/enable_wifi_at_boot_time.cpp new file mode 100644 index 0000000000..a8589c4d25 --- /dev/null +++ b/libraries/ESP8266WiFi/src/enable_wifi_at_boot_time.cpp @@ -0,0 +1,28 @@ +/* + * empty wrappers to play with linker and re-enable wifi at boot time + */ + +#include "coredecls.h" + +#include + +extern "C" void enableWiFiAtBootTime() +{ + /* + * Called by user from anywhere, does nothing and allows overriding + * the core_esp8266_main.cpp's default disableWiFiAtBootTime() by the + * one below, at link time. + */ +} + +extern "C" void __disableWiFiAtBootTime() +{ + // overrides the default __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 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 8095e402a2..5c579c8848 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -26,10 +26,9 @@ class WiFiClient; typedef void (*discard_cb_t)(void*, ClientContext*); -extern "C" void esp_yield(); -extern "C" void esp_schedule(); - -#include "DataSource.h" +#include +#include +#include bool getDefaultPrivateGlobalSyncValue (); @@ -129,6 +128,9 @@ class ClientContext int connect(ip_addr_t* addr, uint16_t port) { + // note: not using `const ip_addr_t* addr` because + // - `ip6_addr_assign_zone()` below modifies `*addr` + // - caller's parameter `WiFiClient::connect` is a local copy #if LWIP_IPV6 // Set zone so that link local addresses use the default interface if (IP_IS_V6(addr) && ip6_addr_lacks_zone(ip_2_ip6(addr), IP6_UNKNOWN)) { @@ -141,11 +143,8 @@ class ClientContext } _connect_pending = true; _op_start_time = millis(); - for (decltype(_timeout_ms) i = 0; _connect_pending && i < _timeout_ms; i++) { - // Give scheduled functions a chance to run (e.g. Ethernet uses recurrent) - delay(1); - // will resume on timeout or when _connected or _notify_error fires - } + // will resume on timeout or when _connected or _notify_error fires + esp_delay(_timeout_ms, [this]() { return this->_connect_pending; }); _connect_pending = false; if (!_pcb) { DEBUGV(":cabrt\r\n"); @@ -314,7 +313,7 @@ class ClientContext _rx_buf_offset = 0; } - bool wait_until_sent(int max_wait_ms = WIFICLIENT_MAX_FLUSH_WAIT_MS) + bool wait_until_acked(int max_wait_ms = WIFICLIENT_MAX_FLUSH_WAIT_MS) { // https://github.com/esp8266/Arduino/pull/3967#pullrequestreview-83451496 // option 1 done @@ -348,9 +347,11 @@ class ClientContext last_sent = millis(); } - delay(0); // from sys or os context + esp_yield(); // from sys or os context if ((state() != ESTABLISHED) || (sndbuf == TCP_SND_BUF)) { + // peer has closed or all bytes are sent and acked + // ((TCP_SND_BUF-sndbuf) is the amount of un-acked bytes) break; } } @@ -369,29 +370,12 @@ class ClientContext return _pcb->state; } - size_t write(const uint8_t* data, size_t size) - { - if (!_pcb) { - return 0; - } - return _write_from_source(new BufferDataSource(data, size)); - } - - size_t write(Stream& stream) + size_t write(const char* ds, const size_t dl) { if (!_pcb) { return 0; } - return _write_from_source(new BufferedStreamDataSource(stream, stream.available())); - } - - size_t write_P(PGM_P buf, size_t size) - { - if (!_pcb) { - return 0; - } - ProgmemStream stream(buf, size); - return _write_from_source(new BufferedStreamDataSource(stream, size)); + return _write_from_source(ds, dl); } 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) @@ -436,6 +420,29 @@ class ClientContext _sync = sync; } + // return a pointer to available data buffer (size = peekAvailable()) + // semantic forbids any kind of read() before calling peekConsume() + const char* peekBuffer () + { + if (!_rx_buf) + return nullptr; + return (const char*)_rx_buf->payload + _rx_buf_offset; + } + + // return number of byte accessible by peekBuffer() + size_t peekAvailable () + { + if (!_rx_buf) + return 0; + return _rx_buf->len - _rx_buf_offset; + } + + // consume bytes after use (see peekBuffer) + void peekConsume (size_t consume) + { + _consume(consume); + } + protected: bool _is_timeout() @@ -446,17 +453,19 @@ class ClientContext void _notify_error() { if (_connect_pending || _send_waiting) { + // resume connect or _write_from_source _send_waiting = false; _connect_pending = false; - esp_schedule(); // break delay in connect or _write_from_source + esp_schedule(); } } - size_t _write_from_source(DataSource* ds) + size_t _write_from_source(const char* ds, const size_t dl) { assert(_datasource == nullptr); assert(!_send_waiting); _datasource = ds; + _datalen = dl; _written = 0; _op_start_time = millis(); do { @@ -464,27 +473,23 @@ class ClientContext _op_start_time = millis(); } - if (!_datasource->available() || _is_timeout() || state() == CLOSED) { + if (_written == _datalen || _is_timeout() || state() == CLOSED) { if (_is_timeout()) { DEBUGV(":wtmo\r\n"); } - delete _datasource; _datasource = nullptr; + _datalen = 0; break; } _send_waiting = true; - for (decltype(_timeout_ms) i = 0; _send_waiting && i < _timeout_ms; i++) { - // Give scheduled functions a chance to run (e.g. Ethernet uses recurrent) - delay(1); - // will resume on timeout or when _write_some_from_cb or _notify_error fires - - } + // will resume on timeout or when _write_some_from_cb or _notify_error fires + esp_delay(_timeout_ms, [this]() { return this->_send_waiting; }); _send_waiting = false; } while(true); if (_sync) - wait_until_sent(); + wait_until_acked(); return _written; } @@ -495,20 +500,21 @@ class ClientContext return false; } - DEBUGV(":wr %d %d\r\n", _datasource->available(), _written); + DEBUGV(":wr %d %d\r\n", _datalen - _written, _written); bool has_written = false; - while (_datasource) { + while (_written < _datalen) { if (state() == CLOSED) return false; - size_t next_chunk_size = std::min((size_t)tcp_sndbuf(_pcb), _datasource->available()); + const auto remaining = _datalen - _written; + size_t next_chunk_size = std::min((size_t)tcp_sndbuf(_pcb), remaining); if (!next_chunk_size) break; - const uint8_t* buf = _datasource->get_buffer(next_chunk_size); + const char* buf = _datasource + _written; uint8_t flags = 0; - if (next_chunk_size < _datasource->available()) + if (next_chunk_size < remaining) // PUSH is meant for peer, telling to give data to user app as soon as received // PUSH "may be set" when sender has finished sending a "meaningful" data block // PUSH does not break Nagle @@ -522,15 +528,14 @@ class ClientContext err_t err = tcp_write(_pcb, buf, next_chunk_size, flags); - DEBUGV(":wrc %d %d %d\r\n", next_chunk_size, _datasource->available(), (int)err); + DEBUGV(":wrc %d %d %d\r\n", next_chunk_size, remaining, (int)err); if (err == ERR_OK) { - _datasource->release_buffer(buf, next_chunk_size); _written += next_chunk_size; has_written = true; } else { - // ERR_MEM(-1) is a valid error meaning - // "come back later". It leaves state() opened + // ERR_MEM(-1) is a valid error meaning + // "come back later". It leaves state() opened break; } } @@ -549,8 +554,9 @@ class ClientContext void _write_some_from_cb() { if (_send_waiting) { + // resume _write_from_source _send_waiting = false; - esp_schedule(); // break delay in _write_from_source + esp_schedule(); } } @@ -565,8 +571,6 @@ class ClientContext void _consume(size_t size) { - if(_pcb) - tcp_recved(_pcb, size); ptrdiff_t left = _rx_buf->len - _rx_buf_offset - size; if(left > 0) { _rx_buf_offset += size; @@ -583,6 +587,8 @@ class ClientContext pbuf_ref(_rx_buf); pbuf_free(head); } + if(_pcb) + tcp_recved(_pcb, size); } err_t _recv(tcp_pcb* pcb, pbuf* pb, err_t err) @@ -637,8 +643,9 @@ class ClientContext (void) pcb; assert(pcb == _pcb); if (_connect_pending) { + // resume connect _connect_pending = false; - esp_schedule(); // break delay in connect + esp_schedule(); } return ERR_OK; } @@ -683,7 +690,8 @@ class ClientContext discard_cb_t _discard_cb; void* _discard_cb_arg; - DataSource* _datasource = nullptr; + const char* _datasource = nullptr; + size_t _datalen = 0; size_t _written = 0; uint32_t _timeout_ms = 5000; uint32_t _op_start_time = 0; diff --git a/libraries/ESP8266WiFi/src/include/DataSource.h b/libraries/ESP8266WiFi/src/include/DataSource.h deleted file mode 100644 index 2a0bfed260..0000000000 --- a/libraries/ESP8266WiFi/src/include/DataSource.h +++ /dev/null @@ -1,154 +0,0 @@ -/* DataSource.h - a read-only object similar to Stream, but with less methods - * Copyright (c) 2016 Ivan Grokhotkov. All rights reserved. - * This file is distributed under MIT license. - */ -#ifndef DATASOURCE_H -#define DATASOURCE_H - -#include - -class DataSource { -public: - virtual ~DataSource() {} - virtual size_t available() = 0; - virtual const uint8_t* get_buffer(size_t size) = 0; - virtual void release_buffer(const uint8_t* buffer, size_t size) = 0; - -}; - -class BufferDataSource : public DataSource { -public: - BufferDataSource(const uint8_t* data, size_t size) : - _data(data), - _size(size) - { - } - - size_t available() override - { - return _size - _pos; - } - - const uint8_t* get_buffer(size_t size) override - { - (void)size; - assert(_pos + size <= _size); - return _data + _pos; - } - - void release_buffer(const uint8_t* buffer, size_t size) override - { - (void)buffer; - assert(buffer == _data + _pos); - _pos += size; - } - -protected: - const uint8_t* _data; - const size_t _size; - size_t _pos = 0; -}; - -template -class BufferedStreamDataSource : public DataSource { -public: - BufferedStreamDataSource(TStream& stream, size_t size) : - _stream(stream), - _size(size) - { - } - - size_t available() override - { - return _size - _pos; - } - - const uint8_t* get_buffer(size_t size) override - { - assert(_pos + size <= _size); - - //Data that was already read from the stream but not released (e.g. if tcp_write error occured). Otherwise this should be 0. - const size_t stream_read = _streamPos - _pos; - - //Min required buffer size: max(requested size, previous stream data already in buffer) - const size_t min_buffer_size = size > stream_read ? size : stream_read; - - //Buffer too small? - if (_bufferSize < min_buffer_size) { - uint8_t *new_buffer = new uint8_t[min_buffer_size]; - //If stream reading is ahead, than some data is already in the old buffer and needs to be copied to new resized buffer - if (_buffer && stream_read > 0) { - memcpy(new_buffer, _buffer.get(), stream_read); - } - _buffer.reset(new_buffer); - _bufferSize = min_buffer_size; - } - - //Fetch remaining data from stream - //If error in tcp_write in ClientContext::_write_some() occured earlier and therefore release_buffer was not called last time, than the requested stream data is already in the buffer. - if (size > stream_read) { - //Remaining bytes to read from stream - const size_t stream_rem = size - stream_read; - const size_t cb = _stream.readBytes(reinterpret_cast(_buffer.get() + stream_read), stream_rem); - assert(cb == stream_rem); - (void)cb; - _streamPos += stream_rem; - } - return _buffer.get(); - - } - - void release_buffer(const uint8_t* buffer, size_t size) override - { - if (size == 0) { - return; - } - - (void)buffer; - _pos += size; - - //Cannot release more than acquired through get_buffer - assert(_pos <= _streamPos); - - //Release less than requested with get_buffer? - if (_pos < _streamPos) { - // Move unreleased stream data in buffer to front - assert(_buffer); - memmove(_buffer.get(), _buffer.get() + size, _streamPos - _pos); - } - } - -protected: - TStream & _stream; - std::unique_ptr _buffer; - size_t _size; - size_t _pos = 0; - size_t _bufferSize = 0; - size_t _streamPos = 0; -}; - -class ProgmemStream -{ -public: - ProgmemStream(PGM_P buf, size_t size) : - _buf(buf), - _left(size) - { - } - - size_t readBytes(char* dst, size_t size) - { - size_t will_read = (_left < size) ? _left : size; - memcpy_P((void*)dst, (PGM_VOID_P)_buf, will_read); - _left -= will_read; - _buf += will_read; - return will_read; - } - -protected: - PGM_P _buf; - size_t _left; -}; - - -#endif //DATASOURCE_H diff --git a/libraries/ESP8266WiFi/src/include/UdpContext.h b/libraries/ESP8266WiFi/src/include/UdpContext.h index 0c43cd2adb..1e7f639e34 100644 --- a/libraries/ESP8266WiFi/src/include/UdpContext.h +++ b/libraries/ESP8266WiFi/src/include/UdpContext.h @@ -24,7 +24,7 @@ class UdpContext; extern "C" { -void esp_yield(); +void esp_suspend(); void esp_schedule(); #include } @@ -177,7 +177,7 @@ class UdpContext } // warning: handler is called from tcp stack context - // esp_yield and non-reentrant functions which depend on it will fail + // esp_suspend and non-reentrant functions which depend on it will fail void onRx(rxhandler_t handler) { _on_rx = handler; } @@ -186,7 +186,7 @@ class UdpContext // this helper is ready to be used when debugging UDP void printChain (const pbuf* pb, const char* msg, size_t n) const { - // printf the pb pbuf chain, bufferred and all at once + // printf the pb pbuf chain, buffered and all at once char buf[128]; int l = snprintf(buf, sizeof(buf), "UDP: %s %u: ", msg, n); while (pb) @@ -266,7 +266,7 @@ class UdpContext return true; } - // We have interleaved informations on addresses within received pbuf chain: + // We have interleaved information on addresses within received pbuf chain: // (before ipv6 code we had: (data-pbuf) -> (data-pbuf) -> (data-pbuf) -> ... in the receiving order) // Now: (address-info-pbuf -> chained-data-pbuf [-> chained-data-pbuf...]) -> // (chained-address-info-pbuf -> chained-data-pbuf [-> chained...]) -> ... @@ -411,7 +411,7 @@ class UdpContext err_t err; esp8266::polledTimeout::oneShotFastMs timeout(timeoutMs); while (((err = trySend(addr, port, /* keep buffer on error */true)) != ERR_OK) && !timeout) - delay(0); + esp_yield(); if (err != ERR_OK) cancelBuffer(); // get rid of buffer kept on error after timeout return err == ERR_OK; 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/ESP8266WiFiMesh/README.md b/libraries/ESP8266WiFiMesh/README.md index 8d955220ca..d9c7a4344a 100644 --- a/libraries/ESP8266WiFiMesh/README.md +++ b/libraries/ESP8266WiFiMesh/README.md @@ -1,81 +1,398 @@ -ESP8266 WiFi Mesh -================= +# ESP8266 WiFi Mesh -A library for turning your ESP8266 into a mesh network node. +## Contents +1. [Overview](#Overview) +2. [How does it work?](#Work) +3. [The first step](#Start) +4. [TcpIpMeshBackend](#TcpIpMeshBackendMore) + * [Usage](#TcpIpMeshBackendUsage) + * [Note](#TcpIpMeshBackendNote) + * [General Information](#TcpIpMeshBackendGeneral) +5. [EspnowMeshBackend](#EspnowMeshBackendMore) + * [Usage](#EspnowMeshBackendUsage) + * [Note](#EspnowMeshBackendNote) + * [Callbacks](#EspnowMeshBackendCallbacks) + * [Encryption](#EspnowMeshBackendEncryption) + * [CCMP](#CCMP) + * [AEAD](#AEAD) +6. [FloodingMesh](#FloodingMeshMore) + * [Usage](#FloodingMeshUsage) + * [Note](#FloodingMeshNote) + * [Serialization and the internal state of a node](#FloodingMeshSerialization) +7. [FAQ](#FAQ) + * [My ESP8266 crashes on start-up when I use the library!](#FAQStartupCrash) + * [The node does not remember the SSID I assign to it!](#FAQSSIDAmnesia) + * [I want to control the WiFi mode myself.](#FAQModeControl) + * [I have a lot of interference from all the nodes that are close to each other. What can I do?](#FAQInterference) + * [How do I change the interval of the WiFi AP beacon broadcast?](#FAQBeaconInterval) + * [My ESP is ignoring the WiFi AP beacon broadcast interval settings you just told me about above! (a.k.a. How do I change the WiFi scan mode to passive?)](#FAQPassiveScan) + * [My internet is slower when I connect the ESP8266 to my router!](#FAQSlowRouter) -The library has been tested and works with Arduino core for ESP8266 version 2.3.0 (with default lwIP) and 2.4.2 or higher (with lwIP 1.4 and lwIP2). -**Note:** This mesh library has been rewritten for core release 2.4.2. The old method signatures have been retained for compatibility purposes, but will be removed in core release 2.5.0. If you are still using these old method signatures please consider migrating to the new API shown in the `ESP8266WiFiMesh.h` source file. +## Overview -Usage ------ +This is a library for creating a mesh network using the ESP8266. -The basic operation of a mesh node is as follows: +The library has been tested and works with Arduino Core for ESP8266 version 3.0.0 (with lwIP2). It may work with earlier and later core releases, but this has not been tested during development. -The `attemptTransmission` method of the ESP8266WiFiMesh instance is called with a message to send to other nodes in the mesh network. If the node is already connected to an AP, the message is sent only to that AP. Otherwise a WiFi scan is performed. The scan results are sent to the `networkFilter` callback function of the ESP8266WiFiMesh instance which adds the AP:s of interest to the `connectionQueue` vector. The message is then transmitted to the networks in the `connectionQueue`, and the response from each AP is sent to the `responseHandler` callback of the ESP8266WiFiMesh instance. The outcome from each transmission attempt can be found in the `latestTransmissionOutcomes` vector. +**Note:** This mesh library has been extensively rewritten for core release 3.0.0. The old method signatures have been retained for compatibility purposes, but will be removed in core release 3.0.X. If you are still using these old method signatures please consider migrating to the new API shown in the `EspnowMeshBackend.h` or `TcpIpMeshBackend.h` source files. -The node receives messages from other nodes by calling the `acceptRequest` method of the ESP8266WiFiMesh instance. These received messages are passed to the `requestHandler` callback of the mesh instance. For each received message the return value of `requestHandler` is sent to the other node as a response to the message. +## How does it work? -For more details, see the included example. The main functions to modify in the example are `manageRequest` (`requestHandler`), `manageResponse` (`responseHandler`) and `networkFilter`. There is also more information to be found in the source code comments. An example is the ESP8266WiFiMesh constructor comment, which is shown below for reference: -``` -/** -* WiFiMesh Constructor method. Creates a WiFi Mesh Node, ready to be initialised. -* -* @param requestHandler The callback handler for dealing with received requests. Takes a string as an argument which -* is the request string received from another node and returns the string to send back. -* @param responseHandler The callback handler for dealing with received responses. Takes a string as an argument which -* is the response string received from another node. Returns a transmission status code as a transmission_status_t. -* @param networkFilter The callback handler for deciding which WiFi networks to connect to. -* @param meshPassword The WiFi password for the mesh network. -* @param meshName The name of the mesh network. Used as prefix for the node SSID and to find other network nodes in the example network filter function. -* @param nodeID The id for this mesh node. Used as suffix for the node SSID. If set to "", the id will default to ESP.getChipId(). -* @param verboseMode Determines if we should print the events occurring in the library to Serial. Off by default. -* @param meshWiFiChannel The WiFi channel used by the mesh network. Valid values are integers from 1 to 13. Defaults to 1. -* WARNING: The ESP8266 has only one WiFi channel, and the the station/client mode is always prioritized for channel selection. -* This can cause problems if several ESP8266WiFiMesh instances exist on the same ESP8266 and use different WiFi channels. -* In such a case, whenever the station of one ESP8266WiFiMesh instance connects to an AP, it will silently force the -* WiFi channel of any active AP on the ESP8266 to match that of the station. This will cause disconnects and possibly -* make it impossible for other stations to detect the APs whose WiFi channels have changed. -* @param serverPort The server port used by the AP of the ESP8266WiFiMesh instance. If multiple APs exist on a single ESP8266, each requires a separate server port. -* If two AP:s on the same ESP8266 are using the same server port, they will not be able to have both server instances active at the same time. -* This is managed automatically by the activateAP method. -* -*/ -ESP8266WiFiMesh(requestHandlerType requestHandler, responseHandlerType responseHandler, networkFilterType networkFilter, - const String &meshPassword, const String &meshName = "MeshNode_", const String &nodeID = WIFI_MESH_EMPTY_STRING, bool verboseMode = false, - uint8 meshWiFiChannel = 1, int serverPort = 4011); -``` +The ESP8266 WiFi Mesh library is a cake, metaphorically speaking. At the bottom you have the general ESP8266 Arduino Core WiFi functionality. On top of this two mesh backends have been created (`EspnowMeshBackend` and `TcpIpMeshBackend`), a yummy filling that completely covers the bottom. Then at the very top over the backends is the beautiful and delicious frosting: `FloodingMesh`. `FloodingMesh` is an actual mesh network implementation that uses the `EspnowMeshBackend`. + +Eating the cake in its current form is a process which involves all the layers. However, if you prefer to be your own pastry chef it is easy to use both the `EspnowMeshBackend` and the `TcpIpMeshBackend` separately from `FloodingMesh`, perhaps to construct your own mesh network architecture or just to simplify the usage of TCP/IP or ESP-NOW. If you have made a nice mesh architecture with this library that you would like to share with the rest of the world, feel free to make a PR with it! + +In general ESP-NOW is faster than TCP/IP for small data payloads (up to a few kB). The data segment of a standard ESP-NOW transmission is 234 bytes, which takes around 2-4 ms to transmit. + +TCP/IP takes longer to connect (around 1000 ms), and an AP has to disconnect all connected stations in order to transfer data to another AP. However, this backend has a much higher data transfer speed than ESP-NOW once connected (100x faster or so). + +## The first step + +There are plenty of details to the operations of the library, but if you want to get started quickly you really only need to know this: In the example folder of the library there is a file called `HelloMesh.ino`. Upload it to a few ESP8266 and you have a working mesh network. Change the `useLED` variable to `true` if you have built-in LEDs on your ESP8266s to illustrate how the message is spread through the network. Change the `floodingMesh.broadcast` calls to modify what the mesh nodes are transmitting to each other. Change the code of the `meshMessageHandler` to modify how mesh nodes react to received transmissions. + +Finally, three things are important to note: + +1. This library uses the standard Arduino Core for ESP8266 WiFi functions. Therefore, other code that also uses these WiFi functions (e.g. `WiFi.mode()`) may cause conflicts with the library, resulting in strange behaviour. See "[I want to control the WiFi mode myself](#FAQModeControl)" in the FAQ for ideas on how to work around this. +2. Both the `EspnowMeshBackend` and the `TcpIpMeshBackend` can be used simultaneously on the same node. However, since there is only one WiFi radio on the ESP8266, only one backend at a time will be responsible for the settings of this radio (SSID, WiFi channel etc.). The backend in control is known as the `APController` in the library. Both backends can still send messages, regardless of who is `APController`. +3. The `MeshBackendBase`, `EspnowMeshBackend`, `TcpIpMeshBackend` and `FloodingMesh` source files are meant to be the main front-ends of the library and are all extensively documented. If you wonder about how something is working, chances are good that you will find an answer in the documentation of those files. + +## TcpIpMeshBackend -### Note +### Usage -* This library can use static IP:s for the nodes to speed up connection times. To enable this, use the `setStaticIP` method after calling the `begin` method, as in the included example. Ensure that nodes connecting to the same AP have distinct static IP:s. Node IP:s need to be at the same subnet as the server gateway (192.168.4 for this library by default). It may also be worth noting that station gateway IP must match the IP for the server on the nodes, though this is the default setting for the library. +The basic operation of the TCP/IP mesh backend is as follows: - At the moment static IP is a global setting, meaning that all ESP8266WiFiMesh instances on the same ESP8266 share the same static IP settings. +The `attemptTransmission` method of the TcpIpMeshBackend instance is called with a message to send to other nodes in the mesh network. If the node is already connected to an AP, the message is sent only to that AP. Otherwise the default behaviour is for a WiFi scan to be performed. The scan results are sent to the `networkFilter` callback function of the TcpIpMeshBackend instance which adds the AP:s of interest to the `connectionQueue` vector. The message is then transmitted to the networks in the `connectionQueue`, and the response from each AP is sent to the `responseHandler` callback of the TcpIpMeshBackend instance. The outcome from each transmission attempt can be found in the `latestTransmissionOutcomes` vector. -* When Arduino core for ESP8266 version 2.4.2 or higher is used, there are optimizations available for WiFi scans and static IP use to reduce the time it takes for nodes to connect to each other. These optimizations are enabled by default. To take advantage of the static IP optimizations you also need to use lwIP2. The lwIP version can be changed in the Tools menu of Arduino IDE. +The node receives messages from other TCP/IP nodes by calling the `acceptRequests` method of the TcpIpMeshBackend instance. These received messages are passed to the `requestHandler` callback of the mesh instance. For each received message the return value of `requestHandler` is sent to the other node as a response to the message. - If you are using a core version prior to 2.4.2 it is possible to disable the WiFi scan and static IP optimizations by commenting out the `ENABLE_STATIC_IP_OPTIMIZATION` and `ENABLE_WIFI_SCAN_OPTIMIZATION` defines in ESP8266WiFiMesh.h. Press Ctrl+K in the Arduino IDE while an example from the mesh library is opened, to open the library folder (or click "Show Sketch Folder" in the Sketch menu). ESP8266WiFiMesh.h can then be found at ESP8266WiFiMesh/src. Edit the file with any text editor. +For more details, see the included HelloTcpIp example. The main functions to modify in the example are `manageRequest` (`requestHandler`), `manageResponse` (`responseHandler`), `networkFilter` and `exampleTransmissionOutcomesUpdateHook`. There is also much more information to be found in the source code comments. -* The WiFi scan optimization mentioned above works by making WiFi scans only search through the same WiFi channel as the ESP8266WiFiMesh instance is using. If you would like to scan all WiFi channels instead, set the `scanAllWiFiChannels` argument of the `attemptTransmission` method to `true`. Note that scanning all WiFi channels will slow down scans considerably and make it more likely that existing WiFi connections will break during scans. Also note that if the ESP8266 has an active AP, that AP will switch WiFi channel to match that of any other AP the ESP8266 connects to (compare next bullet point). This can make it impossible for other nodes to detect the AP if they are scanning the wrong WiFi channel. To remedy this, force the AP back on the original channel by using the `restartAP` method of the current AP controller once the ESP8266 has disconnected from the other AP. This would typically be done like so: +### Note + +* This library can use static IP:s for the nodes to speed up connection times. To enable this, use the `setStaticIP` method after calling the `begin` method, as in the included example. When using static IP, the following is good to keep in mind: + + Ensure that nodes connecting to the same AP have distinct static IP:s. + + Node IP:s need to be at the same subnet as the server gateway (192.168.4 for this library by default). + + Station gateway IP must match the IP for the server on the nodes. This is the default setting for the library. + + Static IP is a global setting (for now), meaning that all TcpIpMeshBackend instances on the same ESP8266 share the same static IP settings. + +* Scanning all WiFi channels (e.g. via the `attemptTransmission` method with the `scanAllWiFiChannels` argument set to `true`) will slow down scans considerably and make it more likely that existing WiFi connections will break during scans. + +* If the ESP8266 has an active AP, that AP will switch WiFi channel to match that of any other AP the TcpIpMeshBackend of the ESP8266 connects to (compare next bullet point). This can make it impossible for other nodes to detect the AP if they are scanning the wrong WiFi channel. To remedy this, force the AP back on the original channel by using the `restartAP` method of the current AP controller once the ESP8266 has disconnected from the other AP. This would typically be done like so: ``` - if(ESP8266WiFiMesh *apController = ESP8266WiFiMesh::getAPController()) // Make sure apController is not nullptr + if(MeshBackendBase *apController = MeshBackendBase::getAPController()) // Make sure apController is not nullptr apController->restartAP(); ``` -* It is possible to have several ESP8266WiFiMesh instances running on every ESP8266 (e.g. to communicate with different mesh networks). However, because the ESP8266 has one WiFi radio only one AP per ESP8266 can be active at a time. Also note that if the ESP8266WiFiMesh instances use different WiFi channels, active APs are forced to use the same WiFi channel as active stations, possibly causing AP disconnections. +* It is possible to have several TcpIpMeshBackend instances running on every ESP8266 (e.g. to communicate with different mesh networks). However, because the ESP8266 has one WiFi radio only one AP per ESP8266 can be active at a time. Also note that if the TcpIpMeshBackend instances use different WiFi channels, active APs are forced to use the same WiFi channel as active stations, possibly causing AP disconnections. -* While it is possible to connect to other nodes by only giving their SSID, e.g. `ESP8266WiFiMesh::connectionQueue.push_back(NetworkInfo("NodeSSID"));`, it is recommended that AP WiFi channel and AP BSSID are given as well, to minimize connection delay. +* While it is possible to connect to other nodes by only giving their SSID, e.g. `TcpIpMeshBackend::connectionQueue().emplace_back("NodeSSID");`, it is recommended that AP WiFi channel and AP BSSID are given as well, to minimize connection delay. * Also, remember to change the default mesh network WiFi password! -General Information ---------------------------- - -* This library uses the standard Arduino core for ESP8266 WiFi functions. Therefore, other code that also uses these WiFi functions may cause conflicts with the library, resulting in strange behaviour. +### General Information * By default, a maximum of 4 stations can be connected at a time to each AP. This can be changed to a value in the range 0 to 8 via the `setMaxAPStations` method. Once the max number has been reached, any other station that wants to connect will be forced to wait until an already connected station disconnects. The more stations that are connected, the more memory is required. -* Unlike `WiFi.mode(WIFI_AP)`, the `WiFi.mode(WIFI_AP_STA)` which is used in this library allows nodes to stay connected to an AP they connect to while in STA mode, at the same time as they can receive connections from other stations. Nodes cannot send data to an AP while in STA_AP mode though, that requires STA mode. Switching to STA mode will sometimes disconnect stations connected to the node AP (though they can request a reconnect even while the previous AP node is in STA mode). +* Unlike `WiFi.mode(WIFI_AP)`, the `WiFi.mode(WIFI_AP_STA)` which is used in this library allows TCP/IP nodes to stay connected to an AP they connect to while in STA mode, at the same time as they can receive connections from other stations. Nodes cannot send data to an AP while in STA_AP mode though, that requires STA mode. Switching to STA mode will sometimes disconnect stations connected to the node AP (though they can request a reconnect even while the previous AP node is in STA mode). + +## EspnowMeshBackend + +Unlike the TcpIpMeshBackend, the ESP-NOW backend uses pure callbacks even for message reception. This means that whenever `delay()` is called or the `loop()` function returns, the ESP-NOW backend will automatically check if an ESP-NOW message has been received and send it to the correct callback. There is no need to call `acceptRequests` as for the TcpIpMeshBackend. As a result of this, it is possible to receive an ingoing ESP-NOW transmission at the same time as an outgoing ESP-NOW transmission is in progress. This will likely be noted as a spike in the usual transmission time, the size of which will depend on the execution time of `requestHandler`/`responseHandler` (determined by transmission type). + +Some ESP-NOW tasks cannot be securely handled via callbacks. To manage this there are `espnowDelay` and `performEspnowMaintenance` functions available which handle these tasks separately. Either of these methods should be called regularly when your node has some time over for handling background tasks. + +### Usage + +There are two primary ways to send an ESP-NOW message: `broadcast` and `attemptTransmission`. + +If `broadcast` is used, the message is sent to all surrounding nodes in one transmission without any WiFi scan. When the surrounding nodes receive the broadcast they will send it to the `broadcastFilter` callback of the EspnowMeshBackend instance, and based on the return value of this callback either accept or reject the broadcast. The `broadcastFilter` callback is also responsible for removing any metadata from the broadcast. + +If `attemptTransmission` is used, a WiFi scan is by default performed before the transmission. The scan results are sent to the `networkFilter` callback function of the EspnowMeshBackend instance which adds the AP:s of interest to the `connectionQueue` vector. The message is then transmitted to the nodes in the `connectionQueue`. The outcome from each transmission attempt can be found in the `latestTransmissionOutcomes` vector. + +Regardless of whether `broadcast` or `attemptTransmission` is used, when a node receives a message (and it is accepted), the message is passed to the `requestHandler` callback of the EspnowMeshBackend instance. For each received message the return value of `requestHandler` is stored as a response in the `responsesToSend` waiting list. These stored responses will then be sent whenever `performEspnowMaintenance` (or `espnowDelay`) is called. + +When the response is received by the node that sent the request, the response message is forwarded to the `responseHandler` callback of the EspnowMeshBackend instance that sent the request. + +To be completely clear, requests are actually passed to the `broadcastFilter` and `requestHandler` callbacks belonging to the `EspnowRequestManager` of the node, but as long as there is only one EspnowMeshBackend instance on the node this will be the `EspnowRequestManager`. Also, since received ESP-NOW messages are handled via a callback, there is no need to call `acceptRequests` to receive messages, unlike with the TcpIpMeshBackend. + +The EspnowMeshBackend has a few different options for encrypting messages. This is described in greater detail in the [Encryption](#EspnowMeshBackendEncryption) section below. + +More information can be found in the source code comments and in the included HelloEspnow example. The main functions to modify in the example are `manageRequest` (`requestHandler`), `manageResponse` (`responseHandler`), `networkFilter` and `broadcastFilter`. + +### Note + +* `yield()` can cause crashes when using ESP-NOW, since the command requires code to be run in the CONT context. If you are having problems with this, use `delay()` instead. + +* This library uses the ESP8266 modules' MAC addresses to keep track of transmissions. So if you need to change the MAC addresses do so with care and preferably before any transmission is made. +Turning the AP off will make it impossible to send information to the node AP mac. However, it will still be possible to send the data to the station mac. +To do this, send over the station mac to the transmitting node and then manually add it to the `connectionQueue` whenever a transmission should be made to that node. + +* If the available heap goes under `criticalHeapLevel()` bytes (6000 bytes by default), the ESP-NOW backend will temporarily cease accepting new incoming ESP-NOW requests in an attempt to avoid running out of RAM. Warning messages about this will also be printed to the Serial Monitor, assuming `printWarnings()` is `true` (this is the default value). + +* During very heavy load the `performEspnowMaintenance` method may occasionally need to process requests for tens of milliseconds. Since this won't happen until the method is called, you can choose when this is done. Callbacks can be executed while the request processing is ongoing, but note that they should have a very fast execution time in this case. Also be sure to take into account the callback restrictions mentioned [below](#EspnowMeshBackendCallbacks). + +* When `WiFi.mode(WIFI_STA)` is used, nodes are unable to receive ESP-NOW broadcast messages. All nodes can however still receive direct ESP-NOW messages to their STA mac. Nodes seem to continue transmitting successfully to the correct (broadcast) MAC regardless of WiFi mode, only message reception is affected. Different combinations of ESP-NOW roles do not seem to have any influence on the outcome. Stripping out all library code and only using the bare minimum required for a broadcast does not change the outcome. Thus, this issue seems to be unfixable until corrected by Espressif. + + During testing it seemed for a while as though some nodes were able to receive ESP-NOW broadcasts even when in STA mode. There was no obvious difference between the nodes for which this worked and those for which it did not, so what caused this is unknown. Possibly the issue could have been caused by something stored on the nodes, perhaps a different persistent WiFi config or something similar. It is of course also possible that there was an error made during testing, but the event is noted here as it could be an avenue for further investigation. + +* Although ESP-NOW responses will generally be sent in the order they were created, this is not guaranteed to be the case. For example, response order will be mixed up if some responses first fail to transmit while others transmit successfully. Use the `ResponseTransmittedHook`callback if this behaviour should be modified. + +### Callbacks + +For maximum performance and minimum RAM usage it is very important that your callbacks and hooks can be handled quickly (within a few milliseconds, preferably), as node performance can start to suffer quickly otherwise, particularly if transmission intensity is high. Be especially wary of long Serial prints, as these require a lot of time to complete. If transmission activity is very low, it is however possible to have callbacks which take a long time to complete. In these cases, even a callback execution time of multiple seconds can be acceptable. Of course, you would get problems with other parts of the Arduino Core framework (like watch dog timer resets) if you don't call `delay()` or `ESP.wdtFeed()` within that time. + +Certain methods of the EspnowMeshBackend (e.g. `attemptTransmission`, `broadcast`, `espnowDelay` and `performEspnowMaintenance`) should not be used within callbacks, since this can mess with the internal state of the backend. These methods are all using a `MutexTracker` component to enforce this requirement via asserts, so if your nodes are crashing for unknown reasons when using callbacks, make sure to check the Serial Monitor to see if there are any mutex error messages! + +One way to resolve such errors is to simply always call the sensitive methods from the `loop()` instead of from a callback, possibly just storing the received value for later inside the callback. [PolledTimeout](https://github.com/esp8266/Arduino/blob/master/cores/esp8266/PolledTimeout.h) can be helpful for time tracking in this case. + +If a callback with the sensitive methods is required, it has been reported that the methods in `TaskScheduler.h` of the [TaskScheduler library](https://github.com/arkhipenko/TaskScheduler) work well when scheduling tasks. It can in this role be used as a replacement of the [Ticker](https://arduino-esp8266.readthedocs.io/en/latest/libraries.html#ticker) functionality in the Arduino Core. + +The reason the callback limitations exist is that during a transmission the library will only get an ack from the receiver when `delay()` is used. Yet `delay()` also calls all other background tasks, including user callbacks, and these must thus be safe to execute during ongoing transmissions. + +### Encryption + +There are two separate methods for encrypting a message with the ESP-NOW backend. One method creates an encrypted connection between two nodes using the built-in CCMP encryption of the ESP8266. The other method simply uses software AEAD to encrypt and decrypt the messages sent. + +More in-depth information about the encryption methods of the framework can be found at the top of the EspnowMeshBackend.h and EspnowProtocolInterpreter.h files. + +A brief overview of the advantages of each method: + +AEAD + +* The AEAD encryption does not require any pairing, and is thus faster for single messages than establishing a new encrypted connection before transfer. + +* AEAD encryption also works with ESP-NOW broadcasts and supports an unlimited number of nodes, which is not true for encrypted connections. + +CCMP + +* Using AEAD will only encrypt the message content, not the transmission metadata. CCMP encryption covers both. + +* Encrypted ESP-NOW connections come with built in replay attack protection, which is not provided by the framework when using AEAD encryption. + +* Encrypted ESP-NOW connections also allow `EspnowProtocolInterpreter::aeadMetadataSize` extra message bytes per transmission. + +* Transmissions via encrypted connections are also slightly faster than via AEAD once a connection has been established. + +#### CCMP + +For encrypted connections (managed via such methods as `addEncryptedConnection`, `requestEncryptedConnection` and `requestEncryptedConnectionRemoval`), ESP-NOW [uses](https://www.espressif.com/sites/default/files/documentation/esp-now_user_guide_en.pdf) [CCMP encryption](https://en.wikipedia.org/wiki/CCMP_(cryptography)). +To handle some idiosyncrasies of ESP-NOW (like having no way in the application layer to know if received information is encrypted or not), a separate API layer has been built on top. +This API layer is provided in the hope that it will be useful, but has not been subject to any cryptographic validation (yet, feel free to have a go at it if you have the knowledge). +The goal of the API layer is to ensure that when an encrypted connection is established, the received encrypted messages will both be marked as encrypted and be trustworthy. + +Established encrypted connections can be either permanent or temporary. A permanent encrypted connection can only be removed by explicitly calling `removeEncryptedConnection` or `requestEncryptedConnectionRemoval`. A temporary encrypted connection will expire once the duration has passed, although this duration can be updated through the methods used for adding new encrypted connections. + +The maximum number of simultaneous encrypted connections is restricted by the ESP-NOW specifications and is `EspnowProtocolInterpreter::maxEncryptedConnections` (6 by default). If required, a stricter soft upper limit can be used for the number of encrypted connections a node can have when receiving encrypted connection requests, to ensure there is normally some margin to the hard maximum. This is handled via the`setEncryptedConnectionsSoftLimit` method. + +The internal state of an encrypted connection will be lost if the ESP8266 is restarted or loses power, meaning encrypted messages will no longer be received. There is however functionality available to serialize the state of an encrypted connection so it can be restored later. The HelloEspnow.ino example file shows how this is done. Of course, a stored state should only be used once, since the communication will otherwise be susceptible to replay attacks. See "[Serialization and the internal state of a node](#FloodingMeshSerialization)" in the FloodingMesh docs for more info. + +Some security considerations for CCMP encrypted connections are listed below. + +* Part of the separate API layer uses the internal hardware random number generator of the ESP8266 (via `ESP.random()`) to initialize the connection state. This may or may not have enough entropy for your security needs. +For an even more random (but slower) number generation, you may want to replace the use of plain `ESP.random()` with something else. + +* Since there is no way to know whether a received transmission is encrypted or not via the default ESP-NOW API, an attacker can send unencrypted ESP-NOW messages which pretend to be encrypted without this being detected by the application. To prevent such attacks from happening, this framework uses an extra 64 bit session key for all encrypted connections. A message is only accepted as encrypted if it has the correct session key. 64 bits are used mainly because the uint64_t datatype is the largest natively supported by the ESP8266 Arduino Core, and because each ESP-NOW transmission has a relatively small maximum capacity of 250 bytes. + +* The ESP-NOW CCMP encryption should according to the standard have replay attack protection built in, but there is no official documentation from Espressif about this. The 64 bit session key used for encrypted connections, as described above, will however also ensure replay protection. + +* The maximum rate at which a potential attacker can poll a session key (via unencrypted transmissions pretending to be encrypted transmissions) is around 0.3 keys per ms, but in practice this rate would render the node completely unresponsive and is thus easily detected. +Assuming the rate above is used that would mean that an attacker in one day could try 0.3 x 1000 x 60 x 60 x 24 = 25 920 000 keys, which is roughly 1/711 600 000 000 of the total (total is 2^(64) - 2^(32), the top 32 session key bits are all 0 when the transmission is unencrypted). + +* Should there be a need for even more security, the user could enhance the library with 128 bit (or more) session keys, or ensure CCMP encrypted messages are sent frequently since this will rehash the session key every time, or frequently remove and re-add the encrypted connections (which will cause the session keys to be randomized or set to the supplied values). + +#### Authenticated Encryption with Associated Data (AEAD) + +In addition to using encrypted ESP-NOW connections the framework can send automatically encrypted messages (using AEAD) over both encrypted and unencrypted connections. This message encryption is conditioned on the `useEncryptedMessages()` flag of the EspnowMeshBackend. Typically, activating the AEAD encryption would be done like so: +``` +espnowBackendInstance.setEspnowMessageEncryptionKey(F("ChangeThisKeySeed_TODO")); // The message encryption key should always be set manually. Otherwise a default key (all zeroes) is used. +espnowBackendInstance.setUseEncryptedMessages(true); +``` + +The AEAD protocol uses the ChaCha20 stream cipher with Poly1305 for message authentication. +More information about this encryption standard can be found here: https://tools.ietf.org/html/rfc7539 , https://tools.ietf.org/html/rfc8439 + +## FloodingMesh + +**Important:** As of now, the ESP8266 must have the AP active to receive mesh messages (either via AP mode (use only if CCMP encryption is not required) or AP+STA mode). Messages can however be transmitted even when the AP is turned off. This is limited by the Espressif binary in the ESP8266 Arduino Core and so cannot be corrected by the library code. + +*** + +As the name implies, FloodingMesh is a simple flooding mesh architecture, which means it stores no mesh network routing data in the nodes but only passes new messages on to all surrounding nodes. It therefore has no RAM overhead for network size, which is important for the ESP8266 since available RAM is very limited. The downside is that there is a lot of network traffic for each sent message, and all nodes use the same WiFi channel, so especially for dense networks a lot of interference will be created. Based on tests, a mesh with 30 nodes close together (-44 dBm RSSI) will work well (1-2 dropped messages of 1000). A mesh with around 160 nodes close together will not work at all (though this would probably be solved by spreading out the nodes more, so the interference is reduced). + +The FloodingMesh exclusively uses the `EspnowMeshBackend`. The mesh network size is only limited by available MAC addresses, so the maximum is (2^48)/2 = 140 trillion give or take. However, the maximum throughput of the FloodingMesh is around 100 messages per second with 234 bytes per message, so using the maximum number of nodes is not recommended in most cases. Note that while ASCII characters require 1 message byte each, non-ASCII characters usually require 2 message bytes each. + +### Usage + +There are two primary ways to send a message in FloodingMesh: `broadcast` and `encryptedBroadcast`. + +Messages sent via `encryptedBroadcast` use CCMP encryption. Messages sent via `broadcast` are by default unencrypted, but can optionally be encrypted with AEAD encryption. See the "[Encryption](#EspnowMeshBackendEncryption)" segment of the EspnowMeshBackend documentation for more information on the forms of encryption. + +The main advantage of `encryptedBroadcast` over `broadcast` is that replay attack protection comes built-in. However, `encryptedBroadcast` is currently slow and experimental so for now `broadcast` is the recommended method to use. This means that replay attacks must be handled separately in a manner suitable for your application (e.g. by adding a counter to your messages or just by designing your application so repeated messages is not an issue). + +When `broadcast` is used, the message is sent to all surrounding nodes in one transmission without any WiFi scan. + +When a FloodingMesh node receives a message it will first check in its logs to see if the message ID has been received before. If the message ID is not found, the message will be passed to the `meshMessageHandler` of the FloodingMesh instance. + +If `meshMessageHandler` returns `false`, the message will not be propagated from the node. If `meshMessageHandler` returns `true`, the message (including any modifications made to it by the `meshMessageHandler`) will be stored in the `forwardingBacklog`. Messages stored in this way are automatically sent to all surrounding nodes via a new `broadcast` or `encryptedBroadcast` (same method as used for the received message) whenever `performMeshMaintenance()`, `performMeshInstanceMaintenance()` or `floodingMeshDelay` is called. + +For advanced users, the behaviour of FloodingMesh can easily be modified on the fly by changing the callbacks of the EspnowMeshBackend instance used by the FloodingMesh. The default behaviour can then be restored by calling the `restore` method for the respective callbacks. E.g. messages to forward in the FloodingMesh are by default stored in the `_defaultRequestHandler`, so call `floodingMeshInstance.getEspnowMeshBackend().setRequestHandler` with your own `requestHandler` function to modify this behaviour. + +More details can be found in the source code comments of both FloodingMesh and EspnowMeshBackend, as well as in the included HelloMesh example. The main function to modify in the example is `meshMessageHandler`. You can also change the `useLED` variable in the example to `true` if you have built-in LEDs on your ESP8266s to get visual feedback on how the message is spread through the mesh network. + +Note that there is no mesh recovery code in the HelloMesh example. It only selects one node (which is marked via the onboard LED if the `useLED` variable is `true`) and makes it continuously transmit. So if the selected node goes offline, no new transmissions will be made. One way to make the example mesh recover is to add a timeout to re-start the selection process if no message is received after a while. However, in practice you will probably want most or all nodes to broadcast their own messages, not just one selected node, so such a recovery timeout will not be useful in that context. + +**I want to know all the nodes in my FloodingMesh. What do I do?** + +To get a list of all nodes in the HelloMesh.ino example, you will have to make broadcast transmissions such as `floodingMesh.broadcast("Register MAC");` and then add code to register previously unknown `meshInstance.getOriginMac()` in the `meshMessageHandler`. + +**What's the best method to get the number of FloodingMesh nodes around me?** + +You could do a WiFi scan if you just want to see the nodes around you (if WiFi AP is enabled). Or you could make the nodes transmit and pick up the MACs with `meshInstance.getEspnowMeshBackend().getSenderMac()` in the `meshMessageHandler`. + +### Note + +Since FloodingMesh is based on EspnowMeshBackend, it shares all the limitations described for that backend above. In addition there are some more specific issues to keep in mind. + +* The network needs enough time to re-broadcast messages. In practice, if the mesh transmits more than 100 new messages per second (in total), there is a risk of running out of RAM since more messages will be received by the nodes than they can re-transmit. + +* A too low value for `messageLogSize` can result in a broadcast storm since the number of "active" messages will be greater than the log size, resulting in messages that bounce around in the network without end. The message log stores all unique FloodingMesh message IDs seen by a node, with more recent IDs replacing the older ones when `messageLogSize` is reached. This means that a node in a mesh network containing 2 nodes will have to send `messageLogSize + 1` transmissions to cause the message log of the other node to forget the first message, while a node in a mesh network containing 101 nodes will have to send 1 % as many messages (on average) to do the same. + + Use `FloodingMesh::setMessageLogSize` to adapt the log size to your needs. A larger log size will of course lead to a higher RAM usage. + +### Serialization and the internal state of a node + +The internal state of a node will be lost if it is restarted or loses power. There is however a method called `serializeMeshState()` available in FloodingMesh to serialize the state of a node so it can be restored later. Of course, a stored state should only be used once, since the communication will otherwise be susceptible to replay attacks. + +For the node state of FloodingMesh there are a few things to keep in mind. + +1. If you use the serialization functionality everything should just work. +2. If all nodes go to sleep without serializing, they will of course lose their memory but the network will be recreated and work as normal when the nodes wake up. +3. If only some nodes go to sleep without serializing the state, things get more complicated. The following is possible: + * If you use `encryptedBroadcast`, the nodes that wake up may silently ignore messages forever from the nodes they used to have an encrypted connection with. + * If you do not use `encryptedBroadcast` the ESP-NOW backend will by default clear its message ID logs in 2.5 seconds (`logEntryLifetimeMs`) and FloodingMesh will have done the same after 100 new message IDs have been received (`messageLogSize`). Once the logs of both classes have been cleared, things will work as normal. Before that, any new message the awoken node sends may have the same ID as an old message, and will then be silently ignored by the receiver. + +The messageID is always used together with the node MAC of the sender. For details on how the ID is generated, check out the `generateMessageID` methods. + +It is important to realize that there is no global message ID counter, only the local received message IDs for each node in the network. Automatic resynchronizing with this local value is currently only supported for encrypted connections, which exist exclusively between two nodes. For unencrypted connections, `addUnencryptedConnection` may be used manually for similar purposes. + +## FAQ + +### My ESP8266 crashes on start-up when I use the library! + +This could be caused by incorrect arguments to the constructors of the library. Usually you would get a Serial Monitor print of the error in question, but if the constructor is called before you call `Serial.begin(115200)` then there will be nothing to print to. The solution is first to check so that all constructor arguments are valid, e.g. that the mesh password has the correct length and does not contain any forbidden characters. If everything checks out you can try to move all the library constructors you use into the `setup()` function of your sketch, after the position where `Serial.begin(115200)` is called. That should give you a proper error message in the Serial Monitor, so you can locate the problem. + +### The node does not remember the SSID I assign to it! + +All example files use `WiFi.persistent(false)` in the `setup()` function, so if you switch the AP off and on again only by using `WiFi.mode()` without the framework methods (`activateAP`/`deactivateAP`), it is likely your last persisted SSID is used, not the one you set in the FloodingMesh/EspnowMeshBackend/TcpIpMeshBackend constructor. The solution is to always use the framework methods to turn the AP on and off, or to follow the instructions below for controlling WiFi mode. + +### I want to control the WiFi mode myself. + +By default the mesh library assumes it is the only code in charge of managing the WiFi. So it expects to be the middle man when the user wants to do something WiFi related. + +That being said, there are some relatively simple ways to go around this. Note that the steps below are not officially supported and may break in future library versions. + +The key to solving this is to note that the only methods of EspnowMeshBackend and FloodingMesh which interact with the WiFi mode is `begin()`, `activateAP()` and `deactivateAP()` (for TcpIpMeshBackend `attemptTransmission` should be added to this list). Let's take a look at the methods: + +``` +void EspnowMeshBackend::begin() +{ + if(!getAPController()) // If there is no active AP controller + WiFi.mode(WIFI_STA); // WIFI_AP_STA mode automatically sets up an AP, so we can't use that as default. + + activateEspnow(); +} + +void MeshBackendBase::activateAP() +{ + // Deactivate active AP to avoid two servers using the same port, which can lead to crashes. + if(MeshBackendBase *currentAPController = MeshBackendBase::getAPController()) + currentAPController->deactivateAP(); + + activateAPHook(); + + WiFi.mode(WIFI_AP_STA); + + apController = this; +} + +void MeshBackendBase::activateAPHook() +{ + WiFi.softAP( getSSID().c_str(), getMeshPassword().c_str(), getWiFiChannel(), getAPHidden() ); // Note that a maximum of 8 TCP/IP stations can be connected at a time to each AP, max 4 by default. +} + +void MeshBackendBase::deactivateAP() +{ + if(isAPController()) + { + deactivateAPHook(); + + WiFi.softAPdisconnect(); + WiFi.mode(WIFI_STA); + + // Since there is no active AP controller now, make the apController variable point to nothing. + apController = nullptr; + } +} + +void MeshBackendBase::deactivateAPHook() +{ +} +``` + +As you can see, there is nothing in `activateAP` and `deactivateAP` that you cannot do yourself. You do not have to worry about`apController` since it is only used if the mesh library is actually managing an AP (i.e. if `activateAP()` has been called), and the rest is standard Arduino Core WiFi calls. All you have to do then is to call `begin()` once when your program starts and then take responsibility yourself for activating and deactivating an AP with the correct SSID. Essentially, you would create the following function: + +``` +void myActivateAP() +{ + WiFi.softAP( SSID, password, WiFiChannel ); // You can store these values in the mesh backend and call the respective getters, but then you also have to set the backend values whenever they change. + WiFi.mode(WIFI_AP_STA); // Can also be WiFi.mode(WIFI_AP) +} +``` + +Please note that having an AP active is required when receiving broadcasts with FloodingMesh and EspnowMeshBackend (transmitting broadcasts work even when the AP is off). The regular `attemptTransmission` method will transmit even to nodes that have their AP turned off if the recipient STA MAC is already known (then you can set WiFi mode to any mode you like, apart from `WIFI_OFF`). + +When an AP is required, AP+STA mode is used in the ESP-NOW backend to keep compatibility with the TCP/IP backend (both backends can be used at the same time). The reason AP+STA mode is used in the TCP/IP backend can be found in TcpIpMeshBackend.cpp : "Unlike WiFi.mode(WIFI_AP);, WiFi.mode(WIFI_AP_STA); allows us to stay connected to the AP we connected to in STA mode, at the same time as we can receive connections from other stations." +Also, AP+STA mode allows encrypted ESP-NOW connections to recover from failure in some cases. + +So in summary, you can solve this by calling `begin()` once and then only using the library methods that do not interact with the WiFi mode. As long as you manage your own AP. + +### I have a lot of interference from all the nodes that are close to each other. What can I do? + +In general, you can switch WiFi channel for some nodes (use only channel 1, 6 and 11 for optimal spread, remember that nodes on different WiFi channels cannot communicate directly with each other), try to improve signal quality, or try to reduce interference by reducing the amount of transmissions in the network. + +If using FloodingMesh you can try to experiment with reducing error rates by using the mesh method `void setBroadcastReceptionRedundancy(uint8_t redundancy);` (default 2) at the cost of more RAM. + + +With both FloodingMesh and the EspnowMeshBackend it is possible to use `floodingMesh.getEspnowMeshBackend().setBroadcastTransmissionRedundancy(uint8_t redundancy)` (default 1) to increase the chance of a message arriving, at the cost of longer transmission times. + +For reducing the amount of transmissions in the network, that will either require you to optimize your transmission usage or reduce the amount of background protocol transmissions. The latter option is described in greater detail in the two answers below. + +### How do I change the interval of the WiFi AP beacon broadcast? + +Currently this requires hacking your Arduino Core source files. At [line 122](https://github.com/esp8266/Arduino/blob/8ee67ab2b53463466fd9f035eef2c542ad9a6775/libraries/ESP8266WiFi/src/ESP8266WiFiAP.cpp#L122) in `ESP8266WiFiAP.cpp` you will find the following line `conf.beacon_interval = 100;` (within the `softAp` method). You can change 100 to any value in the range [100, 60000] ms. If you are having problems with too many AP beacon broadcasts in a mesh network, increasing this value should help you with that. To prevent all nodes from beaconing at the same time, delay initial AP activation by a random value in the range [0, x] and then change `conf.beacon_interval` to x, for some large value x <= 60000 ms (same for all nodes). + +### My ESP is ignoring the WiFi AP beacon broadcast interval settings you just told me about above! (a.k.a. How do I change the WiFi scan mode to passive?) + +The default WiFi scan mode of the ESP8266 is active. This triggers a probe response by all AP:s that receives the probe request from the scan. So setting a different beacon interval time has little effect on the background transmission activity if a lot of active scans happen, since all nodes will start performing probe responses (at the same time) in response to the scans. + +However, we can change the scan mode so it is passive instead! That will avoid a flood of probe responses after every scan. The downside is that your scan will only detect the nodes that happen to beacon during the scan time. Since you may be able to use ESP-NOW broadcasts instead of AP beacons for node detection, this is perhaps not a problem if you just want to reduce background transmission activity as much as possible to reduce interference. + +Note though, that any device that uses active WiFi scans will trigger probe responses from the ESP8266, including smartphones and laptops. So even if you make all ESPs use passive scans, you can still end up with a lot of probe responses from the ESPs if they are close to other devices. The only way to fix this would be to disable the AP of the ESP8266, which of course will make it impossible to find the node via a WiFi scan, and also seems to make it impossible to receive ESP-NOW broadcasts (sending ESP-NOW broadcasts still work though, see the "[Note](#EspnowMeshBackendNote)" section of the EspnowMeshBackend documentation for more on this). + +To change the WiFi scan mode to passive, the following information is helpful: +1. A `scan_config` struct is found in `user_interface.h` (and the ESP8266 API documentation). We want to modify `scan_type`, but note that `scan_time` can also be set here if we want faster or slower scans. +2. In `ESP8266WiFiScan.cpp` one can find the following variable declaration: `struct scan_config config;` around line 87. Adding `config.scan_type = WIFI_SCAN_TYPE_PASSIVE;` after `memset(&config, 0, sizeof(config));` on line 88 will ensure passive scans are used. + +### My internet is slower when I connect the ESP8266 to my router! +There has been some reports about this happening when the ESP8266 is in AP+STA mode while connected to the router. The ESP8266 automatically switches to 802.11g in AP+STA mode, so if your router normally uses a faster WiFi standard such as 802.11n or 802.11ac the router may change mode of operation to 802.11g. Typically this would result in a maximum WiFi speed of around 30 Mbit/s. + +A possible workaround is to use only AP mode or STA mode (see "[I want to control the WiFi mode myself](#FAQModeControl)"), perhaps with an extra ESP8266 in one of these modes as a buffer between your ESP8266 mesh network and your router. Remember that the ESP8266 must have the AP active in order to receive ESP-NOW broadcast messages. -* Scanning for networks (e.g. via the `attemptTransmission` method) without the WiFi scan optimizations for core version 2.4.2 mentioned above, causes the WiFi radio to cycle through all WiFi channels which means existing WiFi connections are likely to break or work poorly if done frequently. \ No newline at end of file +Another possible workaround is to try with a different router or router firmware. diff --git a/libraries/ESP8266WiFiMesh/examples/HelloEspnow/HelloEspnow.ino b/libraries/ESP8266WiFiMesh/examples/HelloEspnow/HelloEspnow.ino new file mode 100644 index 0000000000..427084e312 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/examples/HelloEspnow/HelloEspnow.ino @@ -0,0 +1,448 @@ +#define ESP8266WIFIMESH_DISABLE_COMPATIBILITY // Excludes redundant compatibility code. TODO: Should be used for new code until the compatibility code is removed with release 3.0.0 of the Arduino core. + +#include +#include +#include +#include +#include + +namespace TypeCast = MeshTypeConversionFunctions; + +/** + NOTE: Although we could define the strings below as normal String variables, + here we are using PROGMEM combined with the FPSTR() macro (and also just the F() macro further down in the file). + The reason is that this approach will place the strings in flash memory which will help save RAM during program execution. + Reading strings from flash will be slower than reading them from RAM, + but this will be a negligible difference when printing them to Serial. + + More on F(), FPSTR() and PROGMEM: + https://github.com/esp8266/Arduino/issues/1143 + https://arduino-esp8266.readthedocs.io/en/latest/PROGMEM.html +*/ +constexpr char exampleMeshName[] PROGMEM = "MeshNode_"; // The name of the mesh network. Used as prefix for the node SSID and to find other network nodes in the example networkFilter and broadcastFilter functions below. +constexpr char exampleWiFiPassword[] PROGMEM = "ChangeThisWiFiPassword_TODO"; // Note: " is an illegal character. The password has to be min 8 and max 64 characters long, otherwise an AP which uses it will not be found during scans. + +// A custom encryption key is required when using encrypted ESP-NOW transmissions. There is always a default Kok set, but it can be replaced if desired. +// All ESP-NOW keys below must match in an encrypted connection pair for encrypted communication to be possible. +// Note that it is also possible to use Strings as key seeds instead of arrays. +uint8_t espnowEncryptedConnectionKey[16] = { 0x33, 0x44, 0x33, 0x44, 0x33, 0x44, 0x33, 0x44, // This is the key for encrypting transmissions of encrypted connections. + 0x33, 0x44, 0x33, 0x44, 0x33, 0x44, 0x32, 0x11 }; +uint8_t espnowEncryptionKok[16] = { 0x22, 0x44, 0x33, 0x44, 0x33, 0x44, 0x33, 0x44, // This is the key for encrypting the encrypted connection key. + 0x33, 0x44, 0x33, 0x44, 0x33, 0x44, 0x32, 0x33 }; +uint8_t espnowHashKey[16] = { 0xEF, 0x44, 0x33, 0x0C, 0x33, 0x44, 0xFE, 0x44, // This is the secret key used for HMAC during encrypted connection requests. + 0x33, 0x44, 0x33, 0xB0, 0x33, 0x44, 0x32, 0xAD }; + +unsigned int requestNumber = 0; +unsigned int responseNumber = 0; + +const char broadcastMetadataDelimiter = 23; // 23 = End-of-Transmission-Block (ETB) control character in ASCII + +String manageRequest(const String &request, MeshBackendBase &meshInstance); +TransmissionStatusType manageResponse(const String &response, MeshBackendBase &meshInstance); +void networkFilter(int numberOfNetworks, MeshBackendBase &meshInstance); +bool broadcastFilter(String &firstTransmission, EspnowMeshBackend &meshInstance); + +/* Create the mesh node object */ +EspnowMeshBackend espnowNode = EspnowMeshBackend(manageRequest, manageResponse, networkFilter, broadcastFilter, FPSTR(exampleWiFiPassword), espnowEncryptedConnectionKey, espnowHashKey, FPSTR(exampleMeshName), TypeCast::uint64ToString(ESP.getChipId()), true); + +/** + Callback for when other nodes send you a request + + @param request The request string received from another node in the mesh + @param meshInstance The MeshBackendBase instance that called the function. + @return The string to send back to the other node. For ESP-NOW, return an empty string ("") if no response should be sent. +*/ +String manageRequest(const String &request, MeshBackendBase &meshInstance) { + // To get the actual class of the polymorphic meshInstance, do as follows (meshBackendCast replaces dynamic_cast since RTTI is disabled) + if (EspnowMeshBackend *espnowInstance = TypeCast::meshBackendCast(&meshInstance)) { + String transmissionEncrypted = espnowInstance->receivedEncryptedTransmission() ? F(", Encrypted transmission") : F(", Unencrypted transmission"); + Serial.print(String(F("ESP-NOW (")) + espnowInstance->getSenderMac() + transmissionEncrypted + F("): ")); + } else if (TcpIpMeshBackend *tcpIpInstance = TypeCast::meshBackendCast(&meshInstance)) { + (void)tcpIpInstance; // This is useful to remove a "unused parameter" compiler warning. Does nothing else. + Serial.print(F("TCP/IP: ")); + } else { + Serial.print(F("UNKNOWN!: ")); + } + + /* Print out received message */ + // Only show first 100 characters because printing a large String takes a lot of time, which is a bad thing for a callback function. + // If you need to print the whole String it is better to store it and print it in the loop() later. + // Note that request.substring will not work as expected if the String contains null values as data. + Serial.print(F("Request received: ")); + + if (request.charAt(0) == 0) { + Serial.println(request); // substring will not work for multiStrings. + } else { + Serial.println(request.substring(0, 100)); + } + + /* return a string to send back */ + return (String(F("Hello world response #")) + String(responseNumber++) + F(" from ") + meshInstance.getMeshName() + meshInstance.getNodeID() + F(" with AP MAC ") + WiFi.softAPmacAddress() + String('.')); +} + +/** + Callback for when you get a response from other nodes + + @param response The response string received from another node in the mesh + @param meshInstance The MeshBackendBase instance that called the function. + @return The status code resulting from the response, as an int +*/ +TransmissionStatusType manageResponse(const String &response, MeshBackendBase &meshInstance) { + TransmissionStatusType statusCode = TransmissionStatusType::TRANSMISSION_COMPLETE; + + // To get the actual class of the polymorphic meshInstance, do as follows (meshBackendCast replaces dynamic_cast since RTTI is disabled) + if (EspnowMeshBackend *espnowInstance = TypeCast::meshBackendCast(&meshInstance)) { + String transmissionEncrypted = espnowInstance->receivedEncryptedTransmission() ? F(", Encrypted transmission") : F(", Unencrypted transmission"); + Serial.print(String(F("ESP-NOW (")) + espnowInstance->getSenderMac() + transmissionEncrypted + F("): ")); + } else if (TcpIpMeshBackend *tcpIpInstance = TypeCast::meshBackendCast(&meshInstance)) { + Serial.print(F("TCP/IP: ")); + + // Getting the sent message like this will work as long as ONLY(!) TCP/IP is used. + // With TCP/IP the response will follow immediately after the request, so the stored message will not have changed. + // With ESP-NOW there is no guarantee when or if a response will show up, it can happen before or after the stored message is changed. + // So for ESP-NOW, adding unique identifiers in the response and request is required to associate a response with a request. + Serial.print(F("Request sent: ")); + Serial.println(tcpIpInstance->getCurrentMessage().substring(0, 100)); + } else { + Serial.print(F("UNKNOWN!: ")); + } + + /* Print out received message */ + // Only show first 100 characters because printing a large String takes a lot of time, which is a bad thing for a callback function. + // If you need to print the whole String it is better to store it and print it in the loop() later. + // Note that response.substring will not work as expected if the String contains null values as data. + Serial.print(F("Response received: ")); + Serial.println(response.substring(0, 100)); + + return statusCode; +} + +/** + Callback used to decide which networks to connect to once a WiFi scan has been completed. + + @param numberOfNetworks The number of networks found in the WiFi scan. + @param meshInstance The MeshBackendBase instance that called the function. +*/ +void networkFilter(int numberOfNetworks, MeshBackendBase &meshInstance) { + // Note that the network index of a given node may change whenever a new scan is done. + for (int networkIndex = 0; networkIndex < numberOfNetworks; ++networkIndex) { + String currentSSID = WiFi.SSID(networkIndex); + int meshNameIndex = currentSSID.indexOf(meshInstance.getMeshName()); + + /* Connect to any _suitable_ APs which contain meshInstance.getMeshName() */ + if (meshNameIndex >= 0) { + uint64_t targetNodeID = TypeCast::stringToUint64(currentSSID.substring(meshNameIndex + meshInstance.getMeshName().length())); + + if (targetNodeID < TypeCast::stringToUint64(meshInstance.getNodeID())) { + if (EspnowMeshBackend *espnowInstance = TypeCast::meshBackendCast(&meshInstance)) { + espnowInstance->connectionQueue().emplace_back(networkIndex); + } else if (TcpIpMeshBackend *tcpIpInstance = TypeCast::meshBackendCast(&meshInstance)) { + tcpIpInstance->connectionQueue().emplace_back(networkIndex); + } else { + Serial.println(F("Invalid mesh backend!")); + } + } + } + } +} + +/** + Callback used to decide which broadcast messages to accept. Only called for the first transmission in each broadcast. + If true is returned from this callback, the first broadcast transmission is saved until the entire broadcast message has been received. + The complete broadcast message will then be sent to the requestHandler (manageRequest in this example). + If false is returned from this callback, the broadcast message is discarded. + Note that the BroadcastFilter may be called multiple times for messages that are discarded in this way, but is only called once for accepted messages. + + @param firstTransmission The first transmission of the broadcast. Modifications to this String are passed on to the broadcast message. + @param meshInstance The EspnowMeshBackend instance that called the function. + + @return True if the broadcast should be accepted. False otherwise. +*/ +bool broadcastFilter(String &firstTransmission, EspnowMeshBackend &meshInstance) { + // This example broadcastFilter will accept a transmission if it contains the broadcastMetadataDelimiter + // and as metaData either no targetMeshName or a targetMeshName that matches the MeshName of meshInstance. + + int32_t metadataEndIndex = firstTransmission.indexOf(broadcastMetadataDelimiter); + + if (metadataEndIndex == -1) { + return false; // broadcastMetadataDelimiter not found + } + + String targetMeshName = firstTransmission.substring(0, metadataEndIndex); + + if (!targetMeshName.isEmpty() && meshInstance.getMeshName() != targetMeshName) { + return false; // Broadcast is for another mesh network + } else { + // Remove metadata from message and mark as accepted broadcast. + // Note that when you modify firstTransmission it is best to avoid using substring or other String methods that rely on null values for String length determination. + // Otherwise your broadcasts cannot include null values in the message bytes. + firstTransmission.remove(0, metadataEndIndex + 1); + return true; + } +} + +/** + Once passed to the setTransmissionOutcomesUpdateHook method of the ESP-NOW backend, + this function will be called after each update of the latestTransmissionOutcomes vector during attemptTransmission. + (which happens after each individual transmission has finished) + + Example use cases is modifying getMessage() between transmissions, or aborting attemptTransmission before all nodes in the connectionQueue have been contacted. + + @param meshInstance The MeshBackendBase instance that called the function. + + @return True if attemptTransmission should continue with the next entry in the connectionQueue. False if attemptTransmission should stop. +*/ +bool exampleTransmissionOutcomesUpdateHook(MeshBackendBase &meshInstance) { + // Currently this is exactly the same as the default hook, but you can modify it to alter the behaviour of attemptTransmission. + + (void)meshInstance; // This is useful to remove a "unused parameter" compiler warning. Does nothing else. + + return true; +} + +/** + Once passed to the setResponseTransmittedHook method of the ESP-NOW backend, + this function will be called after each attempted ESP-NOW response transmission. + In case of a successful response transmission, this happens just before the response is removed from the waiting list. + Only the hook of the EspnowMeshBackend instance that is getEspnowRequestManager() will be called. + + @param transmissionSuccessful True if the response was transmitted successfully. False otherwise. + @param response The sent response. + @param recipientMac The MAC address the response was sent to. + @param responseIndex The index of the response in the waiting list. + @param meshInstance The EspnowMeshBackend instance that called the function. + + @return True if the response transmission process should continue with the next response in the waiting list. + False if the response transmission process should stop once processing of the just sent response is complete. +*/ +bool exampleResponseTransmittedHook(bool transmissionSuccessful, const String &response, const uint8_t *recipientMac, uint32_t responseIndex, EspnowMeshBackend &meshInstance) { + // Currently this is exactly the same as the default hook, but you can modify it to alter the behaviour of sendEspnowResponses. + + (void)transmissionSuccessful; // This is useful to remove a "unused parameter" compiler warning. Does nothing else. + (void)response; + (void)recipientMac; + (void)responseIndex; + (void)meshInstance; + + return true; +} + +void setup() { + // Prevents the flash memory from being worn out, see: https://github.com/esp8266/Arduino/issues/1054 . + // This will however delay node WiFi start-up by about 700 ms. The delay is 900 ms if we otherwise would have stored the WiFi network we want to connect to. + WiFi.persistent(false); + + Serial.begin(115200); + + Serial.println(); + Serial.println(); + + Serial.println(F("Note that this library can use static IP:s for the nodes with the TCP/IP backend to speed up connection times.\n" + "Use the setStaticIP method to enable this.\n" + "Ensure that nodes connecting to the same AP have distinct static IP:s.\n" + "Also, remember to change the default mesh network password and ESP-NOW keys!\n\n")); + + Serial.println(F("Setting up mesh node...")); + + /* Initialise the mesh node */ + espnowNode.begin(); + + // Note: This changes the Kok for all EspnowMeshBackend instances on this ESP8266. + // Encrypted connections added before the Kok change will retain their old Kok. + // Both Kok and encrypted connection key must match in an encrypted connection pair for encrypted communication to be possible. + // Otherwise the transmissions will never reach the recipient, even though acks are received by the sender. + EspnowMeshBackend::setEspnowEncryptionKok(espnowEncryptionKok); + espnowNode.setEspnowEncryptedConnectionKey(espnowEncryptedConnectionKey); + + // Makes it possible to find the node through scans, makes it possible to recover from an encrypted connection where only the other node is encrypted, and also makes it possible to receive broadcast transmissions. + // Note that only one AP can be active at a time in total, and this will always be the one which was last activated. + // Thus the AP is shared by all backends. + espnowNode.activateAP(); + + // Storing our message in the EspnowMeshBackend instance is not required, but can be useful for organizing code, especially when using many EspnowMeshBackend instances. + // Note that calling the multi-recipient versions of espnowNode.attemptTransmission and espnowNode.attemptAutoEncryptingTransmission will replace the stored message with whatever message is transmitted. + // Also note that the maximum allowed number of ASCII characters in a ESP-NOW message is given by EspnowMeshBackend::getMaxMessageLength(). + espnowNode.setMessage(String(F("Hello world request #")) + String(requestNumber) + F(" from ") + espnowNode.getMeshName() + espnowNode.getNodeID() + String('.')); + + espnowNode.setTransmissionOutcomesUpdateHook(exampleTransmissionOutcomesUpdateHook); + espnowNode.setResponseTransmittedHook(exampleResponseTransmittedHook); + + // In addition to using encrypted ESP-NOW connections the framework can also send automatically encrypted messages (AEAD) over both encrypted and unencrypted connections. + // Using AEAD will only encrypt the message content, not the transmission metadata. + // The AEAD encryption does not require any pairing, and is thus faster for single messages than establishing a new encrypted connection before transfer. + // AEAD encryption also works with ESP-NOW broadcasts and supports an unlimited number of nodes, which is not true for encrypted connections. + // Encrypted ESP-NOW connections do however come with built in replay attack protection, which is not provided by the framework when using AEAD encryption, + // and allow EspnowProtocolInterpreter::aeadMetadataSize extra message bytes per transmission. + // Transmissions via encrypted connections are also slightly faster than via AEAD once a connection has been established. + // + // Uncomment the lines below to use automatic AEAD encryption/decryption of messages sent/received. + // All nodes this node wishes to communicate with must then also use encrypted messages with the same getEspnowMessageEncryptionKey(), or messages will not be accepted. + // Note that using AEAD encrypted messages will reduce the number of message bytes that can be transmitted. + // espnowNode.setEspnowMessageEncryptionKey(F("ChangeThisKeySeed_TODO")); // The message encryption key should always be set manually. Otherwise a default key (all zeroes) is used. + // espnowNode.setUseEncryptedMessages(true); +} + +int32_t timeOfLastScan = -10000; +void loop() { + // The performEspnowMaintenance() method performs all the background operations for the EspnowMeshBackend. + // It is recommended to place it in the beginning of the loop(), unless there is a need to put it elsewhere. + // Among other things, the method cleans up old Espnow log entries (freeing up RAM) and sends the responses you provide to Espnow requests. + // Note that depending on the amount of responses to send and their length, this method can take tens or even hundreds of milliseconds to complete. + // More intense transmission activity and less frequent calls to performEspnowMaintenance will likely cause the method to take longer to complete, so plan accordingly. + + // Should not be used inside responseHandler, requestHandler, networkFilter or broadcastFilter callbacks since performEspnowMaintenance() can alter the ESP-NOW state. + EspnowMeshBackend::performEspnowMaintenance(); + + if (millis() - timeOfLastScan > 10000) { // Give other nodes some time to connect between data transfers. + Serial.println(F("\nPerforming unencrypted ESP-NOW transmissions.")); + + uint32_t startTime = millis(); + espnowNode.attemptTransmission(espnowNode.getMessage()); + Serial.println(String(F("Scan and ")) + String(espnowNode.latestTransmissionOutcomes().size()) + F(" transmissions done in ") + String(millis() - startTime) + F(" ms.")); + + timeOfLastScan = millis(); + + // Wait for response. espnowDelay continuously calls performEspnowMaintenance() so we will respond to ESP-NOW request while waiting. + // Should not be used inside responseHandler, requestHandler, networkFilter or broadcastFilter callbacks since performEspnowMaintenance() can alter the ESP-NOW state. + espnowDelay(100); + + // One way to check how attemptTransmission worked out + if (espnowNode.latestTransmissionSuccessful()) { Serial.println(F("Transmission successful.")); } + + // Another way to check how attemptTransmission worked out + if (espnowNode.latestTransmissionOutcomes().empty()) { + Serial.println(F("No mesh AP found.")); + } else { + for (TransmissionOutcome &transmissionOutcome : espnowNode.latestTransmissionOutcomes()) { + if (transmissionOutcome.transmissionStatus() == TransmissionStatusType::TRANSMISSION_FAILED) { + Serial.println(String(F("Transmission failed to mesh AP ")) + transmissionOutcome.SSID()); + } else if (transmissionOutcome.transmissionStatus() == TransmissionStatusType::CONNECTION_FAILED) { + Serial.println(String(F("Connection failed to mesh AP ")) + transmissionOutcome.SSID()); + } else if (transmissionOutcome.transmissionStatus() == TransmissionStatusType::TRANSMISSION_COMPLETE) { + // No need to do anything, transmission was successful. + } else { + Serial.println(String(F("Invalid transmission status for ")) + transmissionOutcome.SSID() + String('!')); + assert(F("Invalid transmission status returned from responseHandler!") && false); + } + } + + Serial.println(F("\nPerforming ESP-NOW broadcast.")); + + startTime = millis(); + + // Remove espnowNode.getMeshName() from the broadcastMetadata below to broadcast to all ESP-NOW nodes regardless of MeshName. + // Note that data that comes before broadcastMetadataDelimiter should not contain any broadcastMetadataDelimiter characters, + // otherwise the broadcastFilter function used in this example file will not work. + String broadcastMetadata = espnowNode.getMeshName() + String(broadcastMetadataDelimiter); + String broadcastMessage = String(F("Broadcast #")) + String(requestNumber) + F(" from ") + espnowNode.getMeshName() + espnowNode.getNodeID() + String('.'); + espnowNode.broadcast(broadcastMetadata + broadcastMessage); + Serial.println(String(F("Broadcast to all mesh nodes done in ")) + String(millis() - startTime) + F(" ms.")); + + espnowDelay(100); // Wait for responses (broadcasts can receive an unlimited number of responses, other transmissions can only receive one response). + + // If you have a data array containing null values it is possible to transmit the raw data by making the array into a multiString as shown below. + // You can use String::c_str() or String::begin() to retrieve the data array later. + // Note that certain String methods such as String::substring use null values to determine String length, which means they will not work as normal with multiStrings. + uint8_t dataArray[] = { 0, '\'', 0, '\'', ' ', '(', 'n', 'u', 'l', 'l', ')', ' ', 'v', 'a', 'l', 'u', 'e' }; + String espnowMessage = TypeCast::uint8ArrayToMultiString(dataArray, sizeof dataArray) + F(" from ") + espnowNode.getMeshName() + espnowNode.getNodeID() + String('.'); + Serial.println(String(F("\nTransmitting: ")) + espnowMessage); + espnowNode.attemptTransmission(espnowMessage, false); + espnowDelay(100); // Wait for response. + + Serial.println(F("\nPerforming encrypted ESP-NOW transmissions.")); + + uint8_t targetBSSID[6]{ 0 }; + + // We can create encrypted connections to individual nodes so that all ESP-NOW communication with the node will be encrypted. + if (espnowNode.constConnectionQueue()[0].getBSSID(targetBSSID) && espnowNode.requestEncryptedConnection(targetBSSID) == EncryptedConnectionStatus::CONNECTION_ESTABLISHED) { + // The WiFi scan will detect the AP MAC, but this will automatically be converted to the encrypted STA MAC by the framework. + String peerMac = TypeCast::macToString(targetBSSID); + + Serial.println(String(F("Encrypted ESP-NOW connection with ")) + peerMac + F(" established!")); + + // Making a transmission now will cause messages to targetBSSID to be encrypted. + String espnowMessage = String(F("This message is encrypted only when received by node ")) + peerMac; + Serial.println(String(F("\nTransmitting: ")) + espnowMessage); + espnowNode.attemptTransmission(espnowMessage, false); + espnowDelay(100); // Wait for response. + + // A connection can be serialized and stored for later use. + // Note that this saves the current state only, so if encrypted communication between the nodes happen after this, the stored state is invalid. + String serializedEncryptedConnection = EspnowMeshBackend::serializeEncryptedConnection(targetBSSID); + + Serial.println(); + // We can remove an encrypted connection like so. + espnowNode.removeEncryptedConnection(targetBSSID); + + // Note that the peer will still be encrypted, so although we can send unencrypted messages to the peer, we cannot read the encrypted responses it sends back. + espnowMessage = String(F("This message is no longer encrypted when received by node ")) + peerMac; + Serial.println(String(F("\nTransmitting: ")) + espnowMessage); + espnowNode.attemptTransmission(espnowMessage, false); + espnowDelay(100); // Wait for response. + Serial.println(F("Cannot read the encrypted response...")); + + // Let's re-add our stored connection so we can communicate properly with targetBSSID again! + espnowNode.addEncryptedConnection(serializedEncryptedConnection); + + espnowMessage = String(F("This message is once again encrypted when received by node ")) + peerMac; + Serial.println(String(F("\nTransmitting: ")) + espnowMessage); + espnowNode.attemptTransmission(espnowMessage, false); + espnowDelay(100); // Wait for response. + + Serial.println(); + // If we want to remove the encrypted connection on both nodes, we can do it like this. + EncryptedConnectionRemovalOutcome removalOutcome = espnowNode.requestEncryptedConnectionRemoval(targetBSSID); + if (removalOutcome == EncryptedConnectionRemovalOutcome::REMOVAL_SUCCEEDED) { + Serial.println(peerMac + F(" is no longer encrypted!")); + + espnowMessage = String(F("This message is only received by node ")) + peerMac + F(". Transmitting in this way will not change the transmission state of the sender."); + Serial.println(String(F("Transmitting: ")) + espnowMessage); + espnowNode.attemptTransmission(espnowMessage, EspnowNetworkInfo(targetBSSID)); + espnowDelay(100); // Wait for response. + + Serial.println(); + + // Of course, we can also just create a temporary encrypted connection that will remove itself once its duration has passed. + if (espnowNode.requestTemporaryEncryptedConnection(targetBSSID, 1000) == EncryptedConnectionStatus::CONNECTION_ESTABLISHED) { + espnowDelay(42); + uint32_t remainingDuration = 0; + EspnowMeshBackend::getConnectionInfo(targetBSSID, &remainingDuration); + + espnowMessage = String(F("Messages this node sends to ")) + peerMac + F(" will be encrypted for ") + String(remainingDuration) + F(" ms more."); + Serial.println(String(F("\nTransmitting: ")) + espnowMessage); + espnowNode.attemptTransmission(espnowMessage, false); + + EspnowMeshBackend::getConnectionInfo(targetBSSID, &remainingDuration); + espnowDelay(remainingDuration + 100); + + espnowMessage = String(F("Due to encrypted connection expiration, this message is no longer encrypted when received by node ")) + peerMac; + Serial.println(String(F("\nTransmitting: ")) + espnowMessage); + espnowNode.attemptTransmission(espnowMessage, false); + espnowDelay(100); // Wait for response. + } + + // Or if we prefer we can just let the library automatically create brief encrypted connections which are long enough to transmit an encrypted message. + // Note that encrypted responses will not be received, unless there already was an encrypted connection established with the peer before attemptAutoEncryptingTransmission was called. + // This can be remedied via the requestPermanentConnections argument, though it must be noted that the maximum number of encrypted connections supported at a time is 6. + espnowMessage = F("This message is always encrypted, regardless of receiver."); + Serial.println(String(F("\nTransmitting: ")) + espnowMessage); + espnowNode.attemptAutoEncryptingTransmission(espnowMessage); + espnowDelay(100); // Wait for response. + } else { + Serial.println(String(F("Ooops! Encrypted connection removal failed. Status: ")) + String(static_cast(removalOutcome))); + } + + // Finally, should you ever want to stop other parties from sending unencrypted messages to the node + // setAcceptsUnencryptedRequests(false); + // can be used for this. It applies to both encrypted connection requests and regular transmissions. + + Serial.println(F("\n##############################################################################################")); + } + + // Our last request was sent to all nodes found, so time to create a new request. + espnowNode.setMessage(String(F("Hello world request #")) + String(++requestNumber) + F(" from ") + espnowNode.getMeshName() + espnowNode.getNodeID() + String('.')); + } + + Serial.println(); + } +} diff --git a/libraries/ESP8266WiFiMesh/examples/HelloMesh/HelloMesh.ino b/libraries/ESP8266WiFiMesh/examples/HelloMesh/HelloMesh.ino index cdd3e6d5c9..4129882acf 100644 --- a/libraries/ESP8266WiFiMesh/examples/HelloMesh/HelloMesh.ino +++ b/libraries/ESP8266WiFiMesh/examples/HelloMesh/HelloMesh.ino @@ -1,7 +1,21 @@ +/** + This example makes every node broadcast their AP MAC to the rest of the network during the first 28 seconds, as long as the node thinks it has the highest AP MAC in the network. + Once 28 seconds have passed, the node that has the highest AP MAC will start broadcasting benchmark messages, which will allow you to see how many messages are lost at the other nodes. + If you have an onboard LED on your ESP8266 it is recommended that you change the useLED variable below to true. + That way you will get instant confirmation of the mesh communication without checking the Serial Monitor. + + If you want to experiment with reducing error rates you can use the mesh method "void setBroadcastReceptionRedundancy(uint8_t redundancy);" (default 2) at the cost of more RAM. + Or "floodingMesh.getEspnowMeshBackend().setBroadcastTransmissionRedundancy(uint8_t redundancy)" (default 1) at the cost of longer transmission times. +*/ + +#define ESP8266WIFIMESH_DISABLE_COMPATIBILITY // Excludes redundant compatibility code. TODO: Should be used for new code until the compatibility code is removed with release 3.0.0 of the Arduino core. + #include -#include #include #include +#include + +namespace TypeCast = MeshTypeConversionFunctions; /** NOTE: Although we could define the strings below as normal String variables, @@ -14,84 +28,89 @@ https://github.com/esp8266/Arduino/issues/1143 https://arduino-esp8266.readthedocs.io/en/latest/PROGMEM.html */ -const char exampleMeshName[] PROGMEM = "MeshNode_"; -const char exampleWiFiPassword[] PROGMEM = "ChangeThisWiFiPassword_TODO"; +constexpr char exampleMeshName[] PROGMEM = "MeshNode_"; // The name of the mesh network. Used as prefix for the node SSID and to find other network nodes in the example networkFilter and broadcastFilter functions below. +constexpr char exampleWiFiPassword[] PROGMEM = "ChangeThisWiFiPassword_TODO"; // Note: " is an illegal character. The password has to be min 8 and max 64 characters long, otherwise an AP which uses it will not be found during scans. -unsigned int requestNumber = 0; -unsigned int responseNumber = 0; +// A custom encryption key is required when using encrypted ESP-NOW transmissions. There is always a default Kok set, but it can be replaced if desired. +// All ESP-NOW keys below must match in an encrypted connection pair for encrypted communication to be possible. +// Note that it is also possible to use Strings as key seeds instead of arrays. +uint8_t espnowEncryptedConnectionKey[16] = { 0x33, 0x44, 0x33, 0x44, 0x33, 0x44, 0x33, 0x44, // This is the key for encrypting transmissions of encrypted connections. + 0x33, 0x44, 0x33, 0x44, 0x33, 0x44, 0x32, 0x11 }; +uint8_t espnowHashKey[16] = { 0xEF, 0x44, 0x33, 0x0C, 0x33, 0x44, 0xFE, 0x44, // This is the secret key used for HMAC during encrypted connection requests. + 0x33, 0x44, 0x33, 0xB0, 0x33, 0x44, 0x32, 0xAD }; -String manageRequest(const String &request, ESP8266WiFiMesh &meshInstance); -transmission_status_t manageResponse(const String &response, ESP8266WiFiMesh &meshInstance); -void networkFilter(int numberOfNetworks, ESP8266WiFiMesh &meshInstance); +bool meshMessageHandler(String &message, FloodingMesh &meshInstance); /* Create the mesh node object */ -ESP8266WiFiMesh meshNode = ESP8266WiFiMesh(manageRequest, manageResponse, networkFilter, FPSTR(exampleWiFiPassword), FPSTR(exampleMeshName), "", true); +FloodingMesh floodingMesh = FloodingMesh(meshMessageHandler, FPSTR(exampleWiFiPassword), espnowEncryptedConnectionKey, espnowHashKey, FPSTR(exampleMeshName), TypeCast::uint64ToString(ESP.getChipId()), true); -/** - Callback for when other nodes send you a request +bool theOne = true; +String theOneMac; - @param request The request string received from another node in the mesh - @param meshInstance The ESP8266WiFiMesh instance that called the function. - @returns The string to send back to the other node -*/ -String manageRequest(const String &request, ESP8266WiFiMesh &meshInstance) { - // We do not store strings in flash (via F()) in this function. - // The reason is that the other node will be waiting for our response, - // so keeping the strings in RAM will give a (small) improvement in response time. - // Of course, it is advised to adjust this approach based on RAM requirements. - - /* Print out received message */ - Serial.print("Request received: "); - Serial.println(request); - - /* return a string to send back */ - return ("Hello world response #" + String(responseNumber++) + " from " + meshInstance.getMeshName() + meshInstance.getNodeID() + "."); -} +bool useLED = false; // Change this to true if you wish the onboard LED to mark The One. /** - Callback for when you get a response from other nodes - - @param response The response string received from another node in the mesh - @param meshInstance The ESP8266WiFiMesh instance that called the function. - @returns The status code resulting from the response, as an int + Callback for when a message is received from the mesh network. + + @param message The message String received from the mesh. + Modifications to this String are passed on when the message is forwarded from this node to other nodes. + However, the forwarded message will still use the same messageID. + Thus it will not be sent to nodes that have already received this messageID. + If you want to send a new message to the whole network, use a new broadcast from within the loop() instead. + @param meshInstance The FloodingMesh instance that received the message. + @return True if this node should forward the received message to other nodes. False otherwise. */ -transmission_status_t manageResponse(const String &response, ESP8266WiFiMesh &meshInstance) { - transmission_status_t statusCode = TS_TRANSMISSION_COMPLETE; - - /* Print out received message */ - Serial.print(F("Request sent: ")); - Serial.println(meshInstance.getMessage()); - Serial.print(F("Response received: ")); - Serial.println(response); +bool meshMessageHandler(String &message, FloodingMesh &meshInstance) { + int32_t delimiterIndex = message.indexOf(meshInstance.metadataDelimiter()); + if (delimiterIndex == 0) { + Serial.print(String(F("Message received from STA MAC ")) + meshInstance.getEspnowMeshBackend().getSenderMac() + F(": ")); + Serial.println(message.substring(2, 102)); + + String potentialMac = message.substring(2, 14); + + if (potentialMac >= theOneMac) { + if (potentialMac > theOneMac) { + theOne = false; + theOneMac = potentialMac; + } - // Our last request got a response, so time to create a new request. - meshInstance.setMessage(String(F("Hello world request #")) + String(++requestNumber) + String(F(" from ")) - + meshInstance.getMeshName() + meshInstance.getNodeID() + String(F("."))); + if (useLED && !theOne) { + bool ledState = message.charAt(1) == '1'; + digitalWrite(LED_BUILTIN, ledState); // Turn LED on/off (LED_BUILTIN is active low) + } - // (void)meshInstance; // This is useful to remove a "unused parameter" compiler warning. Does nothing else. - return statusCode; -} + return true; + } else { + return false; + } + } else if (delimiterIndex > 0) { + if (meshInstance.getOriginMac() == theOneMac) { + uint32_t totalBroadcasts = strtoul(message.c_str(), nullptr, 0); // strtoul stops reading input when an invalid character is discovered. -/** - Callback used to decide which networks to connect to once a WiFi scan has been completed. + // Static variables are only initialized once. + static uint32_t firstBroadcast = totalBroadcasts; - @param numberOfNetworks The number of networks found in the WiFi scan. - @param meshInstance The ESP8266WiFiMesh instance that called the function. -*/ -void networkFilter(int numberOfNetworks, ESP8266WiFiMesh &meshInstance) { - for (int networkIndex = 0; networkIndex < numberOfNetworks; ++networkIndex) { - String currentSSID = WiFi.SSID(networkIndex); - int meshNameIndex = currentSSID.indexOf(meshInstance.getMeshName()); + if (totalBroadcasts - firstBroadcast >= 100) { // Wait a little to avoid start-up glitches + static uint32_t missedBroadcasts = 1; // Starting at one to compensate for initial -1 below. + static uint32_t previousTotalBroadcasts = totalBroadcasts; + static uint32_t totalReceivedBroadcasts = 0; + totalReceivedBroadcasts++; - /* Connect to any _suitable_ APs which contain meshInstance.getMeshName() */ - if (meshNameIndex >= 0) { - uint64_t targetNodeID = stringToUint64(currentSSID.substring(meshNameIndex + meshInstance.getMeshName().length())); + missedBroadcasts += totalBroadcasts - previousTotalBroadcasts - 1; // We expect an increment by 1. + previousTotalBroadcasts = totalBroadcasts; - if (targetNodeID < stringToUint64(meshInstance.getNodeID())) { - ESP8266WiFiMesh::connectionQueue.push_back(NetworkInfo(networkIndex)); + if (totalReceivedBroadcasts % 50 == 0) { Serial.println(String(F("missed/total: ")) + String(missedBroadcasts) + '/' + String(totalReceivedBroadcasts)); } + if (totalReceivedBroadcasts % 500 == 0) { Serial.println(String(F("Benchmark message: ")) + message.substring(0, 100)); } } } + } else { + // Only show first 100 characters because printing a large String takes a lot of time, which is a bad thing for a callback function. + // If you need to print the whole String it is better to store it and print it in the loop() later. + Serial.print(String(F("Message with origin ")) + meshInstance.getOriginMac() + F(" received: ")); + Serial.println(message.substring(0, 100)); } + + return true; } void setup() { @@ -100,63 +119,73 @@ void setup() { WiFi.persistent(false); Serial.begin(115200); - delay(50); // Wait for Serial. - - //yield(); // Use this if you don't want to wait for Serial. - - // The WiFi.disconnect() ensures that the WiFi is working correctly. If this is not done before receiving WiFi connections, - // those WiFi connections will take a long time to make or sometimes will not work at all. - WiFi.disconnect(); Serial.println(); Serial.println(); - Serial.println(F("Note that this library can use static IP:s for the nodes to speed up connection times.\n" - "Use the setStaticIP method as shown in this example to enable this.\n" - "Ensure that nodes connecting to the same AP have distinct static IP:s.\n" - "Also, remember to change the default mesh network password!\n\n")); + Serial.println(F("If you have an onboard LED on your ESP8266 it is recommended that you change the useLED variable to true.\n" + "That way you will get instant confirmation of the mesh communication.\n" + "Also, remember to change the default mesh network password and ESP-NOW keys!\n")); Serial.println(F("Setting up mesh node...")); - /* Initialise the mesh node */ - meshNode.begin(); - meshNode.activateAP(); // Each AP requires a separate server port. - meshNode.setStaticIP(IPAddress(192, 168, 4, 22)); // Activate static IP mode to speed up connection times. + floodingMesh.begin(); + floodingMesh.activateAP(); // Required to receive messages + + uint8_t apMacArray[6]{ 0 }; + theOneMac = TypeCast::macToString(WiFi.softAPmacAddress(apMacArray)); + + if (useLED) { + pinMode(LED_BUILTIN, OUTPUT); // Initialize the LED_BUILTIN pin as an output + digitalWrite(LED_BUILTIN, LOW); // Turn LED on (LED_BUILTIN is active low) + } + + // Uncomment the lines below to use automatic AEAD encryption/decryption of messages sent/received via broadcast() and encryptedBroadcast(). + // The main benefit of AEAD encryption is that it can be used with normal broadcasts (which are substantially faster than encryptedBroadcasts). + // The main drawbacks are that AEAD only encrypts the message data (not transmission metadata), transfers less data per message and lacks replay attack protection. + // When using AEAD, potential replay attacks must thus be handled manually. + // floodingMesh.getEspnowMeshBackend().setEspnowMessageEncryptionKey(F("ChangeThisKeySeed_TODO")); // The message encryption key should always be set manually. Otherwise a default key (all zeroes) is used. + // floodingMesh.getEspnowMeshBackend().setUseEncryptedMessages(true); + + floodingMeshDelay(5000); // Give some time for user to start the nodes } -int32_t timeOfLastScan = -10000; +int32_t timeOfLastProclamation = -10000; void loop() { - if (millis() - timeOfLastScan > 3000 // Give other nodes some time to connect between data transfers. - || (WiFi.status() != WL_CONNECTED && millis() - timeOfLastScan > 2000)) { // Scan for networks with two second intervals when not already connected. - String request = String(F("Hello world request #")) + String(requestNumber) + String(F(" from ")) + meshNode.getMeshName() + meshNode.getNodeID() + String(F(".")); - meshNode.attemptTransmission(request, false); - timeOfLastScan = millis(); - - // One way to check how attemptTransmission worked out - if (ESP8266WiFiMesh::latestTransmissionSuccessful()) { - Serial.println(F("Transmission successful.")); + static bool ledState = 1; + static uint32_t benchmarkCount = 0; + static uint32_t loopStart = millis(); + + // The floodingMeshDelay() method performs all the background operations for the FloodingMesh (via FloodingMesh::performMeshMaintenance()). + // It is recommended to place one of these methods in the beginning of the loop(), unless there is a need to put them elsewhere. + // Among other things, the method cleans up old ESP-NOW log entries (freeing up RAM) and forwards received mesh messages. + // Note that depending on the amount of messages to forward and their length, this method can take tens or even hundreds of milliseconds to complete. + // More intense transmission activity and less frequent calls to performMeshMaintenance will likely cause the method to take longer to complete, so plan accordingly. + // The maintenance methods should not be used inside the meshMessageHandler callback, since they can alter the mesh node state. The framework will alert you during runtime if you make this mistake. + floodingMeshDelay(1); + + // If you wish to transmit only to a single node, try using one of the following methods (requires the node to be within range and know the MAC of the recipient): + // Unencrypted: TransmissionStatusType floodingMesh.getEspnowMeshBackend().attemptTransmission(message, EspnowNetworkInfo(recipientMac)); + // Encrypted (slow): floodingMesh.getEspnowMeshBackend().attemptAutoEncryptingTransmission(message, EspnowNetworkInfo(recipientMac)); + + if (theOne) { + if (millis() - timeOfLastProclamation > 10000) { + uint32_t startTime = millis(); + ledState = ledState ^ bool(benchmarkCount); // Make other nodes' LEDs alternate between on and off once benchmarking begins. + + // Note: The maximum length of an unencrypted broadcast message is given by floodingMesh.maxUnencryptedMessageLength(). It is around 670 bytes by default. + floodingMesh.broadcast(String(floodingMesh.metadataDelimiter()) + String(ledState) + theOneMac + F(" is The One.")); + Serial.println(String(F("Proclamation broadcast done in ")) + String(millis() - startTime) + F(" ms.")); + + timeOfLastProclamation = millis(); + floodingMeshDelay(20); } - // Another way to check how attemptTransmission worked out - if (ESP8266WiFiMesh::latestTransmissionOutcomes.empty()) { - Serial.println(F("No mesh AP found.")); - } else { - for (TransmissionResult &transmissionResult : ESP8266WiFiMesh::latestTransmissionOutcomes) { - if (transmissionResult.transmissionStatus == TS_TRANSMISSION_FAILED) { - Serial.println(String(F("Transmission failed to mesh AP ")) + transmissionResult.SSID); - } else if (transmissionResult.transmissionStatus == TS_CONNECTION_FAILED) { - Serial.println(String(F("Connection failed to mesh AP ")) + transmissionResult.SSID); - } else if (transmissionResult.transmissionStatus == TS_TRANSMISSION_COMPLETE) { - // No need to do anything, transmission was successful. - } else { - Serial.println(String(F("Invalid transmission status for ")) + transmissionResult.SSID + String(F("!"))); - assert(F("Invalid transmission status returned from responseHandler!") && false); - } - } + if (millis() - loopStart > 23000) { // Start benchmarking the mesh once three proclamations have been made + uint32_t startTime = millis(); + floodingMesh.broadcast(String(benchmarkCount++) + String(floodingMesh.metadataDelimiter()) + F(": Not a spoon in sight.")); + Serial.println(String(F("Benchmark broadcast done in ")) + String(millis() - startTime) + F(" ms.")); + floodingMeshDelay(20); } - Serial.println(); - } else { - /* Accept any incoming connections */ - meshNode.acceptRequest(); } } diff --git a/libraries/ESP8266WiFiMesh/examples/HelloTcpIp/HelloTcpIp.ino b/libraries/ESP8266WiFiMesh/examples/HelloTcpIp/HelloTcpIp.ino new file mode 100644 index 0000000000..f59ce2a753 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/examples/HelloTcpIp/HelloTcpIp.ino @@ -0,0 +1,220 @@ +#define ESP8266WIFIMESH_DISABLE_COMPATIBILITY // Excludes redundant compatibility code. TODO: Should be used for new code until the compatibility code is removed with release 3.0.0 of the Arduino core. + +#include +#include +#include +#include +#include + +namespace TypeCast = MeshTypeConversionFunctions; + +/** + NOTE: Although we could define the strings below as normal String variables, + here we are using PROGMEM combined with the FPSTR() macro (and also just the F() macro further down in the file). + The reason is that this approach will place the strings in flash memory which will help save RAM during program execution. + Reading strings from flash will be slower than reading them from RAM, + but this will be a negligible difference when printing them to Serial. + + More on F(), FPSTR() and PROGMEM: + https://github.com/esp8266/Arduino/issues/1143 + https://arduino-esp8266.readthedocs.io/en/latest/PROGMEM.html +*/ +constexpr char exampleMeshName[] PROGMEM = "MeshNode_"; +constexpr char exampleWiFiPassword[] PROGMEM = "ChangeThisWiFiPassword_TODO"; // Note: " is an illegal character. The password has to be min 8 and max 64 characters long, otherwise an AP which uses it will not be found during scans. + +unsigned int requestNumber = 0; +unsigned int responseNumber = 0; + +String manageRequest(const String &request, MeshBackendBase &meshInstance); +TransmissionStatusType manageResponse(const String &response, MeshBackendBase &meshInstance); +void networkFilter(int numberOfNetworks, MeshBackendBase &meshInstance); + +/* Create the mesh node object */ +TcpIpMeshBackend tcpIpNode = TcpIpMeshBackend(manageRequest, manageResponse, networkFilter, FPSTR(exampleWiFiPassword), FPSTR(exampleMeshName), TypeCast::uint64ToString(ESP.getChipId()), true); + +/** + Callback for when other nodes send you a request + + @param request The request string received from another node in the mesh + @param meshInstance The MeshBackendBase instance that called the function. + @return The string to send back to the other node. For ESP-NOW, return an empty string ("") if no response should be sent. +*/ +String manageRequest(const String &request, MeshBackendBase &meshInstance) { + // To get the actual class of the polymorphic meshInstance, do as follows (meshBackendCast replaces dynamic_cast since RTTI is disabled) + if (EspnowMeshBackend *espnowInstance = TypeCast::meshBackendCast(&meshInstance)) { + String transmissionEncrypted = espnowInstance->receivedEncryptedTransmission() ? F(", Encrypted transmission") : F(", Unencrypted transmission"); + Serial.print(String(F("ESP-NOW (")) + espnowInstance->getSenderMac() + transmissionEncrypted + F("): ")); + } else if (TcpIpMeshBackend *tcpIpInstance = TypeCast::meshBackendCast(&meshInstance)) { + (void)tcpIpInstance; // This is useful to remove a "unused parameter" compiler warning. Does nothing else. + Serial.print(F("TCP/IP: ")); + } else { + Serial.print(F("UNKNOWN!: ")); + } + + /* Print out received message */ + // Only show first 100 characters because printing a large String takes a lot of time, which is a bad thing for a callback function. + // If you need to print the whole String it is better to store it and print it in the loop() later. + // Note that request.substring will not work as expected if the String contains null values as data. + Serial.print(F("Request received: ")); + Serial.println(request.substring(0, 100)); + + /* return a string to send back */ + return (String(F("Hello world response #")) + String(responseNumber++) + F(" from ") + meshInstance.getMeshName() + meshInstance.getNodeID() + F(" with AP MAC ") + WiFi.softAPmacAddress() + String('.')); +} + +/** + Callback for when you get a response from other nodes + + @param response The response string received from another node in the mesh + @param meshInstance The MeshBackendBase instance that called the function. + @return The status code resulting from the response, as an int +*/ +TransmissionStatusType manageResponse(const String &response, MeshBackendBase &meshInstance) { + TransmissionStatusType statusCode = TransmissionStatusType::TRANSMISSION_COMPLETE; + + // To get the actual class of the polymorphic meshInstance, do as follows (meshBackendCast replaces dynamic_cast since RTTI is disabled) + if (EspnowMeshBackend *espnowInstance = TypeCast::meshBackendCast(&meshInstance)) { + String transmissionEncrypted = espnowInstance->receivedEncryptedTransmission() ? F(", Encrypted transmission") : F(", Unencrypted transmission"); + Serial.print(String(F("ESP-NOW (")) + espnowInstance->getSenderMac() + transmissionEncrypted + F("): ")); + } else if (TcpIpMeshBackend *tcpIpInstance = TypeCast::meshBackendCast(&meshInstance)) { + Serial.print(F("TCP/IP: ")); + + // Getting the sent message like this will work as long as ONLY(!) TCP/IP is used. + // With TCP/IP the response will follow immediately after the request, so the stored message will not have changed. + // With ESP-NOW there is no guarantee when or if a response will show up, it can happen before or after the stored message is changed. + // So for ESP-NOW, adding unique identifiers in the response and request is required to associate a response with a request. + Serial.print(F("Request sent: ")); + Serial.println(tcpIpInstance->getCurrentMessage().substring(0, 100)); + } else { + Serial.print(F("UNKNOWN!: ")); + } + + /* Print out received message */ + // Only show first 100 characters because printing a large String takes a lot of time, which is a bad thing for a callback function. + // If you need to print the whole String it is better to store it and print it in the loop() later. + // Note that response.substring will not work as expected if the String contains null values as data. + Serial.print(F("Response received: ")); + Serial.println(response.substring(0, 100)); + + return statusCode; +} + +/** + Callback used to decide which networks to connect to once a WiFi scan has been completed. + + @param numberOfNetworks The number of networks found in the WiFi scan. + @param meshInstance The MeshBackendBase instance that called the function. +*/ +void networkFilter(int numberOfNetworks, MeshBackendBase &meshInstance) { + // Note that the network index of a given node may change whenever a new scan is done. + for (int networkIndex = 0; networkIndex < numberOfNetworks; ++networkIndex) { + String currentSSID = WiFi.SSID(networkIndex); + int meshNameIndex = currentSSID.indexOf(meshInstance.getMeshName()); + + /* Connect to any _suitable_ APs which contain meshInstance.getMeshName() */ + if (meshNameIndex >= 0) { + uint64_t targetNodeID = TypeCast::stringToUint64(currentSSID.substring(meshNameIndex + meshInstance.getMeshName().length())); + + if (targetNodeID < TypeCast::stringToUint64(meshInstance.getNodeID())) { + if (EspnowMeshBackend *espnowInstance = TypeCast::meshBackendCast(&meshInstance)) { + espnowInstance->connectionQueue().emplace_back(networkIndex); + } else if (TcpIpMeshBackend *tcpIpInstance = TypeCast::meshBackendCast(&meshInstance)) { + tcpIpInstance->connectionQueue().emplace_back(networkIndex); + } else { + Serial.println(F("Invalid mesh backend!")); + } + } + } + } +} + +/** + Once passed to the setTransmissionOutcomesUpdateHook method of the TCP/IP backend, + this function will be called after each update of the latestTransmissionOutcomes vector during attemptTransmission. + (which happens after each individual transmission has finished) + + Example use cases is modifying getMessage() between transmissions, or aborting attemptTransmission before all nodes in the connectionQueue have been contacted. + + @param meshInstance The MeshBackendBase instance that called the function. + + @return True if attemptTransmission should continue with the next entry in the connectionQueue. False if attemptTransmission should stop. +*/ +bool exampleTransmissionOutcomesUpdateHook(MeshBackendBase &meshInstance) { + // The default hook only returns true and does nothing else. + + if (TcpIpMeshBackend *tcpIpInstance = TypeCast::meshBackendCast(&meshInstance)) { + if (tcpIpInstance->latestTransmissionOutcomes().back().transmissionStatus() == TransmissionStatusType::TRANSMISSION_COMPLETE) { + // Our last request got a response, so time to create a new request. + meshInstance.setMessage(String(F("Hello world request #")) + String(++requestNumber) + F(" from ") + meshInstance.getMeshName() + meshInstance.getNodeID() + String('.')); + } + } else { + Serial.println(F("Invalid mesh backend!")); + } + + return true; +} + +void setup() { + // Prevents the flash memory from being worn out, see: https://github.com/esp8266/Arduino/issues/1054 . + // This will however delay node WiFi start-up by about 700 ms. The delay is 900 ms if we otherwise would have stored the WiFi network we want to connect to. + WiFi.persistent(false); + + Serial.begin(115200); + + Serial.println(); + Serial.println(); + + Serial.println(F("Note that this library can use static IP:s for the nodes to speed up connection times.\n" + "Use the setStaticIP method as shown in this example to enable this.\n" + "Ensure that nodes connecting to the same AP have distinct static IP:s.\n" + "Also, remember to change the default mesh network password!\n\n")); + + Serial.println(F("Setting up mesh node...")); + + /* Initialise the mesh node */ + tcpIpNode.begin(); + tcpIpNode.activateAP(); // Each AP requires a separate server port. + tcpIpNode.setStaticIP(IPAddress(192, 168, 4, 22)); // Activate static IP mode to speed up connection times. + + // Storing our message in the TcpIpMeshBackend instance is not required, but can be useful for organizing code, especially when using many TcpIpMeshBackend instances. + // Note that calling the multi-recipient tcpIpNode.attemptTransmission will replace the stored message with whatever message is transmitted. + tcpIpNode.setMessage(String(F("Hello world request #")) + String(requestNumber) + F(" from ") + tcpIpNode.getMeshName() + tcpIpNode.getNodeID() + String('.')); + + tcpIpNode.setTransmissionOutcomesUpdateHook(exampleTransmissionOutcomesUpdateHook); +} + +int32_t timeOfLastScan = -10000; +void loop() { + if (millis() - timeOfLastScan > 3000 // Give other nodes some time to connect between data transfers. + || (WiFi.status() != WL_CONNECTED && millis() - timeOfLastScan > 2000)) { // Scan for networks with two second intervals when not already connected. + + // attemptTransmission(message, scan, scanAllWiFiChannels, concludingDisconnect, initialDisconnect = false) + tcpIpNode.attemptTransmission(tcpIpNode.getMessage(), true, false, false); + timeOfLastScan = millis(); + + // One way to check how attemptTransmission worked out + if (tcpIpNode.latestTransmissionSuccessful()) { Serial.println(F("Transmission successful.")); } + + // Another way to check how attemptTransmission worked out + if (tcpIpNode.latestTransmissionOutcomes().empty()) { + Serial.println(F("No mesh AP found.")); + } else { + for (TransmissionOutcome &transmissionOutcome : tcpIpNode.latestTransmissionOutcomes()) { + if (transmissionOutcome.transmissionStatus() == TransmissionStatusType::TRANSMISSION_FAILED) { + Serial.println(String(F("Transmission failed to mesh AP ")) + transmissionOutcome.SSID()); + } else if (transmissionOutcome.transmissionStatus() == TransmissionStatusType::CONNECTION_FAILED) { + Serial.println(String(F("Connection failed to mesh AP ")) + transmissionOutcome.SSID()); + } else if (transmissionOutcome.transmissionStatus() == TransmissionStatusType::TRANSMISSION_COMPLETE) { + // No need to do anything, transmission was successful. + } else { + Serial.println(String(F("Invalid transmission status for ")) + transmissionOutcome.SSID() + String('!')); + assert(F("Invalid transmission status returned from responseHandler!") && false); + } + } + } + Serial.println(); + } else { + /* Accept any incoming connections */ + tcpIpNode.acceptRequests(); + } +} diff --git a/libraries/ESP8266WiFiMesh/keywords.txt b/libraries/ESP8266WiFiMesh/keywords.txt index 2ea9f96aad..db8d7cd3be 100644 --- a/libraries/ESP8266WiFiMesh/keywords.txt +++ b/libraries/ESP8266WiFiMesh/keywords.txt @@ -12,51 +12,96 @@ ESP8266WiFiMesh KEYWORD3 # Datatypes (KEYWORD1) ####################################### -ESP8266WiFiMesh KEYWORD1 -NetworkInfo KEYWORD1 -TransmissionResult KEYWORD1 -transmission_status_t KEYWORD1 +MeshBackendBase KEYWORD1 +MeshBackendType KEYWORD1 +requestHandlerType KEYWORD1 +responseHandlerType KEYWORD1 +networkFilterType KEYWORD1 +transmissionOutcomesUpdateHookType KEYWORD1 + +TcpIpMeshBackend KEYWORD1 + +EspnowMeshBackend KEYWORD1 +broadcastFilterType KEYWORD1 +ConnectionType KEYWORD1 +EncryptedConnectionStatus KEYWORD1 +EncryptedConnectionRemovalOutcome KEYWORD1 +responseTransmittedHookType KEYWORD1 + +FloodingMesh KEYWORD1 +messageHandlerType KEYWORD1 + +TransmissionOutcome KEYWORD1 +TransmissionStatusType KEYWORD1 + +NetworkInfoBase KEYWORD1 +TcpIpNetworkInfo KEYWORD1 +EspnowNetworkInfo KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) ####################################### -connectionQueue KEYWORD2 -latestTransmissionOutcomes KEYWORD2 -latestTransmissionSuccessful KEYWORD2 +# MeshBackendBase begin KEYWORD2 activateAP KEYWORD2 deactivateAP KEYWORD2 +deactivateControlledAP KEYWORD2 restartAP KEYWORD2 getAPController KEYWORD2 isAPController KEYWORD2 setWiFiChannel KEYWORD2 getWiFiChannel KEYWORD2 +setSSID KEYWORD2 +getSSID KEYWORD2 +setSSIDPrefix KEYWORD2 +getSSIDPrefix KEYWORD2 +setSSIDRoot KEYWORD2 +getSSIDRoot KEYWORD2 +setSSIDSuffix KEYWORD2 +getSSIDSuffix KEYWORD2 setMeshName KEYWORD2 getMeshName KEYWORD2 setNodeID KEYWORD2 getNodeID KEYWORD2 -setSSID KEYWORD2 -getSSID KEYWORD2 +setMeshPassword KEYWORD2 +getMeshPassword KEYWORD2 setMessage KEYWORD2 getMessage KEYWORD2 attemptTransmission KEYWORD2 -acceptRequest KEYWORD2 -setStaticIP KEYWORD2 -getStaticIP KEYWORD2 -disableStaticIP->KEYWORD2 -uint64ToString KEYWORD2 -stringToUint64 KEYWORD2 setRequestHandler KEYWORD2 getRequestHandler KEYWORD2 setResponseHandler KEYWORD2 getResponseHandler KEYWORD2 setNetworkFilter KEYWORD2 getNetworkFilter KEYWORD2 +setTransmissionOutcomesUpdateHook KEYWORD2 +getTransmissionOutcomesUpdateHook KEYWORD2 setScanHidden KEYWORD2 getScanHidden KEYWORD2 setAPHidden KEYWORD2 getAPHidden KEYWORD2 +setVerboseModeState KEYWORD2 +verboseMode KEYWORD2 +verboseModePrint KEYWORD2 +setPrintWarnings KEYWORD2 +printWarnings KEYWORD2 +warningPrint KEYWORD2 +getClassType KEYWORD2 +printAPInfo KEYWORD2 + +# TcpIpMeshBackend +connectionQueue KEYWORD2 +constConnectionQueue KEYWORD2 +latestTransmissionOutcomes KEYWORD2 +latestTransmissionSuccessful KEYWORD2 +acceptRequests KEYWORD2 +getCurrentMessage KEYWORD2 +setStaticIP KEYWORD2 +getStaticIP KEYWORD2 +disableStaticIP->KEYWORD2 +setServerPort KEYWORD2 +getServerPort KEYWORD2 setMaxAPStations KEYWORD2 getMaxAPStations KEYWORD2 setConnectionAttemptTimeout KEYWORD2 @@ -66,10 +111,152 @@ getStationModeTimeout KEYWORD2 setAPModeTimeout KEYWORD2 getAPModeTimeout KEYWORD2 +# EspnowMeshBackend +espnowDelay KEYWORD2 +performEspnowMaintenance KEYWORD2 +criticalHeapLevel KEYWORD2 +setCriticalHeapLevelBuffer KEYWORD2 +criticalHeapLevelBuffer KEYWORD2 +deactivateEspnow KEYWORD2 +attemptAutoEncryptingTransmission KEYWORD2 +broadcast KEYWORD2 +setBroadcastTransmissionRedundancy KEYWORD2 +getBroadcastTransmissionRedundancy KEYWORD2 +setEspnowRequestManager KEYWORD2 +getEspnowRequestManager KEYWORD2 +isEspnowRequestManager KEYWORD2 +setLogEntryLifetimeMs KEYWORD2 +logEntryLifetimeMs KEYWORD2 +setBroadcastResponseTimeoutMs KEYWORD2 +broadcastResponseTimeoutMs KEYWORD2 +setEspnowEncryptedConnectionKey KEYWORD2 +getEspnowEncryptedConnectionKey KEYWORD2 +setEspnowEncryptionKok KEYWORD2 +getEspnowEncryptionKok KEYWORD2 +setEspnowHashKey KEYWORD2 +getEspnowHashKey KEYWORD2 +setUseEncryptedMessages KEYWORD2 +useEncryptedMessages KEYWORD2 +setEspnowMessageEncryptionKey KEYWORD2 +getEspnowMessageEncryptionKey KEYWORD2 +getMaxMessageBytesPerTransmission KEYWORD2 +setMaxTransmissionsPerMessage KEYWORD2 +getMaxTransmissionsPerMessage KEYWORD2 +getMaxMessageLength KEYWORD2 +staticVerboseMode KEYWORD2 +staticVerboseModePrint KEYWORD2 +getScheduledResponseMessage KEYWORD2 +getScheduledResponseRecipient KEYWORD2 +numberOfScheduledResponses KEYWORD2 +clearAllScheduledResponses KEYWORD2 +deleteScheduledResponsesByRecipient KEYWORD2 +setEspnowTransmissionTimeout KEYWORD2 +getEspnowTransmissionTimeout KEYWORD2 +setEspnowRetransmissionInterval KEYWORD2 +getEspnowRetransmissionInterval KEYWORD2 +setEncryptionRequestTimeout KEYWORD2 +getEncryptionRequestTimeout KEYWORD2 +setAutoEncryptionDuration KEYWORD2 +getAutoEncryptionDuration KEYWORD2 +setBroadcastFilter KEYWORD2 +getBroadcastFilter KEYWORD2 +setResponseTransmittedHook KEYWORD2 +getResponseTransmittedHook KEYWORD2 +getSenderMac KEYWORD2 +getSenderAPMac KEYWORD2 +receivedEncryptedTransmission KEYWORD2 +addUnencryptedConnection KEYWORD2 +addEncryptedConnection KEYWORD2 +addTemporaryEncryptedConnection KEYWORD2 +requestEncryptedConnection KEYWORD2 +requestTemporaryEncryptedConnection KEYWORD2 +requestFlexibleTemporaryEncryptedConnection KEYWORD2 +removeEncryptedConnection KEYWORD2 +requestEncryptedConnectionRemoval KEYWORD2 +setAcceptsUnverifiedRequests KEYWORD2 +acceptsUnverifiedRequests KEYWORD2 +setEncryptedConnectionsSoftLimit KEYWORD2 +encryptedConnectionsSoftLimit KEYWORD2 +numberOfEncryptedConnections KEYWORD2 +getEncryptedMac KEYWORD2 +serializeUnencryptedConnection KEYWORD2 +serializeEncryptedConnection KEYWORD2 +serializeEncryptedConnection KEYWORD2 +getConnectionInfo KEYWORD2 +getTransmissionFailRate KEYWORD2 +resetTransmissionFailRate KEYWORD2 + +# FloodingMesh +floodingMeshDelay KEYWORD2 +performMeshMaintenance KEYWORD2 +performMeshInstanceMaintenance KEYWORD2 +serializeMeshState KEYWORD2 +setBroadcastReceptionRedundancy KEYWORD2 +getBroadcastReceptionRedundancy KEYWORD2 +encryptedBroadcast KEYWORD2 +clearMessageLogs KEYWORD2 +clearForwardingBacklog KEYWORD2 +setMessageHandler KEYWORD2 +getMessageHandler KEYWORD2 +getOriginMac KEYWORD2 +setMessageLogSize KEYWORD2 +messageLogSize KEYWORD2 +maxUnencryptedMessageLength KEYWORD2 +maxEncryptedMessageLength KEYWORD2 +setMetadataDelimiter KEYWORD2 +metadataDelimiter KEYWORD2 +getEspnowMeshBackend KEYWORD2 +getEspnowMeshBackendConst KEYWORD2 +restoreDefaultRequestHandler KEYWORD2 +restoreDefaultResponseHandler KEYWORD2 +restoreDefaultNetworkFilter KEYWORD2 +restoreDefaultBroadcastFilter KEYWORD2 +restoreDefaultTransmissionOutcomesUpdateHook KEYWORD2 +restoreDefaultResponseTransmittedHook KEYWORD2 + +# NetworkInfoBase +setBSSID KEYWORD2 +getBSSID KEYWORD2 +setWifiChannel KEYWORD2 +wifiChannel KEYWORD2 +setEncryptionType KEYWORD2 +setRSSI KEYWORD2 +setIsHidden KEYWORD2 + +# TransmissionOutcome +setTransmissionStatus KEYWORD2 +transmissionStatus KEYWORD2 + +# TypeConversionFunctions +uint64ToString KEYWORD2 +stringToUint64 KEYWORD2 +uint8ArrayToHexString KEYWORD2 +hexStringToUint8Array KEYWORD2 +uint8ArrayToMultiString KEYWORD2 +bufferedUint8ArrayToMultiString KEYWORD2 +macToString KEYWORD2 +stringToMac KEYWORD2 +macToUint64 KEYWORD2 +uint64ToMac KEYWORD2 +uint64ToUint8Array KEYWORD2 +uint8ArrayToUint64 KEYWORD2 +meshBackendCast KEYWORD2 + +# UtilityFunctions +macEqual KEYWORD2 +randomUint64 KEYWORD2 +getMapValue KEYWORD2 + ####################################### # Constants (LITERAL1) ####################################### emptyIP LITERAL1 + NETWORK_INFO_DEFAULT_INT LITERAL1 -WIFI_MESH_EMPTY_STRING LITERAL1 +defaultBSSID LITERAL1 +defaultEncryptionType LITERAL1 +defaultIsHidden LITERAL1 +defaultSSID LITERAL1 +defaultWifiChannel LITERAL1 +defaultRSSI LITERAL1 diff --git a/libraries/ESP8266WiFiMesh/library.properties b/libraries/ESP8266WiFiMesh/library.properties index ddfea96a58..2ff47b0ead 100644 --- a/libraries/ESP8266WiFiMesh/library.properties +++ b/libraries/ESP8266WiFiMesh/library.properties @@ -1,6 +1,6 @@ name=ESP8266WiFiMesh -version=2.1 -author=Julian Fell +version=2.2 +author=Julian Fell, Anders Löfgren maintainer=Anders Löfgren sentence=Mesh network library paragraph=The library sets up a Mesh Node which acts as a router, creating a Mesh Network with other nodes. diff --git a/libraries/ESP8266WiFiMesh/src/CompatibilityLayer.cpp b/libraries/ESP8266WiFiMesh/src/CompatibilityLayer.cpp index fd926a9351..537b58d061 100644 --- a/libraries/ESP8266WiFiMesh/src/CompatibilityLayer.cpp +++ b/libraries/ESP8266WiFiMesh/src/CompatibilityLayer.cpp @@ -27,9 +27,9 @@ /******************************************************************************************** * NOTE! * -* All method signatures in this file are deprecated and will be removed in core version 2.5.0. +* All method signatures in this file are deprecated and will be removed in core version 3.0.0. * If you are still using these methods, please consider migrating to the new API shown in -* the ESP8266WiFiMesh.h source file. +* the EspnowMeshBackend.h or TcpIpMeshBackend.h source files. * * TODO: delete this file. ********************************************************************************************/ @@ -84,7 +84,7 @@ bool ESP8266WiFiMesh::waitForClient(WiFiClient &currClient, int maxWait) * and pass that to the user-supplied handler. * * @message The string to send to the node. - * @returns: True if the exchange was a succes, false otherwise. + * @returns: True if the exchange was a success, false otherwise. * */ // DEPRECATED! @@ -178,4 +178,4 @@ template void ESP8266WiFiMesh::attemptScan(char (&message)[Size]) { attemptScanKernel(message); -} \ No newline at end of file +} diff --git a/libraries/ESP8266WiFiMesh/src/ConditionalPrinter.cpp b/libraries/ESP8266WiFiMesh/src/ConditionalPrinter.cpp new file mode 100644 index 0000000000..15b08195b8 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/ConditionalPrinter.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2020 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#include "TypeConversionFunctions.h" +#include "MeshBackendBase.h" +#include "EspnowMeshBackend.h" + +namespace +{ + bool _staticVerboseMode = false; + bool _printWarnings = true; +} + +void ConditionalPrinter::setVerboseModeState(const bool enabled) {_verboseMode = enabled;} +bool ConditionalPrinter::verboseMode() const {return _verboseMode;} + +void ConditionalPrinter::verboseModePrint(const String &stringToPrint, const bool newline) const +{ + if(verboseMode()) + { + if(newline) + Serial.println(stringToPrint); + else + Serial.print(stringToPrint); + } +} + +void ConditionalPrinter::setStaticVerboseModeState(const bool enabled) {_staticVerboseMode = enabled;}; +bool ConditionalPrinter::staticVerboseMode() {return _staticVerboseMode;} + +void ConditionalPrinter::staticVerboseModePrint(const String &stringToPrint, const bool newline) +{ + if(staticVerboseMode()) + { + if(newline) + Serial.println(stringToPrint); + else + Serial.print(stringToPrint); + } +} + +void ConditionalPrinter::setPrintWarnings(const bool printEnabled) {_printWarnings = printEnabled;} +bool ConditionalPrinter::printWarnings() {return _printWarnings;} + +void ConditionalPrinter::warningPrint(const String &stringToPrint, const bool newline) +{ + if(printWarnings()) + { + if(newline) + Serial.println(stringToPrint); + else + Serial.print(stringToPrint); + } +} diff --git a/libraries/ESP8266WiFiMesh/src/ConditionalPrinter.h b/libraries/ESP8266WiFiMesh/src/ConditionalPrinter.h new file mode 100644 index 0000000000..bf66c6652d --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/ConditionalPrinter.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2020 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#ifndef __CONDITIONALPRINTER_H__ +#define __CONDITIONALPRINTER_H__ + +class ConditionalPrinter +{ + +public: + + /** + * Set whether the normal events occurring in the library will be printed to Serial or not. + * + * @param enabled If true, library Serial prints are activated. + */ + void setVerboseModeState(const bool enabled); + bool verboseMode() const; + + /** + * Only print stringToPrint if verboseMode() returns true. + * + * @param stringToPrint String to print. + * @param newline If true, will end the print with a newline. True by default. + */ + void verboseModePrint(const String &stringToPrint, const bool newline = true) const; + + /** + * Same as verboseMode(), but used for printing from static functions. + * + * @param enabled If true, the normal events occurring in the library will be printed to Serial. + */ + static void setStaticVerboseModeState(const bool enabled); + static bool staticVerboseMode(); + + /** + * Only print stringToPrint if staticVerboseMode() returns true. + * + * @param stringToPrint String to print. + * @param newline If true, will end the print with a newline. True by default. + */ + static void staticVerboseModePrint(const String &stringToPrint, const bool newline = true); + + /** + * Set whether the warnings occurring in the library will be printed to Serial or not. On by default. + * + * @param printEnabled If true, warning Serial prints from the library are activated. + */ + static void setPrintWarnings(const bool printEnabled); + static bool printWarnings(); + + /** + * Only print stringToPrint if printWarnings() returns true. + * + * @param stringToPrint String to print. + * @param newline If true, will end the print with a newline. True by default. + */ + static void warningPrint(const String &stringToPrint, const bool newline = true); + +private: + + bool _verboseMode = false; + +}; + +#endif diff --git a/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.cpp b/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.cpp index e35cac33b9..81cffe48eb 100644 --- a/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.cpp +++ b/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.cpp @@ -18,6 +18,28 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + + + + + +/******************************************************************************************** +* NOTE! +* +* This class is deprecated and will be removed in core version 3.0.0. +* If you are still using this class, please consider migrating to the new API shown in +* the EspnowMeshBackend.h or TcpIpMeshBackend.h source files. +* +* TODO: delete this file. +********************************************************************************************/ + + + + + + + #include #include #include @@ -26,10 +48,11 @@ #include "ESP8266WiFiMesh.h" #include "TypeConversionFunctions.h" +namespace TypeCast = MeshTypeConversionFunctions; + #define SERVER_IP_ADDR "192.168.4.1" const IPAddress ESP8266WiFiMesh::emptyIP = IPAddress(); -const uint32_t ESP8266WiFiMesh::lwipVersion203Signature[3] {2,0,3}; String ESP8266WiFiMesh::lastSSID; bool ESP8266WiFiMesh::staticIPActivated = false; @@ -51,11 +74,9 @@ ESP8266WiFiMesh::~ESP8266WiFiMesh() ESP8266WiFiMesh::ESP8266WiFiMesh(ESP8266WiFiMesh::requestHandlerType requestHandler, ESP8266WiFiMesh::responseHandlerType responseHandler, ESP8266WiFiMesh::networkFilterType networkFilter, const String &meshPassword, const String &meshName, const String &nodeID, bool verboseMode, uint8 meshWiFiChannel, uint16_t serverPort) - : _server(serverPort), _lwipVersion{0, 0, 0} -{ - storeLwipVersion(); - - updateNetworkNames(meshName, (!nodeID.isEmpty() ? nodeID : uint64ToString(ESP.getChipId()))); + : _server(serverPort) +{ + updateNetworkNames(meshName, (!nodeID.isEmpty() ? nodeID : TypeCast::uint64ToString(ESP.getChipId()))); _requestHandler = requestHandler; _responseHandler = responseHandler; setWiFiChannel(meshWiFiChannel); @@ -99,15 +120,10 @@ void ESP8266WiFiMesh::begin() if(!ESP8266WiFiMesh::getAPController()) // If there is no active AP controller WiFi.mode(WIFI_STA); // WIFI_AP_STA mode automatically sets up an AP, so we can't use that as default. - #ifdef ENABLE_STATIC_IP_OPTIMIZATION - if(atLeastLwipVersion(lwipVersion203Signature)) - { - verboseModePrint(F("lwIP version is at least 2.0.3. Static ip optimizations enabled.\n")); - } - else - { - verboseModePrint(F("lwIP version is less than 2.0.3. Static ip optimizations DISABLED.\n")); - } + #if LWIP_VERSION_MAJOR >= 2 + verboseModePrint(F("lwIP version is at least 2. Static ip optimizations enabled.\n")); + #else + verboseModePrint(F("lwIP version is less than 2. Static ip optimizations DISABLED.\n")); #endif } } @@ -323,7 +339,7 @@ void ESP8266WiFiMesh::fullStop(WiFiClient &currClient) /** * Wait for a WiFiClient to transmit * - * @returns: True if the client is ready, false otherwise. + * @return: True if the client is ready, false otherwise. * */ bool ESP8266WiFiMesh::waitForClientTransmission(WiFiClient &currClient, uint32_t maxWait) @@ -351,7 +367,7 @@ bool ESP8266WiFiMesh::waitForClientTransmission(WiFiClient &currClient, uint32_t * and pass that to the user-supplied responseHandler. * * @param currClient The client to which the message should be transmitted. - * @returns: A status code based on the outcome of the exchange. + * @return: A status code based on the outcome of the exchange. * */ transmission_status_t ESP8266WiFiMesh::exchangeInfo(WiFiClient &currClient) @@ -384,7 +400,7 @@ transmission_status_t ESP8266WiFiMesh::exchangeInfo(WiFiClient &currClient) /** * Handle data transfer process with a connected AP. * - * @returns: A status code based on the outcome of the data transfer attempt. + * @return: A status code based on the outcome of the data transfer attempt. */ transmission_status_t ESP8266WiFiMesh::attemptDataTransfer() { @@ -404,7 +420,7 @@ transmission_status_t ESP8266WiFiMesh::attemptDataTransfer() /** * Helper function that contains the core functionality for the data transfer process with a connected AP. * - * @returns: A status code based on the outcome of the data transfer attempt. + * @return: A status code based on the outcome of the data transfer attempt. */ transmission_status_t ESP8266WiFiMesh::attemptDataTransferKernel() { @@ -448,32 +464,25 @@ void ESP8266WiFiMesh::initiateConnectionToAP(const String &targetSSID, int targe * @param targetSSID The name of the AP the other node has set up. * @param targetChannel The WiFI channel of the AP the other node has set up. * @param targetBSSID The mac address of the AP the other node has set up. - * @returns: A status code based on the outcome of the connection and data transfer process. + * @return: A status code based on the outcome of the connection and data transfer process. * */ transmission_status_t ESP8266WiFiMesh::connectToNode(const String &targetSSID, int targetChannel, uint8_t *targetBSSID) { if(staticIPActivated && !lastSSID.isEmpty() && lastSSID != targetSSID) // So we only do this once per connection, in case there is a performance impact. { - #ifdef ENABLE_STATIC_IP_OPTIMIZATION - if(atLeastLwipVersion(lwipVersion203Signature)) - { - // Can be used with Arduino core for ESP8266 version 2.4.2 or higher with lwIP2 enabled to keep static IP on even during network switches. - WiFiMode_t storedWiFiMode = WiFi.getMode(); - WiFi.mode(WIFI_OFF); - WiFi.mode(storedWiFiMode); - yield(); - } - else - { - // Disable static IP so that we can connect to other servers via DHCP (DHCP is slower but required for connecting to more than one server, it seems (possible bug?)). - disableStaticIP(); - verboseModePrint(F("\nConnecting to a different network. Static IP deactivated to make this possible.")); - } + #if LWIP_VERSION_MAJOR >= 2 + // Can be used with Arduino core for ESP8266 version 2.4.2 or higher with lwIP2 enabled to keep static IP on even during network switches. + WiFiMode_t storedWiFiMode = WiFi.getMode(); + WiFi.mode(WIFI_OFF); + WiFi.mode(storedWiFiMode); + yield(); + #else // Disable static IP so that we can connect to other servers via DHCP (DHCP is slower but required for connecting to more than one server, it seems (possible bug?)). disableStaticIP(); verboseModePrint(F("\nConnecting to a different network. Static IP deactivated to make this possible.")); + #endif } lastSSID = targetSSID; @@ -537,10 +546,9 @@ void ESP8266WiFiMesh::attemptTransmission(const String &message, bool concluding /* Scan for APs */ connectionQueue.clear(); - // If scanAllWiFiChannels is true or Arduino core for ESP8266 version < 2.4.2 scanning will cause the WiFi radio to cycle through all WiFi channels. + // If scanAllWiFiChannels is true scanning will cause the WiFi radio to cycle through all WiFi channels. // This means existing WiFi connections are likely to break or work poorly if done frequently. int n = 0; - #ifdef ENABLE_WIFI_SCAN_OPTIMIZATION if(scanAllWiFiChannels) { n = WiFi.scanNetworks(false, _scanHidden); @@ -550,9 +558,6 @@ void ESP8266WiFiMesh::attemptTransmission(const String &message, bool concluding // Scan function argument overview: scanNetworks(bool async = false, bool show_hidden = false, uint8 channel = 0, uint8* ssid = NULL) n = WiFi.scanNetworks(false, _scanHidden, _meshWiFiChannel); } - #else - n = WiFi.scanNetworks(false, _scanHidden); - #endif _networkFilter(n, *this); // Update the connectionQueue. } @@ -619,7 +624,7 @@ void ESP8266WiFiMesh::acceptRequest() if(_handler != NULL) { while (true) { - _client = _server.available(); + _client = _server.accept(); if (!_client) break; @@ -642,7 +647,7 @@ void ESP8266WiFiMesh::acceptRequest() { //////////////////////////// TODO: REMOVE IN 2.5.0//////////////////////////// while (true) { - WiFiClient _client = _server.available(); + WiFiClient _client = _server.accept(); if (!_client) break; @@ -650,12 +655,12 @@ void ESP8266WiFiMesh::acceptRequest() if (!waitForClientTransmission(_client, _apModeTimeoutMs) || !_client.available()) { continue; } - + /* Read in request and pass it to the supplied requestHandler */ String request = _client.readStringUntil('\r'); yield(); _client.flush(); - + String response = _requestHandler(request, *this); /* Send the response back to the client */ @@ -669,3 +674,15 @@ void ESP8266WiFiMesh::acceptRequest() } } } + + +void ESP8266WiFiMesh::verboseModePrint(const String &stringToPrint, bool newline) +{ + if(_verboseMode) + { + if(newline) + Serial.println(stringToPrint); + else + Serial.print(stringToPrint); + } +} diff --git a/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.h b/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.h index ceca8f0ff4..e070b07b88 100644 --- a/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.h +++ b/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.h @@ -18,6 +18,28 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + + + + + + +/******************************************************************************************** +* NOTE! +* +* This class is deprecated and will be removed in core version 3.0.0. +* If you are still using this class, please consider migrating to the new API shown in +* the EspnowMeshBackend.h or TcpIpMeshBackend.h source files. +* +* TODO: delete this file. +********************************************************************************************/ + + + + + + + #ifndef __WIFIMESH_H__ #define __WIFIMESH_H__ @@ -25,11 +47,9 @@ #include #include #include -#include "NetworkInfo.h" #include "TransmissionResult.h" +#include "NetworkInfo.h" -#define ENABLE_STATIC_IP_OPTIMIZATION // Requires Arduino core for ESP8266 version 2.4.2 or higher and lwIP2 (lwIP can be changed in "Tools" menu of Arduino IDE). -#define ENABLE_WIFI_SCAN_OPTIMIZATION // Requires Arduino core for ESP8266 version 2.4.2 or higher. Scan time should go from about 2100 ms to around 60 ms if channel 1 (standard) is used. const String WIFI_MESH_EMPTY_STRING = ""; @@ -44,8 +64,6 @@ class ESP8266WiFiMesh { uint8 _meshWiFiChannel; bool _verboseMode; WiFiServer _server; - uint32_t _lwipVersion[3]; - static const uint32_t lwipVersion203Signature[3]; String _message = WIFI_MESH_EMPTY_STRING; bool _scanHidden = false; bool _apHidden = false; @@ -56,6 +74,7 @@ class ESP8266WiFiMesh { static String lastSSID; static bool staticIPActivated; + bool useStaticIP; static IPAddress staticIP; static IPAddress gateway; static IPAddress subnetMask; @@ -78,8 +97,6 @@ class ESP8266WiFiMesh { bool waitForClientTransmission(WiFiClient &currClient, uint32_t maxWait); transmission_status_t attemptDataTransfer(); transmission_status_t attemptDataTransferKernel(); - void storeLwipVersion(); - bool atLeastLwipVersion(const uint32_t minLwipVersion[3]); @@ -133,7 +150,7 @@ class ESP8266WiFiMesh { //////////////////////////// TODO: REMOVE IN 2.5.0//////////////////////////// ~ESP8266WiFiMesh(); - + /** * WiFiMesh Constructor method. Creates a WiFi Mesh Node, ready to be initialised. * @@ -159,13 +176,13 @@ class ESP8266WiFiMesh { */ ESP8266WiFiMesh(requestHandlerType requestHandler, responseHandlerType responseHandler, networkFilterType networkFilter, const String &meshPassword, const String &meshName = "MeshNode_", const String &nodeID = WIFI_MESH_EMPTY_STRING, bool verboseMode = false, - uint8 meshWiFiChannel = 1, uint16_t serverPort = 4011); + uint8 meshWiFiChannel = 1, uint16_t serverPort = 4011) __attribute__((deprecated)); /** * A vector that contains the NetworkInfo for each WiFi network to connect to. * The connectionQueue vector is cleared before each new scan and filled via the networkFilter callback function once the scan completes. * WiFi connections will start with connectionQueue[0] and then incrementally proceed to higher vector positions. - * Note that old network indicies often are invalidated whenever a new WiFi network scan occurs. + * Note that old network indices often are invalidated whenever a new WiFi network scan occurs. */ static std::vector connectionQueue; @@ -173,12 +190,12 @@ class ESP8266WiFiMesh { * A vector with the TransmissionResult for each AP to which a transmission was attempted during the latest attemptTransmission call. * The latestTransmissionOutcomes vector is cleared before each new transmission attempt. * Connection attempts are indexed in the same order they were attempted. - * Note that old network indicies often are invalidated whenever a new WiFi network scan occurs. + * Note that old network indices often are invalidated whenever a new WiFi network scan occurs. */ static std::vector latestTransmissionOutcomes; /** - * @returns True if latest transmission was successful (i.e. latestTransmissionOutcomes is not empty and all entries have transmissionStatus TS_TRANSMISSION_COMPLETE). False otherwise. + * @return True if latest transmission was successful (i.e. latestTransmissionOutcomes is not empty and all entries have transmissionStatus TS_TRANSMISSION_COMPLETE). False otherwise. */ static bool latestTransmissionSuccessful(); @@ -201,7 +218,7 @@ class ESP8266WiFiMesh { * If another instance takes control over the AP after the pointer is created, * the created pointer will still point to the old AP instance. * - * @returns A pointer to the ESP8266WiFiMesh instance currently in control of the ESP8266 AP, + * @return A pointer to the ESP8266WiFiMesh instance currently in control of the ESP8266 AP, * or nullptr if there is no active AP controller. */ static ESP8266WiFiMesh * getAPController(); @@ -209,7 +226,7 @@ class ESP8266WiFiMesh { /** * Check if this ESP8266WiFiMesh instance is in control of the ESP8266 AP. * - * @returns True if this ESP8266WiFiMesh instance is in control of the ESP8266 AP. False otherwise. + * @return True if this ESP8266WiFiMesh instance is in control of the ESP8266 AP. False otherwise. */ bool isAPController(); diff --git a/libraries/ESP8266WiFiMesh/src/EncryptedConnectionData.cpp b/libraries/ESP8266WiFiMesh/src/EncryptedConnectionData.cpp new file mode 100644 index 0000000000..03a8a89fcd --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/EncryptedConnectionData.cpp @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#include "EncryptedConnectionData.h" +#include "UtilityFunctions.h" +#include "TypeConversionFunctions.h" +#include "JsonTranslator.h" +#include "MeshCryptoInterface.h" +#include "Serializer.h" + +namespace +{ + using EspnowProtocolInterpreter::hashKeyLength; + namespace TypeCast = MeshTypeConversionFunctions; +} + +EncryptedConnectionData::EncryptedConnectionData(const uint8_t peerStaMac[6], const uint8_t peerApMac[6], const uint64_t peerSessionKey, const uint64_t ownSessionKey, const uint8_t hashKey[hashKeyLength]) + : _peerSessionKey(peerSessionKey), _ownSessionKey(ownSessionKey) +{ + std::copy_n(peerStaMac, 6, _peerStaMac); + std::copy_n(peerApMac, 6, _peerApMac); + std::copy_n(hashKey, hashKeyLength, _hashKey); +} + +EncryptedConnectionData::EncryptedConnectionData(const uint8_t peerStaMac[6], const uint8_t peerApMac[6], const uint64_t peerSessionKey, const uint64_t ownSessionKey, const uint32_t duration, const uint8_t hashKey[hashKeyLength]) + : EncryptedConnectionData(peerStaMac, peerApMac, peerSessionKey, ownSessionKey, hashKey) +{ + setRemainingDuration(duration); +} + +EncryptedConnectionData::EncryptedConnectionData(const EncryptedConnectionData &other) + : _peerSessionKey(other.getPeerSessionKey()), _ownSessionKey(other.getOwnSessionKey()), + _timeTracker(other.temporary() ? new ExpiringTimeTracker(*other.temporary()) : nullptr), + _desync(other.desync()) +{ + other.getPeerStaMac(_peerStaMac); + other.getPeerApMac(_peerApMac); + other.getHashKey(_hashKey); +} + +EncryptedConnectionData & EncryptedConnectionData::operator=(const EncryptedConnectionData &other) +{ + if(this != &other) + { + other.getPeerStaMac(_peerStaMac); + other.getPeerApMac(_peerApMac); + _peerSessionKey = other.getPeerSessionKey(); + _ownSessionKey = other.getOwnSessionKey(); + other.getHashKey(_hashKey); + _desync = other.desync(); + _timeTracker = std::unique_ptr(other.temporary() ? new ExpiringTimeTracker(*other.temporary()) : nullptr); + } + return *this; +} + +uint8_t *EncryptedConnectionData::getEncryptedPeerMac(uint8_t *resultArray) const +{ + return getPeerStaMac(resultArray); +} + +uint8_t *EncryptedConnectionData::getUnencryptedPeerMac(uint8_t *resultArray) const +{ + return getPeerApMac(resultArray); +} + +uint8_t *EncryptedConnectionData::getPeerStaMac(uint8_t *resultArray) const +{ + std::copy_n(_peerStaMac, 6, resultArray); + return resultArray; +} + +uint8_t *EncryptedConnectionData::getPeerApMac(uint8_t *resultArray) const +{ + std::copy_n(_peerApMac, 6, resultArray); + return resultArray; +} + +void EncryptedConnectionData::setPeerApMac(const uint8_t *peerApMac) +{ + std::copy_n(peerApMac, 6, _peerApMac); +} + +bool EncryptedConnectionData::connectedTo(const uint8_t *peerMac) const +{ + if(MeshUtilityFunctions::macEqual(peerMac, _peerStaMac) || MeshUtilityFunctions::macEqual(peerMac, _peerApMac)) + { + return true; + } + + return false; +} + +void EncryptedConnectionData::setHashKey(const uint8_t hashKey[hashKeyLength]) +{ + assert(hashKey != nullptr); + + std::copy_n(hashKey, hashKeyLength, _hashKey); +} + +uint8_t *EncryptedConnectionData::getHashKey(uint8_t *resultArray) const +{ + std::copy_n(_hashKey, hashKeyLength, resultArray); + return resultArray; +} + +void EncryptedConnectionData::setPeerSessionKey(const uint64_t sessionKey) { _peerSessionKey = sessionKey; } +uint64_t EncryptedConnectionData::getPeerSessionKey() const { return _peerSessionKey; } + +void EncryptedConnectionData::setOwnSessionKey(const uint64_t sessionKey) { _ownSessionKey = sessionKey; } +uint64_t EncryptedConnectionData::getOwnSessionKey() const { return _ownSessionKey; } + +uint64_t EncryptedConnectionData::incrementSessionKey(const uint64_t sessionKey, const uint8_t *hashKey, const uint8_t hashKeyLength) +{ + uint8_t inputArray[8] {0}; + uint8_t hmacArray[experimental::crypto::SHA256::NATURAL_LENGTH] {0}; + experimental::crypto::SHA256::hmac(TypeCast::uint64ToUint8Array(sessionKey, inputArray), 8, hashKey, hashKeyLength, hmacArray, experimental::crypto::SHA256::NATURAL_LENGTH); + + /* HMAC truncation should be OK since hmac sha256 is a PRF and we are truncating to the leftmost (MSB) bits. + PRF: https://crypto.stackexchange.com/questions/26410/whats-the-gcm-sha-256-of-a-tls-protocol/26434#26434 + Truncate to leftmost bits: https://tools.ietf.org/html/rfc2104#section-5 */ + uint64_t newLeftmostBits = TypeCast::uint8ArrayToUint64(hmacArray) & EspnowProtocolInterpreter::uint64LeftmostBits; + + if(newLeftmostBits == 0) + newLeftmostBits = ((uint64_t)ESP.random() | (1 << 31)) << 32; // We never want newLeftmostBits == 0 since that would indicate an unencrypted transmission. + + uint64_t newRightmostBits = (uint32_t)(sessionKey + 1); + + return newLeftmostBits | newRightmostBits; +} + +void EncryptedConnectionData::incrementOwnSessionKey() +{ + setOwnSessionKey(incrementSessionKey(getOwnSessionKey(), _hashKey, EspnowProtocolInterpreter::hashKeyLength)); +} + +void EncryptedConnectionData::setDesync(const bool desync) { _desync = desync; } +bool EncryptedConnectionData::desync() const { return _desync; } + +String EncryptedConnectionData::serialize() const +{ + return Serializer:: serializeEncryptedConnection((temporary() ? String(temporary()->remainingDuration()) : emptyString), String(desync()), TypeCast::uint64ToString(getOwnSessionKey()), + TypeCast::uint64ToString(getPeerSessionKey()), TypeCast::macToString(_peerStaMac), TypeCast::macToString(_peerApMac)); +} + +const ExpiringTimeTracker *EncryptedConnectionData::temporary() const +{ + return _timeTracker.get(); +} + +void EncryptedConnectionData::setRemainingDuration(const uint32_t remainingDuration) +{ + if(!_timeTracker) + { + _timeTracker = std::unique_ptr(new ExpiringTimeTracker(remainingDuration)); // TODO: Change to std::make_unique(remainingDuration); once compiler fully supports C++14 + } + else + { + _timeTracker->setRemainingDuration(remainingDuration); + } +} + +void EncryptedConnectionData::removeDuration() +{ + _timeTracker = nullptr; +} diff --git a/libraries/ESP8266WiFiMesh/src/EncryptedConnectionData.h b/libraries/ESP8266WiFiMesh/src/EncryptedConnectionData.h new file mode 100644 index 0000000000..cb9ac95593 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/EncryptedConnectionData.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#ifndef __ESPNOWENCRYPTEDCONNECTIONDATA_H__ +#define __ESPNOWENCRYPTEDCONNECTIONDATA_H__ + +#include "ExpiringTimeTracker.h" +#include "EspnowProtocolInterpreter.h" +#include +#include + +class EncryptedConnectionData { + +public: + + virtual ~EncryptedConnectionData() = default; + + EncryptedConnectionData(const uint8_t peerStaMac[6], const uint8_t peerApMac[6], const uint64_t peerSessionKey, const uint64_t ownSessionKey, + const uint8_t hashKey[EspnowProtocolInterpreter::hashKeyLength]); + EncryptedConnectionData(const uint8_t peerStaMac[6], const uint8_t peerApMac[6], const uint64_t peerSessionKey, const uint64_t ownSessionKey, + const uint32_t duration, const uint8_t hashKey[EspnowProtocolInterpreter::hashKeyLength]); + + EncryptedConnectionData(const EncryptedConnectionData &other); + + EncryptedConnectionData & operator=(const EncryptedConnectionData &other); + + /** + * @param resultArray An uint8_t array with at least size 6. + * + * @return The interface MAC used for communicating with the peer. + */ + uint8_t *getEncryptedPeerMac(uint8_t *resultArray) const; + uint8_t *getUnencryptedPeerMac(uint8_t *resultArray) const; + + // @param resultArray At least size 6. + uint8_t *getPeerStaMac(uint8_t *resultArray) const; + void setPeerStaMac(const uint8_t *peerStaMac) = delete; // A method for setPeerStaMac would sometimes require interacting with the ESP-NOW API to change encrypted connections, so it is not implemented. + uint8_t *getPeerApMac(uint8_t *resultArray) const; + void setPeerApMac(const uint8_t *peerApMac); + + bool connectedTo(const uint8_t *peerMac) const; + + void setHashKey(const uint8_t hashKey[EspnowProtocolInterpreter::hashKeyLength]); + // @param resultArray At least size hashKeyLength. + uint8_t *getHashKey(uint8_t *resultArray) const; + + void setPeerSessionKey(const uint64_t sessionKey); + uint64_t getPeerSessionKey() const; + void setOwnSessionKey(const uint64_t sessionKey); + uint64_t getOwnSessionKey() const; + + static uint64_t incrementSessionKey(const uint64_t sessionKey, const uint8_t *hashKey, const uint8_t hashKeyLength); + void incrementOwnSessionKey(); + + void setDesync(const bool desync); + bool desync() const; + + // Note that the espnowEncryptedConnectionKey, espnowEncryptionKok, espnowHashKey and espnowMessageEncryptionKey are not serialized. + // These will be set to the values of the EspnowMeshBackend instance that is adding the serialized encrypted connection. + String serialize() const; + + const ExpiringTimeTracker *temporary() const; + virtual void setRemainingDuration(const uint32_t remainingDuration); + virtual void removeDuration(); + +private: + + uint8_t _peerStaMac[6] {0}; + uint8_t _peerApMac[6] {0}; + uint64_t _peerSessionKey; + uint64_t _ownSessionKey; + std::unique_ptr _timeTracker = nullptr; + uint8_t _hashKey[EspnowProtocolInterpreter::hashKeyLength] {0}; + bool _desync = false; +}; + +#endif diff --git a/libraries/ESP8266WiFiMesh/src/EncryptedConnectionLog.cpp b/libraries/ESP8266WiFiMesh/src/EncryptedConnectionLog.cpp new file mode 100644 index 0000000000..6b979575c4 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/EncryptedConnectionLog.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#include "EncryptedConnectionLog.h" + +namespace +{ + using EspnowProtocolInterpreter::hashKeyLength; +} + +EncryptedConnectionLog::EncryptedConnectionLog(const uint8_t peerStaMac[6], const uint8_t peerApMac[6], const uint64_t peerSessionKey, const uint64_t ownSessionKey, const uint8_t hashKey[hashKeyLength]) + : EncryptedConnectionData(peerStaMac, peerApMac, peerSessionKey, ownSessionKey, hashKey) +{ } + +EncryptedConnectionLog::EncryptedConnectionLog(const uint8_t peerStaMac[6], const uint8_t peerApMac[6], const uint64_t peerSessionKey, const uint64_t ownSessionKey, const uint32_t duration, const uint8_t hashKey[hashKeyLength]) + : EncryptedConnectionData(peerStaMac, peerApMac, peerSessionKey, ownSessionKey, duration, hashKey) +{ } + +std::unique_ptr EncryptedConnectionLog::_soonestExpiringConnectionTracker = nullptr; + +bool EncryptedConnectionLog::_newRemovalsScheduled = false; + +void EncryptedConnectionLog::setRemainingDuration(const uint32_t remainingDuration) +{ + EncryptedConnectionData::setRemainingDuration(remainingDuration); + + setScheduledForRemoval(false); + + updateSoonestExpiringConnectionTracker(remainingDuration); +} + +void EncryptedConnectionLog::removeDuration() +{ + EncryptedConnectionData::removeDuration(); + setScheduledForRemoval(false); +} + +void EncryptedConnectionLog::scheduleForRemoval() +{ + // When we give the connection 0 remaining duration it will be removed during the next performEspnowMaintenance() call. + // Duration must be changed before setting the scheduledForRemoval flag to true, since the flag is otherwise cleared. + setRemainingDuration(0); + setScheduledForRemoval(true); +} + +void EncryptedConnectionLog::setScheduledForRemoval(const bool scheduledForRemoval) +{ + _scheduledForRemoval = scheduledForRemoval; + + if(scheduledForRemoval) + setNewRemovalsScheduled(true); +} +bool EncryptedConnectionLog::removalScheduled() const { return _scheduledForRemoval; } + +void EncryptedConnectionLog::setNewRemovalsScheduled(const bool newRemovalsScheduled) { _newRemovalsScheduled = newRemovalsScheduled; } +bool EncryptedConnectionLog::newRemovalsScheduled( ) { return _newRemovalsScheduled; } + +const ExpiringTimeTracker *EncryptedConnectionLog::getSoonestExpiringConnectionTracker() +{ + return _soonestExpiringConnectionTracker.get(); +} + +void EncryptedConnectionLog::updateSoonestExpiringConnectionTracker(const uint32_t remainingDuration) +{ + if(!getSoonestExpiringConnectionTracker() || remainingDuration < getSoonestExpiringConnectionTracker()->remainingDuration()) + { + _soonestExpiringConnectionTracker = std::unique_ptr(new ExpiringTimeTracker(remainingDuration)); // TODO: Change to std::make_unique(remainingDuration); once compiler fully supports C++14 + } +} + +void EncryptedConnectionLog::clearSoonestExpiringConnectionTracker() +{ + _soonestExpiringConnectionTracker = nullptr; +} diff --git a/libraries/ESP8266WiFiMesh/src/EncryptedConnectionLog.h b/libraries/ESP8266WiFiMesh/src/EncryptedConnectionLog.h new file mode 100644 index 0000000000..91386c75f1 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/EncryptedConnectionLog.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#ifndef __ESPNOWENCRYPTEDCONNECTIONLOG_H__ +#define __ESPNOWENCRYPTEDCONNECTIONLOG_H__ + +#include "EncryptedConnectionData.h" +#include "EspnowProtocolInterpreter.h" + +class EncryptedConnectionLog : public EncryptedConnectionData { + +public: + + EncryptedConnectionLog(const uint8_t peerStaMac[6], const uint8_t peerApMac[6], const uint64_t peerSessionKey, const uint64_t ownSessionKey, + const uint8_t hashKey[EspnowProtocolInterpreter::hashKeyLength]); + EncryptedConnectionLog(const uint8_t peerStaMac[6], const uint8_t peerApMac[6], const uint64_t peerSessionKey, const uint64_t ownSessionKey, + const uint32_t duration, const uint8_t hashKey[EspnowProtocolInterpreter::hashKeyLength]); + + // Only guaranteed to expire at the latest when the soonestExpiringConnection does. Can expire before the soonestExpiringConnection since it is not updated on connection removal. + // Needs to be a copy to avoid invalidation during operations on temporaryEncryptedConnections. + static std::unique_ptr _soonestExpiringConnectionTracker; + + // Only indicates if at least one removal was scheduled since the flag was last cleared, not if the removal is still scheduled to happen. + // Canceling a removal will not update the flag. + static bool _newRemovalsScheduled; + + // Can be used to set a duration both for temporary and permanent encrypted connections (transforming the latter into a temporary connection in the process). + void setRemainingDuration(const uint32_t remainingDuration) override; + void removeDuration() override; + + void scheduleForRemoval(); + bool removalScheduled() const; + + static void setNewRemovalsScheduled(const bool newRemovalsScheduled); + static bool newRemovalsScheduled(); + + static const ExpiringTimeTracker *getSoonestExpiringConnectionTracker(); + static void updateSoonestExpiringConnectionTracker(const uint32_t remainingDuration); + static void clearSoonestExpiringConnectionTracker(); + +private: + + bool _scheduledForRemoval = false; + void setScheduledForRemoval(const bool scheduledForRemoval); +}; + +#endif diff --git a/libraries/ESP8266WiFiMesh/src/EspnowConnectionManager.cpp b/libraries/ESP8266WiFiMesh/src/EspnowConnectionManager.cpp new file mode 100644 index 0000000000..c0534d269c --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/EspnowConnectionManager.cpp @@ -0,0 +1,585 @@ +/* + Copyright (C) 2020 Anders Löfgren + + 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 +extern "C" { + #include +} + +#include "EspnowConnectionManager.h" +#include "JsonTranslator.h" +#include "MeshCryptoInterface.h" +#include "Serializer.h" +#include "EspnowTransmitter.h" + +namespace +{ + using EspnowProtocolInterpreter::encryptedConnectionKeyLength; + using EspnowProtocolInterpreter::hashKeyLength; + using EspnowProtocolInterpreter::maxEncryptedConnections; + + namespace TypeCast = MeshTypeConversionFunctions; + + std::vector _encryptedConnections = {}; + + uint32_t _unsynchronizedMessageID = 0; + + uint8_t _espnowEncryptionKok[encryptedConnectionKeyLength] = { 0 }; + bool _espnowEncryptionKokSet = false; +} + +EspnowConnectionManager::EspnowConnectionManager(ConditionalPrinter &conditionalPrinterInstance, EspnowDatabase &databaseInstance) + : _conditionalPrinter(conditionalPrinterInstance), _database(databaseInstance) +{ + // Reserve the maximum possible usage early on to prevent heap fragmentation later. + encryptedConnections().reserve(maxEncryptedConnections); +} + +std::vector & EspnowConnectionManager::encryptedConnections() { return _encryptedConnections; } + +uint8_t EspnowConnectionManager::numberOfEncryptedConnections() +{ + return encryptedConnections().size(); +} + +ConnectionType EspnowConnectionManager::getConnectionInfo(uint8_t *peerMac, uint32_t *remainingDuration) +{ + EncryptedConnectionLog *encryptedConnection = nullptr; + + if(peerMac) + encryptedConnection = getEncryptedConnection(peerMac); + + return getConnectionInfoHelper(encryptedConnection, remainingDuration); +} + +ConnectionType EspnowConnectionManager::getConnectionInfo(const uint32_t connectionIndex, uint32_t *remainingDuration, uint8_t *peerMac) +{ + EncryptedConnectionLog *encryptedConnection = nullptr; + + if(connectionIndex < numberOfEncryptedConnections()) + encryptedConnection = &encryptedConnections()[connectionIndex]; + + return getConnectionInfoHelper(encryptedConnection, remainingDuration, peerMac); +} + +EspnowConnectionManager::connectionLogIterator EspnowConnectionManager::connectionLogEndIterator() +{ + return encryptedConnections().end(); +} + +void EspnowConnectionManager::setEspnowEncryptedConnectionKey(const uint8_t espnowEncryptedConnectionKey[encryptedConnectionKeyLength]) +{ + assert(espnowEncryptedConnectionKey != nullptr); + + for(int i = 0; i < encryptedConnectionKeyLength; ++i) + { + _espnowEncryptedConnectionKey[i] = espnowEncryptedConnectionKey[i]; + } +} + +void EspnowConnectionManager::setEspnowEncryptedConnectionKey(const String &espnowEncryptedConnectionKeySeed) +{ + MeshCryptoInterface::initializeKey(_espnowEncryptedConnectionKey, encryptedConnectionKeyLength, espnowEncryptedConnectionKeySeed); +} + +const uint8_t *EspnowConnectionManager::getEspnowEncryptedConnectionKey() const +{ + return _espnowEncryptedConnectionKey; +} + +uint8_t *EspnowConnectionManager::getEspnowEncryptedConnectionKey(uint8_t resultArray[encryptedConnectionKeyLength]) const +{ + std::copy_n(_espnowEncryptedConnectionKey, encryptedConnectionKeyLength, resultArray); + return resultArray; +} + +bool EspnowConnectionManager::initializeEncryptionKok() +{ + // esp_now_set_kok returns 0 on success. + return !(_espnowEncryptionKokSet && esp_now_set_kok(_espnowEncryptionKok, encryptedConnectionKeyLength)); +} + +bool EspnowConnectionManager::setEspnowEncryptionKok(uint8_t espnowEncryptionKok[encryptedConnectionKeyLength]) +{ + if(espnowEncryptionKok == nullptr || esp_now_set_kok(espnowEncryptionKok, encryptedConnectionKeyLength)) // esp_now_set_kok failed if not == 0 + return false; + + for(int i = 0; i < encryptedConnectionKeyLength; ++i) + { + _espnowEncryptionKok[i] = espnowEncryptionKok[i]; + } + + _espnowEncryptionKokSet = true; + + return true; +} + +bool EspnowConnectionManager::setEspnowEncryptionKok(const String &espnowEncryptionKokSeed) +{ + uint8_t espnowEncryptionKok[encryptedConnectionKeyLength] {}; + MeshCryptoInterface::initializeKey(espnowEncryptionKok, encryptedConnectionKeyLength, espnowEncryptionKokSeed); + + return setEspnowEncryptionKok(espnowEncryptionKok); +} + +const uint8_t *EspnowConnectionManager::getEspnowEncryptionKok() +{ + if(_espnowEncryptionKokSet) + return _espnowEncryptionKok; + else + return nullptr; +} + +void EspnowConnectionManager::setEspnowHashKey(const uint8_t espnowHashKey[hashKeyLength]) +{ + assert(espnowHashKey != nullptr); + + for(int i = 0; i < hashKeyLength; ++i) + { + _espnowHashKey[i] = espnowHashKey[i]; + } +} + +void EspnowConnectionManager::setEspnowHashKey(const String &espnowHashKeySeed) +{ + MeshCryptoInterface::initializeKey(_espnowHashKey, hashKeyLength, espnowHashKeySeed); +} + +const uint8_t *EspnowConnectionManager::getEspnowHashKey() const +{ + return _espnowHashKey; +} + +uint8_t *EspnowConnectionManager::getEncryptedMac(const uint8_t *peerMac, uint8_t *resultArray) +{ + if(EncryptedConnectionLog *encryptedConnection = getEncryptedConnection(peerMac)) + { + return encryptedConnection->getEncryptedPeerMac(resultArray); + } + + return nullptr; +} + +EncryptedConnectionLog *EspnowConnectionManager::getEncryptedConnection(const uint8_t *peerMac) +{ + auto connectionIterator = getEncryptedConnectionIterator(peerMac, encryptedConnections()); + if(connectionIterator != encryptedConnections().end()) + { + return &(*connectionIterator); + } + + return nullptr; +} + +EncryptedConnectionLog *EspnowConnectionManager::getEncryptedConnection(const uint32_t connectionIndex) +{ + if(connectionIndex < numberOfEncryptedConnections()) + return &encryptedConnections()[connectionIndex]; + + return nullptr; +} + +EncryptedConnectionLog *EspnowConnectionManager::getTemporaryEncryptedConnection(const uint8_t *peerMac) +{ + connectionLogIterator connectionIterator = connectionLogEndIterator(); + if(getTemporaryEncryptedConnectionIterator(peerMac, connectionIterator)) + { + return &(*connectionIterator); + } + + return nullptr; +} + +template +typename T::iterator EspnowConnectionManager::getEncryptedConnectionIterator(const uint8_t *peerMac, T &connectionContainer) +{ + typename T::iterator connectionIterator = connectionContainer.begin(); + + while(connectionIterator != connectionContainer.end()) + { + if(connectionIterator->connectedTo(peerMac)) + break; + else + ++connectionIterator; + } + + return connectionIterator; +} + +bool EspnowConnectionManager::getEncryptedConnectionIterator(const uint8_t *peerMac, connectionLogIterator &iterator) +{ + connectionLogIterator result = getEncryptedConnectionIterator(peerMac, encryptedConnections()); + + if(result != connectionLogEndIterator()) + { + iterator = result; + return true; + } + + return false; +} + +bool EspnowConnectionManager::getTemporaryEncryptedConnectionIterator(const uint8_t *peerMac, connectionLogIterator &iterator) +{ + connectionLogIterator result = connectionLogEndIterator(); + + if(getEncryptedConnectionIterator(peerMac, result) && result->temporary()) + { + iterator = result; + return true; + } + + return false; +} + +ConnectionType EspnowConnectionManager::getConnectionInfoHelper(const EncryptedConnectionLog *encryptedConnection, uint32_t *remainingDuration, uint8_t *peerMac) +{ + if(!encryptedConnection) + { + return ConnectionType::NO_CONNECTION; + } + + if(peerMac) + encryptedConnection->getEncryptedPeerMac(peerMac); + + if(const ExpiringTimeTracker *timeTracker = encryptedConnection->temporary()) + { + if(remainingDuration) + *remainingDuration = timeTracker->remainingDuration(); + + return ConnectionType::TEMPORARY_CONNECTION; + } + + return ConnectionType::PERMANENT_CONNECTION; +} + + +void EspnowConnectionManager::setEncryptedConnectionsSoftLimit(const uint8_t softLimit) +{ + assert(softLimit <= 6); // Valid values are 0 to 6, but uint8_t is always at least 0. + _encryptedConnectionsSoftLimit = softLimit; +} + +uint8_t EspnowConnectionManager::encryptedConnectionsSoftLimit() const { return _encryptedConnectionsSoftLimit; } + +bool EspnowConnectionManager::addUnencryptedConnection(const String &serializedConnectionState) +{ + return JsonTranslator::getUnsynchronizedMessageID(serializedConnectionState, _unsynchronizedMessageID); +} + +EncryptedConnectionStatus EspnowConnectionManager::addEncryptedConnection(uint8_t *peerStaMac, uint8_t *peerApMac, const uint64_t peerSessionKey, const uint64_t ownSessionKey) +{ + assert(encryptedConnections().size() <= maxEncryptedConnections); // If this is not the case, ESP-NOW is no longer in sync with the library + + uint8_t encryptionKeyArray[encryptedConnectionKeyLength] = { 0 }; + + if(EncryptedConnectionLog *encryptedConnection = getEncryptedConnection(peerStaMac)) + { + // Encrypted connection with MAC already exists, so no need to replace it, just updating is enough. + temporaryEncryptedConnectionToPermanent(peerStaMac); + encryptedConnection->setPeerSessionKey(peerSessionKey); + encryptedConnection->setOwnSessionKey(ownSessionKey); + esp_now_set_peer_key(peerStaMac, getEspnowEncryptedConnectionKey(encryptionKeyArray), encryptedConnectionKeyLength); + encryptedConnection->setHashKey(getEspnowHashKey()); + + return EncryptedConnectionStatus::CONNECTION_ESTABLISHED; + } + + if(encryptedConnections().size() == maxEncryptedConnections) + { + // No capacity for more encrypted connections. + return EncryptedConnectionStatus::MAX_CONNECTIONS_REACHED_SELF; + } + // returns 0 on success: int esp_now_add_peer(u8 *mac_addr, u8 role, u8 channel, u8 *key, u8 key_len) + // Only MAC, encryption key and key length (16) actually matter. The rest is not used by ESP-NOW. + else if(0 == esp_now_add_peer(peerStaMac, ESP_NOW_ROLE_CONTROLLER, _database.getWiFiChannel(), getEspnowEncryptedConnectionKey(encryptionKeyArray), encryptedConnectionKeyLength)) + { + encryptedConnections().emplace_back(peerStaMac, peerApMac, peerSessionKey, ownSessionKey, getEspnowHashKey()); + return EncryptedConnectionStatus::CONNECTION_ESTABLISHED; + } + else + { + return EncryptedConnectionStatus::API_CALL_FAILED; + } +} + +EncryptedConnectionStatus EspnowConnectionManager::addEncryptedConnection(const String &serializedConnectionState, const bool ignoreDuration) +{ + uint32_t duration = 0; + bool desync = false; + uint64_t ownSessionKey = 0; + uint64_t peerSessionKey = 0; + uint8_t peerStaMac[6] = { 0 }; + uint8_t peerApMac[6] = { 0 }; + + if(JsonTranslator::getDesync(serializedConnectionState, desync) + && JsonTranslator::getOwnSessionKey(serializedConnectionState, ownSessionKey) && JsonTranslator::getPeerSessionKey(serializedConnectionState, peerSessionKey) + && JsonTranslator::getPeerStaMac(serializedConnectionState, peerStaMac) && JsonTranslator::getPeerApMac(serializedConnectionState, peerApMac)) + { + EncryptedConnectionStatus result = EncryptedConnectionStatus::API_CALL_FAILED; + + if(!ignoreDuration && JsonTranslator::getDuration(serializedConnectionState, duration)) + { + result = addTemporaryEncryptedConnection(peerStaMac, peerApMac, peerSessionKey, ownSessionKey, duration); + } + else + { + result = addEncryptedConnection(peerStaMac, peerApMac, peerSessionKey, ownSessionKey); + } + + if(result == EncryptedConnectionStatus::CONNECTION_ESTABLISHED) + { + EncryptedConnectionLog *encryptedConnection = getEncryptedConnection(peerStaMac); + encryptedConnection->setDesync(desync); + } + + return result; + } + + return EncryptedConnectionStatus::REQUEST_TRANSMISSION_FAILED; +} + +EncryptedConnectionStatus EspnowConnectionManager::addTemporaryEncryptedConnection(uint8_t *peerStaMac, uint8_t *peerApMac, const uint64_t peerSessionKey, const uint64_t ownSessionKey, const uint32_t duration) +{ + assert(encryptedConnections().size() <= maxEncryptedConnections); // If this is not the case, ESP-NOW is no longer in sync with the library + + uint8_t encryptionKeyArray[encryptedConnectionKeyLength] = { 0 }; + + connectionLogIterator encryptedConnection = connectionLogEndIterator(); + + if(getEncryptedConnectionIterator(peerStaMac, encryptedConnection)) + { + // There is already an encrypted connection to this mac, so no need to replace it, just updating is enough. + encryptedConnection->setPeerSessionKey(peerSessionKey); + encryptedConnection->setOwnSessionKey(ownSessionKey); + esp_now_set_peer_key(peerStaMac, getEspnowEncryptedConnectionKey(encryptionKeyArray), encryptedConnectionKeyLength); + encryptedConnection->setHashKey(getEspnowHashKey()); + + if(encryptedConnection->temporary()) + { + encryptedConnection->setRemainingDuration(duration); + } + + return EncryptedConnectionStatus::CONNECTION_ESTABLISHED; + } + + EncryptedConnectionStatus result = addEncryptedConnection(peerStaMac, peerApMac, peerSessionKey, ownSessionKey); + + if(result == EncryptedConnectionStatus::CONNECTION_ESTABLISHED) + { + if(!getEncryptedConnectionIterator(peerStaMac, encryptedConnection)) + assert(false && String(F("No connection found despite being added in addTemporaryEncryptedConnection."))); + + encryptedConnection->setRemainingDuration(duration); + } + + return result; +} + +EncryptedConnectionStatus EspnowConnectionManager::addTemporaryEncryptedConnection(const String &serializedConnectionState, const uint32_t duration) +{ + bool desync = false; + uint64_t ownSessionKey = 0; + uint64_t peerSessionKey = 0; + uint8_t peerStaMac[6] = { 0 }; + uint8_t peerApMac[6] = { 0 }; + + if(JsonTranslator::getDesync(serializedConnectionState, desync) + && JsonTranslator::getOwnSessionKey(serializedConnectionState, ownSessionKey) && JsonTranslator::getPeerSessionKey(serializedConnectionState, peerSessionKey) + && JsonTranslator::getPeerStaMac(serializedConnectionState, peerStaMac) && JsonTranslator::getPeerApMac(serializedConnectionState, peerApMac)) + { + EncryptedConnectionStatus result = addTemporaryEncryptedConnection(peerStaMac, peerApMac, peerSessionKey, ownSessionKey, duration); + + if(result == EncryptedConnectionStatus::CONNECTION_ESTABLISHED) + { + EncryptedConnectionLog *encryptedConnection = getEncryptedConnection(peerStaMac); + encryptedConnection->setDesync(desync); + } + + return result; + } + + return EncryptedConnectionStatus::REQUEST_TRANSMISSION_FAILED; +} + +EncryptedConnectionRemovalOutcome EspnowConnectionManager::removeEncryptedConnection(const uint8_t *peerMac) +{ + auto connectionIterator = getEncryptedConnectionIterator(peerMac, encryptedConnections()); + if(connectionIterator != encryptedConnections().end()) + { + MutexTracker mutexTracker(EspnowTransmitter::captureEspnowTransmissionMutex()); + if(!mutexTracker.mutexCaptured()) + { + // We should not remove an encrypted connection while there is a transmission in progress, since that may cause encrypted data to be sent unencrypted. + // Thus when a transmission is in progress we just schedule the encrypted connection for removal, so it will be removed during the next updateTemporaryEncryptedConnections() call. + connectionIterator->scheduleForRemoval(); + return EncryptedConnectionRemovalOutcome::REMOVAL_SCHEDULED; + } + else + { + return removeEncryptedConnectionUnprotected(peerMac); + } + } + + // peerMac is already removed + return EncryptedConnectionRemovalOutcome::REMOVAL_SUCCEEDED; +} + + +EncryptedConnectionRemovalOutcome EspnowConnectionManager::removeEncryptedConnectionUnprotected(const uint8_t *peerMac, std::vector::iterator *resultingIterator) +{ + connectionLogIterator connectionIterator = getEncryptedConnectionIterator(peerMac, encryptedConnections()); + return removeEncryptedConnectionUnprotected(connectionIterator, resultingIterator); +} + +EncryptedConnectionRemovalOutcome EspnowConnectionManager::removeEncryptedConnectionUnprotected(connectionLogIterator &connectionIterator, std::vector::iterator *resultingIterator) +{ + assert(encryptedConnections().size() <= maxEncryptedConnections); // If this is not the case, ESP-NOW is no longer in sync with the library + + if(connectionIterator != connectionLogEndIterator()) + { + uint8_t encryptedMac[6] {0}; + connectionIterator->getEncryptedPeerMac(encryptedMac); + ConditionalPrinter::staticVerboseModePrint(String(F("Removing connection ")) + TypeCast::macToString(encryptedMac) + String(F("... ")), false); + bool removalSucceeded = esp_now_del_peer(encryptedMac) == 0; + + if(removalSucceeded) + { + if(resultingIterator != nullptr) + { + *resultingIterator = encryptedConnections().erase(connectionIterator); + } + else + { + encryptedConnections().erase(connectionIterator); + } + ConditionalPrinter::staticVerboseModePrint(String(F("Removal succeeded"))); + + // Not deleting encrypted responses here would cause them to be sent unencrypted, + // exposing the peer session key which can be misused later if the encrypted connection is re-established. + EspnowDatabase::deleteScheduledResponsesByRecipient(encryptedMac, true); + + // Not deleting these entries here may cause issues if the encrypted connection is quickly re-added + // and happens to get the same session keys as before (e.g. requestReceived() could then give false positives). + EspnowDatabase::deleteEntriesByMac(EspnowDatabase::receivedEspnowTransmissions(), encryptedMac, true); + EspnowDatabase::deleteEntriesByMac(EspnowDatabase::sentRequests(), encryptedMac, true); + EspnowDatabase::deleteEntriesByMac(EspnowDatabase::receivedRequests(), encryptedMac, true); + + return EncryptedConnectionRemovalOutcome::REMOVAL_SUCCEEDED; + } + else + { + ConditionalPrinter::staticVerboseModePrint(String(F("Removal failed"))); + return EncryptedConnectionRemovalOutcome::REMOVAL_FAILED; + } + } + + // connection is already removed + return EncryptedConnectionRemovalOutcome::REMOVAL_SUCCEEDED; +} + +bool EspnowConnectionManager::temporaryEncryptedConnectionToPermanent(const uint8_t *peerMac) +{ + if(EncryptedConnectionLog *temporaryConnection = getTemporaryEncryptedConnection(peerMac)) + { + temporaryConnection->removeDuration(); + return true; + } + + return false; +} + +String EspnowConnectionManager::serializeUnencryptedConnection() +{ + return Serializer::serializeUnencryptedConnection(String(_unsynchronizedMessageID)); +} + +String EspnowConnectionManager::serializeEncryptedConnection(const uint8_t *peerMac) +{ + String serializedConnection(emptyString); + + EncryptedConnectionLog *encryptedConnection = nullptr; + + if(peerMac) + encryptedConnection = getEncryptedConnection(peerMac); + + if(encryptedConnection) + serializedConnection = encryptedConnection->serialize(); + + return serializedConnection; +} + +String EspnowConnectionManager::serializeEncryptedConnection(const uint32_t connectionIndex) +{ + String serializedConnection(emptyString); + + if(EncryptedConnectionLog *encryptedConnection = getEncryptedConnection(connectionIndex)) + serializedConnection = encryptedConnection->serialize(); + + return serializedConnection; +} + +void EspnowConnectionManager::handlePostponedRemovals() +{ + MutexTracker mutexTracker(EspnowTransmitter::captureEspnowTransmissionMutex()); + if(!mutexTracker.mutexCaptured()) + { + assert(false && String(F("ERROR! Transmission in progress. Don't call handlePostponedRemovals from callbacks as this may corrupt program state! Aborting."))); + return; + } + + if(EncryptedConnectionLog::newRemovalsScheduled()) + { + updateTemporaryEncryptedConnections(true); + } +} + +void EspnowConnectionManager::updateTemporaryEncryptedConnections(const bool scheduledRemovalOnly) +{ + EncryptedConnectionLog::clearSoonestExpiringConnectionTracker(); + + for(auto connectionIterator = encryptedConnections().begin(); connectionIterator != encryptedConnections().end(); ) + { + if(auto timeTrackerPointer = connectionIterator->temporary()) + { + if(timeTrackerPointer->expired() && (!scheduledRemovalOnly || connectionIterator->removalScheduled())) + { + uint8_t macArray[6] = { 0 }; + removeEncryptedConnectionUnprotected(connectionIterator->getEncryptedPeerMac(macArray), &connectionIterator); + continue; + } + else + { + EncryptedConnectionLog::updateSoonestExpiringConnectionTracker(timeTrackerPointer->remainingDuration()); + } + } + assert(!connectionIterator->removalScheduled()); // timeTracker should always exist and be expired if removal is scheduled. + + ++connectionIterator; + } + + EncryptedConnectionLog::setNewRemovalsScheduled(false); +} + +uint64_t EspnowConnectionManager::generateMessageID(const EncryptedConnectionLog *encryptedConnection) +{ + if(encryptedConnection) + { + return encryptedConnection->getOwnSessionKey(); + } + + return _unsynchronizedMessageID++; +} diff --git a/libraries/ESP8266WiFiMesh/src/EspnowConnectionManager.h b/libraries/ESP8266WiFiMesh/src/EspnowConnectionManager.h new file mode 100644 index 0000000000..7edcfcb930 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/EspnowConnectionManager.h @@ -0,0 +1,152 @@ +/* + Copyright (C) 2020 Anders Löfgren + + 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 __ESPNOWCONNECTIONMANAGER_H__ +#define __ESPNOWCONNECTIONMANAGER_H__ + +#include +#include "ConditionalPrinter.h" +#include "EspnowDatabase.h" +#include "EspnowProtocolInterpreter.h" +#include "EncryptedConnectionLog.h" + +class EspnowMeshBackend; + +enum class ConnectionType +{ + NO_CONNECTION = 0, + TEMPORARY_CONNECTION = 1, + PERMANENT_CONNECTION = 2 +}; + +// A value greater than 0 means that an encrypted connection has been established. +enum class EncryptedConnectionStatus +{ + MAX_CONNECTIONS_REACHED_SELF = -3, + REQUEST_TRANSMISSION_FAILED = -2, + MAX_CONNECTIONS_REACHED_PEER = -1, + API_CALL_FAILED = 0, + CONNECTION_ESTABLISHED = 1, + SOFT_LIMIT_CONNECTION_ESTABLISHED = 2 // Only used if _encryptedConnectionsSoftLimit is less than 6. See the setEncryptedConnectionsSoftLimit method documentation for details. +}; + +enum class EncryptedConnectionRemovalOutcome +{ + REMOVAL_REQUEST_FAILED = -1, + REMOVAL_FAILED = 0, + REMOVAL_SUCCEEDED = 1, + REMOVAL_SCHEDULED = 2 +}; + +class EspnowConnectionManager +{ + +public: + + using connectionLogIterator = std::vector::iterator; + + EspnowConnectionManager(ConditionalPrinter &conditionalPrinterInstance, EspnowDatabase &databaseInstance); + + static std::vector & encryptedConnections(); + + static uint8_t numberOfEncryptedConnections(); + + static ConnectionType getConnectionInfo(uint8_t *peerMac, uint32_t *remainingDuration = nullptr); + static ConnectionType getConnectionInfo(const uint32_t connectionIndex, uint32_t *remainingDuration = nullptr, uint8_t *peerMac = nullptr); + + static connectionLogIterator connectionLogEndIterator(); + + void setEspnowEncryptedConnectionKey(const uint8_t espnowEncryptedConnectionKey[EspnowProtocolInterpreter::encryptedConnectionKeyLength]); + void setEspnowEncryptedConnectionKey(const String &espnowEncryptedConnectionKeySeed); + const uint8_t *getEspnowEncryptedConnectionKey() const; + uint8_t *getEspnowEncryptedConnectionKey(uint8_t resultArray[EspnowProtocolInterpreter::encryptedConnectionKeyLength]) const; + // Returns false if failed to apply the current KoK (default KoK is used if no KoK provided) + static bool initializeEncryptionKok(); + static bool setEspnowEncryptionKok(uint8_t espnowEncryptionKok[EspnowProtocolInterpreter::encryptedConnectionKeyLength]); + static bool setEspnowEncryptionKok(const String &espnowEncryptionKokSeed); + static const uint8_t *getEspnowEncryptionKok(); + void setEspnowHashKey(const uint8_t espnowHashKey[EspnowProtocolInterpreter::hashKeyLength]); + void setEspnowHashKey(const String &espnowHashKeySeed); + const uint8_t *getEspnowHashKey() const; + + static uint8_t *getEncryptedMac(const uint8_t *peerMac, uint8_t *resultArray); + + static EncryptedConnectionLog *getEncryptedConnection(const uint8_t *peerMac); + static EncryptedConnectionLog *getEncryptedConnection(const uint32_t connectionIndex); + static EncryptedConnectionLog *getTemporaryEncryptedConnection(const uint8_t *peerMac); + + //@return iterator to connection in connectionContainer, or connectionContainer.end() if element not found + template + static typename T::iterator getEncryptedConnectionIterator(const uint8_t *peerMac, T &connectionContainer); + static bool getEncryptedConnectionIterator(const uint8_t *peerMac, connectionLogIterator &iterator); + // @return true if an encrypted connection to peerMac is found and the found connection is temporary. Only changes iterator if true is returned. + static bool getTemporaryEncryptedConnectionIterator(const uint8_t *peerMac, connectionLogIterator &iterator); + + void setEncryptedConnectionsSoftLimit(const uint8_t softLimit); + uint8_t encryptedConnectionsSoftLimit() const; + + static bool addUnencryptedConnection(const String &serializedConnectionState); + EncryptedConnectionStatus addEncryptedConnection(uint8_t *peerStaMac, uint8_t *peerApMac, const uint64_t peerSessionKey, const uint64_t ownSessionKey); + EncryptedConnectionStatus addEncryptedConnection(const String &serializedConnectionState, const bool ignoreDuration = false); + EncryptedConnectionStatus addTemporaryEncryptedConnection(uint8_t *peerStaMac, uint8_t *peerApMac, const uint64_t peerSessionKey, const uint64_t ownSessionKey, const uint32_t duration); + EncryptedConnectionStatus addTemporaryEncryptedConnection(const String &serializedConnectionState, const uint32_t duration); + + static EncryptedConnectionRemovalOutcome removeEncryptedConnection(const uint8_t *peerMac); + + // Note that removing an encrypted connection while there are encrypted responses scheduled for transmission to the encrypted peer will cause these encrypted responses to be removed without being sent. + // Also note that removing an encrypted connection while there is encrypted data to be received will make the node unable to decrypt that data (although an ack will still be sent to confirm data reception). + // In other words, it is good to use these methods with care and to make sure that both nodes in an encrypted pair are in a state where it is safe for the encrypted connection to be removed before using them. + // Consider using getScheduledResponseRecipient and similar methods for this preparation. + // Should only be used when there is no transmissions in progress. In practice when _espnowTransmissionMutex is free. + // @param resultingIterator Will be set to the iterator position after the removed element, if an element to remove was found. Otherwise no change will occur. + static EncryptedConnectionRemovalOutcome removeEncryptedConnectionUnprotected(const uint8_t *peerMac, std::vector::iterator *resultingIterator = nullptr); + static EncryptedConnectionRemovalOutcome removeEncryptedConnectionUnprotected(connectionLogIterator &connectionIterator, std::vector::iterator *resultingIterator); + + static bool temporaryEncryptedConnectionToPermanent(const uint8_t *peerMac); + + static String serializeUnencryptedConnection(); + static String serializeEncryptedConnection(const uint8_t *peerMac); + static String serializeEncryptedConnection(const uint32_t connectionIndex); + + static void handlePostponedRemovals(); + + // Should only be used when there is no transmissions in progress, so it is safe to remove encrypted connections. In practice when _espnowTransmissionMutex is free. + // @param scheduledRemovalOnly If true, only deletes encrypted connections where removalScheduled() is true. This means only connections which have been requested for removal will be deleted, + // not other connections which have expired. + static void updateTemporaryEncryptedConnections(const bool scheduledRemovalOnly = false); + + /** + * Generate a new message ID to be used when making a data transmission. The generated ID will be different depending on whether an encrypted connection exists or not. + * + * @param encryptedConnection A pointer to the EncryptedConnectionLog of the encrypted connection. Can be set to nullptr if the connection is unecrypted. + * @return The generated message ID. + */ + static uint64_t generateMessageID(const EncryptedConnectionLog *encryptedConnection); + +private: + + ConditionalPrinter & _conditionalPrinter; + EspnowDatabase & _database; + + static ConnectionType getConnectionInfoHelper(const EncryptedConnectionLog *encryptedConnection, uint32_t *remainingDuration, uint8_t *peerMac = nullptr); + + uint8_t _espnowEncryptedConnectionKey[EspnowProtocolInterpreter::encryptedConnectionKeyLength] {0}; + uint8_t _espnowHashKey[EspnowProtocolInterpreter::hashKeyLength] {0}; + + uint8_t _encryptedConnectionsSoftLimit = 6; +}; + +#endif diff --git a/libraries/ESP8266WiFiMesh/src/EspnowDatabase.cpp b/libraries/ESP8266WiFiMesh/src/EspnowDatabase.cpp new file mode 100644 index 0000000000..d718726787 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/EspnowDatabase.cpp @@ -0,0 +1,383 @@ +/* + Copyright (C) 2020 Anders Löfgren + + 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 "EspnowDatabase.h" +#include "EspnowMeshBackend.h" +#include "UtilityFunctions.h" + +namespace +{ + namespace TypeCast = MeshTypeConversionFunctions; + + // _logEntryLifetimeMs is based on someone storing 40 responses of 750 bytes each = 30 000 bytes (roughly full memory), + // which takes 2000 ms + some margin to send. Also, we want to avoid old entries taking up memory if they cannot be sent, + // so storage duration should not be too long. + uint32_t _logEntryLifetimeMs = 2500; + uint32_t _broadcastResponseTimeoutMs = 1000; // This is shorter than _logEntryLifetimeMs to preserve RAM since broadcasts are not deleted from sentRequests until they expire. + ExpiringTimeTracker _logClearingCooldown(500); + + uint32_t _encryptionRequestTimeoutMs = 300; + + uint32_t _criticalHeapLevel = 6000; // In bytes + uint32_t _criticalHeapLevelBuffer = 6000; // In bytes + + using EspnowProtocolInterpreter::macAndType_td; + using EspnowProtocolInterpreter::messageID_td; + using EspnowProtocolInterpreter::peerMac_td; + + std::list _responsesToSend = {}; + std::list _peerRequestConfirmationsToSend = {}; + + std::map, MessageData> _receivedEspnowTransmissions = {}; + std::map, RequestData> _sentRequests = {}; + std::map, TimeTracker> _receivedRequests = {}; + + std::shared_ptr _espnowConnectionQueueMutex = std::make_shared(false); + std::shared_ptr _responsesToSendMutex = std::make_shared(false); +} + +std::vector EspnowDatabase::_connectionQueue = {}; +std::vector EspnowDatabase::_latestTransmissionOutcomes = {}; + +EspnowDatabase::EspnowDatabase(ConditionalPrinter &conditionalPrinterInstance, const uint8 espnowWiFiChannel) : _conditionalPrinter(conditionalPrinterInstance), _espnowWiFiChannel(espnowWiFiChannel) +{ +} + +std::vector & EspnowDatabase::connectionQueue() +{ + MutexTracker connectionQueueMutexTracker(_espnowConnectionQueueMutex); + if(!connectionQueueMutexTracker.mutexCaptured()) + { + assert(false && String(F("ERROR! connectionQueue locked. Don't call connectionQueue() from callbacks other than NetworkFilter as this may corrupt program state!"))); + } + + return _connectionQueue; +} + +const std::vector & EspnowDatabase::constConnectionQueue() +{ + return _connectionQueue; +} + +std::vector & EspnowDatabase::latestTransmissionOutcomes() +{ + return _latestTransmissionOutcomes; +} + +void EspnowDatabase::setCriticalHeapLevelBuffer(const uint32_t bufferInBytes) +{ + _criticalHeapLevelBuffer = bufferInBytes; +} + +uint32_t EspnowDatabase::criticalHeapLevelBuffer() +{ + return _criticalHeapLevelBuffer; +} + +uint32_t EspnowDatabase::criticalHeapLevel() +{ + return _criticalHeapLevel; +} + +template +void EspnowDatabase::deleteExpiredLogEntries(std::map, T> &logEntries, const uint32_t maxEntryLifetimeMs) +{ + for(typename std::map, T>::iterator entryIterator = logEntries.begin(); + entryIterator != logEntries.end(); ) + { + if(entryIterator->second.getTimeTracker().timeSinceCreation() > maxEntryLifetimeMs) + { + entryIterator = logEntries.erase(entryIterator); + } + else + ++entryIterator; + } +} + +template +void EspnowDatabase::deleteExpiredLogEntries(std::map, TimeTracker> &logEntries, const uint32_t maxEntryLifetimeMs) +{ + for(typename std::map, TimeTracker>::iterator entryIterator = logEntries.begin(); + entryIterator != logEntries.end(); ) + { + if(entryIterator->second.timeSinceCreation() > maxEntryLifetimeMs) + { + entryIterator = logEntries.erase(entryIterator); + } + else + ++entryIterator; + } +} + +void EspnowDatabase::deleteExpiredLogEntries(std::map, RequestData> &logEntries, const uint32_t requestLifetimeMs, const uint32_t broadcastLifetimeMs) +{ + for(typename std::map, RequestData>::iterator entryIterator = logEntries.begin(); + entryIterator != logEntries.end(); ) + { + bool broadcast = entryIterator->first.first == EspnowProtocolInterpreter::uint64BroadcastMac; + uint32_t timeSinceCreation = entryIterator->second.getTimeTracker().timeSinceCreation(); + + if((!broadcast && timeSinceCreation > requestLifetimeMs) + || (broadcast && timeSinceCreation > broadcastLifetimeMs)) + { + entryIterator = logEntries.erase(entryIterator); + } + else + ++entryIterator; + } +} + +template +void EspnowDatabase::deleteExpiredLogEntries(std::list &logEntries, const uint32_t maxEntryLifetimeMs) +{ + for(typename std::list::iterator entryIterator = logEntries.begin(); + entryIterator != logEntries.end(); ) + { + if(entryIterator->getTimeTracker().timeSinceCreation() > maxEntryLifetimeMs) + { + entryIterator = logEntries.erase(entryIterator); + } + else + ++entryIterator; + } +} + +template <> +void EspnowDatabase::deleteExpiredLogEntries(std::list &logEntries, const uint32_t maxEntryLifetimeMs) +{ + for(typename std::list::iterator entryIterator = logEntries.begin(); + entryIterator != logEntries.end(); ) + { + auto timeTrackerPointer = entryIterator->temporary(); + if(timeTrackerPointer && timeTrackerPointer->elapsedTime() > maxEntryLifetimeMs) + { + entryIterator = logEntries.erase(entryIterator); + } + else + ++entryIterator; + } +} + +void EspnowDatabase::setLogEntryLifetimeMs(const uint32_t logEntryLifetimeMs) +{ + _logEntryLifetimeMs = logEntryLifetimeMs; +} +uint32_t EspnowDatabase::logEntryLifetimeMs() { return _logEntryLifetimeMs; } + +void EspnowDatabase::setBroadcastResponseTimeoutMs(const uint32_t broadcastResponseTimeoutMs) +{ + _broadcastResponseTimeoutMs = broadcastResponseTimeoutMs; +} +uint32_t EspnowDatabase::broadcastResponseTimeoutMs() { return _broadcastResponseTimeoutMs; } + +String EspnowDatabase::getScheduledResponseMessage(const uint32_t responseIndex) +{ + return getScheduledResponse(responseIndex)->getMessage(); +} + +const uint8_t *EspnowDatabase::getScheduledResponseRecipient(const uint32_t responseIndex) +{ + return getScheduledResponse(responseIndex)->getRecipientMac(); +} + +uint32_t EspnowDatabase::numberOfScheduledResponses() {return responsesToSend().size();} + +void EspnowDatabase::clearAllScheduledResponses() +{ + MutexTracker responsesToSendMutexTracker(_responsesToSendMutex); + if(!responsesToSendMutexTracker.mutexCaptured()) + { + assert(false && String(F("ERROR! responsesToSend locked. Don't call clearAllScheduledResponses from callbacks as this may corrupt program state! Aborting."))); + } + + responsesToSend().clear(); +} + +void EspnowDatabase::deleteScheduledResponsesByRecipient(const uint8_t *recipientMac, const bool encryptedOnly) +{ + MutexTracker responsesToSendMutexTracker(_responsesToSendMutex); + if(!responsesToSendMutexTracker.mutexCaptured()) + { + assert(false && String(F("ERROR! responsesToSend locked. Don't call deleteScheduledResponsesByRecipient from callbacks as this may corrupt program state! Aborting."))); + } + + for(auto responseIterator = responsesToSend().begin(); responseIterator != responsesToSend().end(); ) + { + if(MeshUtilityFunctions::macEqual(responseIterator->getRecipientMac(), recipientMac) && + (!encryptedOnly || EspnowProtocolInterpreter::usesEncryption(responseIterator->getRequestID()))) + { + responseIterator = responsesToSend().erase(responseIterator); + } + else + ++responseIterator; + } +} + +void EspnowDatabase::setEncryptionRequestTimeout(const uint32_t timeoutMs) +{ + _encryptionRequestTimeoutMs = timeoutMs; +} +uint32_t EspnowDatabase::getEncryptionRequestTimeout() {return _encryptionRequestTimeoutMs;} + +void EspnowDatabase::setAutoEncryptionDuration(const uint32_t duration) +{ + _autoEncryptionDuration = duration; +} +uint32_t EspnowDatabase::getAutoEncryptionDuration() const {return _autoEncryptionDuration;} + +String EspnowDatabase::getSenderMac() const {return TypeCast::macToString(_senderMac);} +uint8_t *EspnowDatabase::getSenderMac(uint8_t *macArray) const +{ + std::copy_n(_senderMac, 6, macArray); + return macArray; +} + +String EspnowDatabase::getSenderAPMac() const {return TypeCast::macToString(_senderAPMac);} +uint8_t *EspnowDatabase::getSenderAPMac(uint8_t *macArray) const +{ + std::copy_n(_senderAPMac, 6, macArray); + return macArray; +} + +void EspnowDatabase::clearOldLogEntries(bool forced) +{ + // Clearing all old log entries at the same time should help minimize heap fragmentation. + + // uint32_t startTime = millis(); + + if(!forced && !_logClearingCooldown) // Clearing too frequently will cause a lot of unnecessary container iterations. + { + return; + } + + _logClearingCooldown.reset(); + + deleteExpiredLogEntries(receivedEspnowTransmissions(), logEntryLifetimeMs()); + deleteExpiredLogEntries(receivedRequests(), logEntryLifetimeMs()); // Just needs to be long enough to not accept repeated transmissions by mistake. + deleteExpiredLogEntries(sentRequests(), logEntryLifetimeMs(), broadcastResponseTimeoutMs()); + deleteExpiredLogEntries(responsesToSend(), logEntryLifetimeMs()); + deleteExpiredLogEntries(peerRequestConfirmationsToSend(), getEncryptionRequestTimeout()); +} + +std::list::const_iterator EspnowDatabase::getScheduledResponse(const uint32_t responseIndex) +{ + assert(responseIndex < numberOfScheduledResponses()); + + bool startFromBeginning = responseIndex < numberOfScheduledResponses()/2; + auto responseIterator = startFromBeginning ? responsesToSend().cbegin() : responsesToSend().cend(); + uint32_t stepsToTarget = startFromBeginning ? responseIndex : numberOfScheduledResponses() - responseIndex; // cend is one element beyond the last + + while(stepsToTarget > 0) + { + startFromBeginning ? ++responseIterator : --responseIterator; + --stepsToTarget; + } + + return responseIterator; +} + +void EspnowDatabase::setSenderMac(const uint8_t *macArray) +{ + std::copy_n(macArray, 6, _senderMac); +} + +void EspnowDatabase::setSenderAPMac(const uint8_t *macArray) +{ + std::copy_n(macArray, 6, _senderAPMac); +} + +void EspnowDatabase::setWiFiChannel(const uint8 newWiFiChannel) +{ + wifi_country_t wifiCountry; + wifi_get_country(&wifiCountry); // Note: Should return 0 on success and -1 on failure, but always seems to return 1. Possibly broken API. Channels 1 to 13 are the default limits. + assert(wifiCountry.schan <= newWiFiChannel && newWiFiChannel <= wifiCountry.schan + wifiCountry.nchan - 1); + + _espnowWiFiChannel = newWiFiChannel; +} + +uint8 EspnowDatabase::getWiFiChannel() const +{ + return _espnowWiFiChannel; +} + +bool EspnowDatabase::requestReceived(const uint64_t requestMac, const uint64_t requestID) +{ + return receivedRequests().count(std::make_pair(requestMac, requestID)); +} + +MutexTracker EspnowDatabase::captureEspnowConnectionQueueMutex() +{ + // Syntax like this will move the resulting value into its new position (similar to NRVO): https://stackoverflow.com/a/11540204 + return MutexTracker(_espnowConnectionQueueMutex); +} + +MutexTracker EspnowDatabase::captureEspnowConnectionQueueMutex(const std::function destructorHook) { return MutexTracker(_espnowConnectionQueueMutex, destructorHook); } + +MutexTracker EspnowDatabase::captureResponsesToSendMutex(){ return MutexTracker(_responsesToSendMutex); } + +MutexTracker EspnowDatabase::captureResponsesToSendMutex(const std::function destructorHook) { return MutexTracker(_responsesToSendMutex, destructorHook); } + +void EspnowDatabase::storeSentRequest(const uint64_t targetBSSID, const uint64_t messageID, const RequestData &requestData) +{ + sentRequests().insert(std::make_pair(std::make_pair(targetBSSID, messageID), requestData)); +} + +void EspnowDatabase::storeReceivedRequest(const uint64_t senderBSSID, const uint64_t messageID, const TimeTracker &timeTracker) +{ + receivedRequests().insert(std::make_pair(std::make_pair(senderBSSID, messageID), timeTracker)); +} + +EspnowMeshBackend *EspnowDatabase::getOwnerOfSentRequest(const uint64_t requestMac, const uint64_t requestID) +{ + std::map, RequestData>::iterator sentRequest = sentRequests().find(std::make_pair(requestMac, requestID)); + + if(sentRequest != sentRequests().end()) + { + return &sentRequest->second.getMeshInstance(); + } + + return nullptr; +} + +size_t EspnowDatabase::deleteSentRequest(const uint64_t requestMac, const uint64_t requestID) +{ + return sentRequests().erase(std::make_pair(requestMac, requestID)); +} + +size_t EspnowDatabase::deleteSentRequestsByOwner(const EspnowMeshBackend *instancePointer) +{ + size_t numberDeleted = 0; + + for(std::map, RequestData>::iterator requestIterator = sentRequests().begin(); + requestIterator != sentRequests().end(); ) + { + if(&requestIterator->second.getMeshInstance() == instancePointer) // If instance at instancePointer made the request + { + requestIterator = sentRequests().erase(requestIterator); + numberDeleted++; + } + else + ++requestIterator; + } + + return numberDeleted; +} + +std::list & EspnowDatabase::responsesToSend() { return _responsesToSend; } +std::list & EspnowDatabase::peerRequestConfirmationsToSend() { return _peerRequestConfirmationsToSend; } +std::map, MessageData> & EspnowDatabase::receivedEspnowTransmissions() { return _receivedEspnowTransmissions; } +std::map, RequestData> & EspnowDatabase::sentRequests() { return _sentRequests; } +std::map, TimeTracker> & EspnowDatabase::receivedRequests() { return _receivedRequests; } diff --git a/libraries/ESP8266WiFiMesh/src/EspnowDatabase.h b/libraries/ESP8266WiFiMesh/src/EspnowDatabase.h new file mode 100644 index 0000000000..34ec34f924 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/EspnowDatabase.h @@ -0,0 +1,223 @@ +/* + Copyright (C) 2020 Anders Löfgren + + 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 __ESPNOWDATABASE_H__ +#define __ESPNOWDATABASE_H__ + +#include +#include "EspnowNetworkInfo.h" +#include "TransmissionOutcome.h" +#include "ResponseData.h" +#include "RequestData.h" +#include "EspnowProtocolInterpreter.h" +#include +#include +#include "MessageData.h" +#include "MutexTracker.h" +#include "PeerRequestLog.h" +#include "ConditionalPrinter.h" +#include "TypeConversionFunctions.h" + +class EspnowMeshBackend; + +class EspnowDatabase +{ + +public: + + EspnowDatabase(ConditionalPrinter &conditionalPrinterInstance, const uint8 espnowWiFiChannel); + + static std::vector & connectionQueue(); + static const std::vector & constConnectionQueue(); + static std::vector & latestTransmissionOutcomes(); + static uint32_t criticalHeapLevel(); + static void setCriticalHeapLevelBuffer(const uint32_t bufferInBytes); + static uint32_t criticalHeapLevelBuffer(); + static void setLogEntryLifetimeMs(const uint32_t logEntryLifetimeMs); + static uint32_t logEntryLifetimeMs(); + static void setBroadcastResponseTimeoutMs(const uint32_t broadcastResponseTimeoutMs); + static uint32_t broadcastResponseTimeoutMs(); + static String getScheduledResponseMessage(const uint32_t responseIndex); + static const uint8_t *getScheduledResponseRecipient(const uint32_t responseIndex); + static uint32_t numberOfScheduledResponses(); + static void clearAllScheduledResponses(); + static void deleteScheduledResponsesByRecipient(const uint8_t *recipientMac, const bool encryptedOnly); + static void setEncryptionRequestTimeout(const uint32_t timeoutMs); + static uint32_t getEncryptionRequestTimeout(); + + void setAutoEncryptionDuration(const uint32_t duration); + uint32_t getAutoEncryptionDuration() const; + String getSenderMac() const; + uint8_t *getSenderMac(uint8_t *macArray) const; + String getSenderAPMac() const; + uint8_t *getSenderAPMac(uint8_t *macArray) const; + + using macAndType_td = EspnowProtocolInterpreter::macAndType_td; + using messageID_td = EspnowProtocolInterpreter::messageID_td; + using peerMac_td = EspnowProtocolInterpreter::peerMac_td; + + static size_t deleteSentRequestsByOwner(const EspnowMeshBackend *instancePointer); + static std::list & responsesToSend(); + static std::list & peerRequestConfirmationsToSend(); + static std::map, MessageData> & receivedEspnowTransmissions(); + static std::map, RequestData> & sentRequests(); + static std::map, TimeTracker> & receivedRequests(); + + static bool requestReceived(const uint64_t requestMac, const uint64_t requestID); + + /** + * Will be captured when the connectionQueue should not be modified. + */ + static MutexTracker captureEspnowConnectionQueueMutex(); + static MutexTracker captureEspnowConnectionQueueMutex(const std::function destructorHook); + + /** + * Will be captured when no responsesToSend element should be removed. + */ + static MutexTracker captureResponsesToSendMutex(); + static MutexTracker captureResponsesToSendMutex(const std::function destructorHook); + + static void clearOldLogEntries(bool forced); + + static void storeSentRequest(const uint64_t targetBSSID, const uint64_t messageID, const RequestData &requestData); + static void storeReceivedRequest(const uint64_t senderBSSID, const uint64_t messageID, const TimeTracker &timeTracker); + + /** + * Get a pointer to the EspnowMeshBackend instance that sent a request with the given requestID to the specified mac address. + * + * @return A valid EspnowMeshBackend pointer if a matching entry is found in the EspnowMeshBackend sentRequests container. nullptr otherwise. + */ + static EspnowMeshBackend *getOwnerOfSentRequest(const uint64_t requestMac, const uint64_t requestID); + + /** + * Delete all entries in the sentRequests container where requestMac is noted as having received requestID. + * + * @return The number of entries deleted. + */ + static size_t deleteSentRequest(const uint64_t requestMac, const uint64_t requestID); + + /** + * Set the MAC address considered to be the sender of the most recently received ESP-NOW request, response or broadcast. + * + * @param macArray An uint8_t array which contains the MAC address to store. The method will store the first 6 bytes of the array. + */ + void setSenderMac(const uint8_t *macArray); + + /** + * Set the MAC address considered to be the AP MAC of the sender of the most recently received ESP-NOW request, response or broadcast. + * + * @param macArray An uint8_t array which contains the MAC address to store. The method will store the first 6 bytes of the array. + */ + void setSenderAPMac(const uint8_t *macArray); + + void setWiFiChannel(const uint8 newWiFiChannel); + uint8 getWiFiChannel() const; + + /** + * Remove all entries which target peerMac in the logEntries map. + * Optionally deletes only entries sent/received by encrypted transmissions. + * + * @param logEntries The map to process. + * @param peerMac The MAC address of the peer node. + * @param encryptedOnly If true, only entries sent/received by encrypted transmissions will be deleted. + */ + template + static void deleteEntriesByMac(std::map, T> &logEntries, const uint8_t *peerMac, const bool encryptedOnly) + { + bool macFound = false; + + for(typename std::map, T>::iterator entryIterator = logEntries.begin(); + entryIterator != logEntries.end(); ) + { + if(macAndTypeToUint64Mac(entryIterator->first.first) == MeshTypeConversionFunctions::macToUint64(peerMac)) + { + macFound = true; + + if(!encryptedOnly || EspnowProtocolInterpreter::usesEncryption(entryIterator->first.second)) + { + entryIterator = logEntries.erase(entryIterator); + continue; + } + } + else if(macFound) + { + // Since the map is sorted by MAC, we know here that no more matching MAC will be found. + return; + } + + ++entryIterator; + } + } + + template + static void deleteEntriesByMac(std::map, T> &logEntries, const uint8_t *peerMac, const bool encryptedOnly) + { + bool macFound = false; + + for(typename std::map, T>::iterator entryIterator = logEntries.begin(); + entryIterator != logEntries.end(); ) + { + if(entryIterator->first.first == MeshTypeConversionFunctions::macToUint64(peerMac)) + { + macFound = true; + + if(!encryptedOnly || EspnowProtocolInterpreter::usesEncryption(entryIterator->first.second)) + { + entryIterator = logEntries.erase(entryIterator); + continue; + } + } + else if(macFound) + { + // Since the map is sorted by MAC, we know here that no more matching MAC will be found. + return; + } + + ++entryIterator; + } + } + +protected: + + static std::vector _connectionQueue; + static std::vector _latestTransmissionOutcomes; + + static std::list::const_iterator getScheduledResponse(const uint32_t responseIndex); + +private: + + ConditionalPrinter & _conditionalPrinter; + + uint32_t _autoEncryptionDuration = 50; + + template + static void deleteExpiredLogEntries(std::map, T> &logEntries, const uint32_t maxEntryLifetimeMs); + + template + static void deleteExpiredLogEntries(std::map, TimeTracker> &logEntries, const uint32_t maxEntryLifetimeMs); + + static void deleteExpiredLogEntries(std::map, RequestData> &logEntries, const uint32_t requestLifetimeMs, const uint32_t broadcastLifetimeMs); + + template + static void deleteExpiredLogEntries(std::list &logEntries, const uint32_t maxEntryLifetimeMs); + + uint8_t _senderMac[6] = {0}; + uint8_t _senderAPMac[6] = {0}; + + uint8 _espnowWiFiChannel; +}; + +#endif diff --git a/libraries/ESP8266WiFiMesh/src/EspnowEncryptionBroker.cpp b/libraries/ESP8266WiFiMesh/src/EspnowEncryptionBroker.cpp new file mode 100644 index 0000000000..f0d11070b6 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/EspnowEncryptionBroker.cpp @@ -0,0 +1,721 @@ +/* + Copyright (C) 2020 Anders Löfgren + + 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 +extern "C" { + #include +} + +#include "EspnowEncryptionBroker.h" +#include "EspnowMeshBackend.h" +#include "JsonTranslator.h" +#include "UtilityFunctions.h" +#include "Serializer.h" +#include "MeshCryptoInterface.h" + +namespace +{ + using EspnowProtocolInterpreter::encryptedConnectionKeyLength; + using EspnowProtocolInterpreter::hashKeyLength; + using EspnowProtocolInterpreter::maxEncryptedConnections; + + using connectionLogIterator = EspnowConnectionManager::connectionLogIterator; + + namespace TypeCast = MeshTypeConversionFunctions; + + String _ongoingPeerRequestNonce; + EspnowMeshBackend *_ongoingPeerRequester = nullptr; + EncryptedConnectionStatus _ongoingPeerRequestResult = EncryptedConnectionStatus::MAX_CONNECTIONS_REACHED_SELF; + ExpiringTimeTracker _ongoingPeerRequestEncryptionTimeout([](){ return EspnowDatabase::getEncryptionRequestTimeout(); }); + uint8_t _ongoingPeerRequestMac[6] = {0}; + bool _reciprocalPeerRequestConfirmation = false; +} + +EspnowEncryptionBroker::EspnowEncryptionBroker(ConditionalPrinter &conditionalPrinterInstance, EspnowDatabase &databaseInstance, EspnowConnectionManager &connectionManagerInstance, EspnowTransmitter &transmitterInstance) + : _conditionalPrinter(conditionalPrinterInstance), _database(databaseInstance), _connectionManager(connectionManagerInstance), _transmitter(transmitterInstance) +{ +} + +void EspnowEncryptionBroker::handlePeerRequest(const uint8_t *macaddr, uint8_t *dataArray, const uint8_t len, const uint64_t uint64StationMac, const uint64_t receivedMessageID) +{ + // Pairing process ends when encryptedConnectionVerificationHeader is received, maxConnectionsReachedHeader is sent or timeout is reached. + // Pairing process stages for request receiver: + // Receive: encryptionRequestHeader or temporaryEncryptionRequestHeader. + // Send: maxConnectionsReachedHeader / basicConnectionInfoHeader -> encryptedConnectionInfoHeader or softLimitEncryptedConnectionInfoHeader or maxConnectionsReachedHeader. + // Receive: encryptedConnectionVerificationHeader. + + using namespace EspnowProtocolInterpreter; + + if(!EspnowDatabase::requestReceived(uint64StationMac, receivedMessageID)) + { + EspnowDatabase::storeReceivedRequest(uint64StationMac, receivedMessageID, TimeTracker(millis())); + + bool encryptedCorrectly = synchronizePeerSessionKey(receivedMessageID, macaddr); + String message = getHashKeyLength(dataArray, len); + int32_t messageHeaderEndIndex = message.indexOf(':'); + String messageHeader = message.substring(0, messageHeaderEndIndex + 1); + + if(messageHeader == FPSTR(encryptedConnectionVerificationHeader)) + { + if(encryptedCorrectly) + { + int32_t connectionRequestTypeEndIndex = message.indexOf(':', messageHeaderEndIndex + 1); + String connectionRequestType = message.substring(messageHeaderEndIndex + 1, connectionRequestTypeEndIndex + 1); + connectionLogIterator encryptedConnection = EspnowConnectionManager::connectionLogEndIterator(); + if(!EspnowConnectionManager::getEncryptedConnectionIterator(macaddr, encryptedConnection)) + assert(false && String(F("We must have an encrypted connection if we received an encryptedConnectionVerificationHeader which was encryptedCorrectly."))); + + if(connectionRequestType == FPSTR(encryptionRequestHeader)) + { + EspnowConnectionManager::temporaryEncryptedConnectionToPermanent(macaddr); + } + else if(connectionRequestType == FPSTR(temporaryEncryptionRequestHeader)) + { + if(encryptedConnection->temporary()) // Should not change duration for existing permanent connections. + { + uint32_t connectionDuration = 0; + if(JsonTranslator::getDuration(message, connectionDuration)) + { + encryptedConnection->setRemainingDuration(connectionDuration); + } + } + } + else + { + assert(false && String(F("Unknown P-type verification message received!"))); + } + } + } + else if(messageHeader == FPSTR(encryptionRequestHeader) || messageHeader == FPSTR(temporaryEncryptionRequestHeader)) + { + // If there is a espnowRequestManager, get it + if(EspnowMeshBackend *currentEspnowRequestManager = EspnowMeshBackend::getEspnowRequestManager()) + { + String requestNonce; + + if(JsonTranslator::getNonce(message, requestNonce) && requestNonce.length() >= 12) // The destination MAC address requires 12 characters. + { + uint8_t destinationMac[6] = {0}; + TypeCast::stringToMac(requestNonce, destinationMac); + + uint8_t apMac[6] {0}; + WiFi.softAPmacAddress(apMac); + + bool correctDestination = false; + if(MeshUtilityFunctions::macEqual(destinationMac, apMac)) + { + correctDestination = true; + } + else + { + uint8_t staMac[6] {0}; + WiFi.macAddress(staMac); + + if(MeshUtilityFunctions::macEqual(destinationMac, staMac)) + { + correctDestination = true; + } + } + + uint8_t apMacArray[6] = { 0 }; + if(correctDestination && verifyEncryptionRequestHmac(message, macaddr, getTransmissionMac(dataArray, apMacArray), currentEspnowRequestManager->getEspnowHashKey(), hashKeyLength)) + EspnowDatabase::peerRequestConfirmationsToSend().emplace_back(receivedMessageID, encryptedCorrectly, currentEspnowRequestManager->getMeshPassword(), currentEspnowRequestManager->encryptedConnectionsSoftLimit(), + requestNonce, macaddr, apMacArray, currentEspnowRequestManager->getEspnowHashKey()); + } + } + } + else if(messageHeader == FPSTR(encryptedConnectionRemovalRequestHeader)) + { + if(encryptedCorrectly) + EspnowConnectionManager::removeEncryptedConnection(macaddr); + } + else + { + assert(false && String(F("Unknown P-type message received!"))); + } + } +} + +void EspnowEncryptionBroker::handlePeerRequestConfirmation(uint8_t *macaddr, uint8_t *dataArray, const uint8_t len) +{ + // Pairing process ends when _ongoingPeerRequestNonce == "" or timeout is reached. + // Pairing process stages for request sender: + // Send: encryptionRequestHeader or temporaryEncryptionRequestHeader. + // Receive: maxConnectionsReachedHeader / basicConnectionInfoHeader -> encryptedConnectionInfoHeader or softLimitEncryptedConnectionInfoHeader or maxConnectionsReachedHeader. + // Send: encryptedConnectionVerificationHeader. + + using namespace EspnowProtocolInterpreter; + + if(!_ongoingPeerRequestNonce.isEmpty()) + { + String message = getHashKeyLength(dataArray, len); + String requestNonce; + + if(JsonTranslator::getNonce(message, requestNonce) && requestNonce == _ongoingPeerRequestNonce) + { + int32_t messageHeaderEndIndex = message.indexOf(':'); + String messageHeader = message.substring(0, messageHeaderEndIndex + 1); + String messageBody = message.substring(messageHeaderEndIndex + 1); + uint8_t apMacArray[6] = { 0 }; + getTransmissionMac(dataArray, apMacArray); + + if(messageHeader == FPSTR(basicConnectionInfoHeader)) + { + // encryptedConnectionEstablished(_ongoingPeerRequestResult) means we have already received a basicConnectionInfoHeader + if(!encryptedConnectionEstablished(_ongoingPeerRequestResult) && + verifyEncryptionRequestHmac(message, macaddr, apMacArray, _ongoingPeerRequester->getEspnowHashKey(), hashKeyLength)) + { + _ongoingPeerRequestEncryptionTimeout.reset(); + + connectionLogIterator existingEncryptedConnection = EspnowConnectionManager::connectionLogEndIterator(); + + if(!EspnowConnectionManager::getEncryptedConnectionIterator(macaddr, existingEncryptedConnection)) + { + // Although the newly created session keys are normally never used (they are replaced with synchronized ones later), the session keys must still be randomized to prevent attacks until replaced. + _ongoingPeerRequestResult = _ongoingPeerRequester->addTemporaryEncryptedConnection(macaddr, apMacArray, createSessionKey(), createSessionKey(), EspnowDatabase::getEncryptionRequestTimeout()); + } + else + { + // Encrypted connection already exists + _ongoingPeerRequestResult = EncryptedConnectionStatus::CONNECTION_ESTABLISHED; + + if(auto timeTrackerPointer = existingEncryptedConnection->temporary()) + { + if(timeTrackerPointer->remainingDuration() < EspnowDatabase::getEncryptionRequestTimeout()) // Should only extend duration for existing connections. + { + existingEncryptedConnection->setRemainingDuration(EspnowDatabase::getEncryptionRequestTimeout()); + } + } + } + + if(!encryptedConnectionEstablished(_ongoingPeerRequestResult)) + { + // Adding connection failed, abort ongoing peer request. + _ongoingPeerRequestNonce.clear(); + } + } + } + else if(messageHeader == FPSTR(encryptedConnectionInfoHeader) || messageHeader == FPSTR(softLimitEncryptedConnectionInfoHeader)) + { + String messagePassword; + + if(JsonTranslator::getPassword(messageBody, messagePassword) && messagePassword == _ongoingPeerRequester->getMeshPassword()) + { + // The mesh password is only shared via encrypted messages, so now we know this message is valid since it was encrypted and contained the correct nonce. + + EncryptedConnectionLog *encryptedConnection = EspnowConnectionManager::getEncryptedConnection(macaddr); + uint64_t peerSessionKey = 0; + uint64_t ownSessionKey = 0; + if(encryptedConnection && JsonTranslator::getPeerSessionKey(messageBody, peerSessionKey) && JsonTranslator::getOwnSessionKey(messageBody, ownSessionKey)) + { + encryptedConnection->setPeerSessionKey(peerSessionKey); + encryptedConnection->setOwnSessionKey(ownSessionKey); + + if(messageHeader == FPSTR(encryptedConnectionInfoHeader)) + _ongoingPeerRequestResult = EncryptedConnectionStatus::CONNECTION_ESTABLISHED; + else if(messageHeader == FPSTR(softLimitEncryptedConnectionInfoHeader)) + _ongoingPeerRequestResult = EncryptedConnectionStatus::SOFT_LIMIT_CONNECTION_ESTABLISHED; + else + assert(false && String(F("Unknown _ongoingPeerRequestResult!"))); + } + else + { + _ongoingPeerRequestResult = EncryptedConnectionStatus::REQUEST_TRANSMISSION_FAILED; + } + + _ongoingPeerRequestNonce.clear(); + } + } + else if(messageHeader == FPSTR(maxConnectionsReachedHeader)) + { + if(verifyEncryptionRequestHmac(message, macaddr, apMacArray, _ongoingPeerRequester->getEspnowHashKey(), hashKeyLength)) + { + _ongoingPeerRequestResult = EncryptedConnectionStatus::MAX_CONNECTIONS_REACHED_PEER; + _ongoingPeerRequestNonce.clear(); + } + } + else + { + assert(messageHeader == FPSTR(basicConnectionInfoHeader) || messageHeader == FPSTR(encryptedConnectionInfoHeader) || + messageHeader == FPSTR(softLimitEncryptedConnectionInfoHeader) || messageHeader == FPSTR(maxConnectionsReachedHeader)); + } + } + } +} + +void EspnowEncryptionBroker::sendPeerRequestConfirmations(const ExpiringTimeTracker *estimatedMaxDurationTracker) +{ + uint32_t bufferedCriticalHeapLevel = EspnowDatabase::criticalHeapLevel() + EspnowDatabase::criticalHeapLevelBuffer(); // We preferably want to start clearing the logs a bit before things get critical. + // _ongoingPeerRequestNonce can change during every delay(), but we need to remember the initial value to know from where sendPeerRequestConfirmations was called. + String initialOngoingPeerRequestNonce = _ongoingPeerRequestNonce; + + for(std::list::iterator confirmationsIterator = EspnowDatabase::peerRequestConfirmationsToSend().begin(); confirmationsIterator != EspnowDatabase::peerRequestConfirmationsToSend().end(); ) + { + using namespace EspnowProtocolInterpreter; + + // True if confirmationsIterator contains a peer request received from the same node we are currently sending a peer request to. + bool reciprocalPeerRequest = !initialOngoingPeerRequestNonce.isEmpty() && confirmationsIterator->connectedTo(_ongoingPeerRequestMac); + + auto timeTrackerPointer = confirmationsIterator->temporary(); + assert(timeTrackerPointer); // peerRequestConfirmations should always expire and so should always have a timeTracker + if(timeTrackerPointer->elapsedTime() > EspnowDatabase::getEncryptionRequestTimeout() + || (reciprocalPeerRequest && confirmationsIterator->getPeerRequestNonce() <= initialOngoingPeerRequestNonce)) + { + // The peer request has expired, + // or the peer request comes from the node we are currently making a peer request to ourselves and we are supposed to wait in this event to avoid simultaneous session key transfer. + ++confirmationsIterator; + continue; + } + + uint8_t defaultBSSID[6] {0}; + confirmationsIterator->getEncryptedPeerMac(defaultBSSID); + uint8_t unencryptedBSSID[6] {0}; + confirmationsIterator->getUnencryptedPeerMac(unencryptedBSSID); + uint8_t hashKey[hashKeyLength] {0}; + confirmationsIterator->getHashKey(hashKey); + + EncryptedConnectionLog *existingEncryptedConnection = EspnowConnectionManager::getEncryptedConnection(defaultBSSID); + + // If we receive a non-encrypted request for encrypted connection from a node that already exists as an encrypted peer for us we cannot send a response to the encrypted MAC + // since that transmission will then be encrypted and impossible for the request sender to read. Of course, removing the existing encrypted connection would also work, + // but make it very simple for a third party to disrupt an encrypted connection by just sending random requests for encrypted connection. + bool sendToDefaultBSSID = confirmationsIterator->requestEncrypted() || !existingEncryptedConnection; + + // Note that callbacks can be called during delay time, so it is possible to receive a transmission during espnowSendToNode + // (which may add an element to the peerRequestConfirmationsToSend list). + + if(!existingEncryptedConnection && + ((reciprocalPeerRequest && EspnowConnectionManager::encryptedConnections().size() >= maxEncryptedConnections) || (!reciprocalPeerRequest && reservedEncryptedConnections() >= maxEncryptedConnections))) + { + EspnowTransmitter::espnowSendPeerRequestConfirmationsUnsynchronized(Serializer::createEncryptionRequestHmacMessage(FPSTR(maxConnectionsReachedHeader), + confirmationsIterator->getPeerRequestNonce(), hashKey, hashKeyLength), + defaultBSSID, 'C'); // Generates a new message ID to avoid sending encrypted sessionKeys over unencrypted connections. + + confirmationsIterator = EspnowDatabase::peerRequestConfirmationsToSend().erase(confirmationsIterator); + } + else if(EspnowTransmitter::espnowSendPeerRequestConfirmationsUnsynchronized(Serializer::createEncryptionRequestHmacMessage(FPSTR(basicConnectionInfoHeader), + confirmationsIterator->getPeerRequestNonce(), hashKey, hashKeyLength), + sendToDefaultBSSID ? defaultBSSID : unencryptedBSSID, 'C') // Generates a new message ID to avoid sending encrypted sessionKeys over unencrypted connections. + == TransmissionStatusType::TRANSMISSION_COMPLETE) + { + // Try to add encrypted connection. If connection added send confirmation with encryptedConnection->getOwnSessionKey() as session key and C type message (won't increment key). Then proceed with next request (no need to wait for answer). + if(existingEncryptedConnection) + { + if(auto timeTrackerPointer = existingEncryptedConnection->temporary()) + { + if(EspnowDatabase::getEncryptionRequestTimeout() > timeTrackerPointer->remainingDuration()) + { + existingEncryptedConnection->setRemainingDuration(EspnowDatabase::getEncryptionRequestTimeout()); + } + } + } + else if(EspnowMeshBackend *currentEspnowRequestManager = EspnowMeshBackend::getEspnowRequestManager()) + { + uint8_t staMacArray[6] = { 0 }; + uint8_t apMacArray[6] = { 0 }; + currentEspnowRequestManager->addTemporaryEncryptedConnection(confirmationsIterator->getPeerStaMac(staMacArray), confirmationsIterator->getPeerApMac(apMacArray), + createSessionKey(), createSessionKey(), EspnowDatabase::getEncryptionRequestTimeout()); + existingEncryptedConnection = EspnowConnectionManager::getEncryptedConnection(defaultBSSID); + } + else + { + ConditionalPrinter::warningPrint(String(F("WARNING! Ignoring received encrypted connection request since no EspnowRequestManager is assigned."))); + } + + if(!existingEncryptedConnection) + { + // Send "node full" message + EspnowTransmitter::espnowSendPeerRequestConfirmationsUnsynchronized(Serializer::createEncryptionRequestHmacMessage(FPSTR(maxConnectionsReachedHeader), + confirmationsIterator->getPeerRequestNonce(), hashKey, hashKeyLength), + defaultBSSID, 'C'); // Generates a new message ID to avoid sending encrypted sessionKeys over unencrypted connections. + } + else + { + if(reciprocalPeerRequest) + _reciprocalPeerRequestConfirmation = true; + + delay(5); // Give some time for the peer to add an encrypted connection + + assert(esp_now_is_peer_exist(defaultBSSID) > 0 && String(F("ERROR! Attempting to send content marked as encrypted via unencrypted connection!"))); + + String messageHeader; + + if(existingEncryptedConnection->temporary() && // Should never change permanent connections + ((reciprocalPeerRequest && EspnowConnectionManager::encryptedConnections().size() > confirmationsIterator->getEncryptedConnectionsSoftLimit()) + || (!reciprocalPeerRequest && reservedEncryptedConnections() > confirmationsIterator->getEncryptedConnectionsSoftLimit()))) + { + messageHeader = FPSTR(softLimitEncryptedConnectionInfoHeader); + } + else + { + messageHeader = FPSTR(encryptedConnectionInfoHeader); + } + + // Send password and keys. + // Probably no need to know which connection type to use, that is stored in request node and will be sent over for finalization. + EspnowTransmitter::espnowSendPeerRequestConfirmationsUnsynchronized(Serializer::createEncryptedConnectionInfo(messageHeader, + confirmationsIterator->getPeerRequestNonce(), confirmationsIterator->getAuthenticationPassword(), + existingEncryptedConnection->getOwnSessionKey(), existingEncryptedConnection->getPeerSessionKey()), + defaultBSSID, 'C'); // Generates a new message ID to avoid sending encrypted sessionKeys over unencrypted connections. + } + + confirmationsIterator = EspnowDatabase::peerRequestConfirmationsToSend().erase(confirmationsIterator); + } + else + { + ++confirmationsIterator; + } + + if(ESP.getFreeHeap() <= bufferedCriticalHeapLevel) + { + // Heap is getting very low, which probably means we are receiving a lot of transmissions while trying to transmit responses. + // Clear all old data to try to avoid running out of memory. + ConditionalPrinter::warningPrint("WARNING! Free heap below chosen minimum. Performing emergency log clearing."); + EspnowDatabase::clearOldLogEntries(true); + return; // confirmationsIterator may be invalid now. Also, we should give the main loop a chance to respond to the situation. + } + + if(estimatedMaxDurationTracker && estimatedMaxDurationTracker->expired()) + return; + } +} + +EncryptedConnectionStatus EspnowEncryptionBroker::requestEncryptedConnection(const uint8_t *peerMac, EspnowMeshBackend &espnowInstance) +{ + using namespace std::placeholders; + return requestEncryptedConnectionKernel(peerMac, std::bind(defaultEncryptionRequestBuilder, FPSTR(EspnowProtocolInterpreter::encryptionRequestHeader), 0, _connectionManager.getEspnowHashKey(), _1, _2), espnowInstance); +} + +EncryptedConnectionStatus EspnowEncryptionBroker::requestTemporaryEncryptedConnection(const uint8_t *peerMac, const uint32_t durationMs, EspnowMeshBackend &espnowInstance) +{ + using namespace std::placeholders; + return requestEncryptedConnectionKernel(peerMac, std::bind(defaultEncryptionRequestBuilder, FPSTR(EspnowProtocolInterpreter::temporaryEncryptionRequestHeader), + durationMs, _connectionManager.getEspnowHashKey(), _1, _2), espnowInstance); +} + +EncryptedConnectionStatus EspnowEncryptionBroker::requestFlexibleTemporaryEncryptedConnection(const uint8_t *peerMac, const uint32_t minDurationMs, EspnowMeshBackend &espnowInstance) +{ + using namespace std::placeholders; + return requestEncryptedConnectionKernel(peerMac, std::bind(flexibleEncryptionRequestBuilder, minDurationMs, _connectionManager.getEspnowHashKey(), _1, _2), espnowInstance); +} + +EncryptedConnectionRemovalOutcome EspnowEncryptionBroker::requestEncryptedConnectionRemoval(const uint8_t *peerMac) +{ + using EspnowProtocolInterpreter::encryptedConnectionRemovalRequestHeader; + + assert(EspnowConnectionManager::encryptedConnections().size() <= maxEncryptedConnections); // If this is not the case, ESP-NOW is no longer in sync with the library + + MutexTracker mutexTracker(EspnowTransmitter::captureEspnowTransmissionMutex(EspnowConnectionManager::handlePostponedRemovals)); + if(!mutexTracker.mutexCaptured()) + { + assert(false && String(F("ERROR! Transmission in progress. Don't call requestEncryptedConnectionRemoval from callbacks as this may corrupt program state! Aborting."))); + return EncryptedConnectionRemovalOutcome::REMOVAL_REQUEST_FAILED; + } + + if(EncryptedConnectionLog *encryptedConnection = EspnowConnectionManager::getEncryptedConnection(peerMac)) + { + if(EspnowTransmitter::espnowSendToNode(FPSTR(encryptedConnectionRemovalRequestHeader), peerMac, 'P') == TransmissionStatusType::TRANSMISSION_COMPLETE) + { + return EspnowConnectionManager::removeEncryptedConnectionUnprotected(peerMac); + } + else + { + if(encryptedConnection->removalScheduled()) + return EncryptedConnectionRemovalOutcome::REMOVAL_SUCCEEDED; // Removal will be completed by mutex destructorHook. + else + return EncryptedConnectionRemovalOutcome::REMOVAL_REQUEST_FAILED; + } + } + + // peerMac is already removed + return EncryptedConnectionRemovalOutcome::REMOVAL_SUCCEEDED; +} + +bool EspnowEncryptionBroker::encryptedConnectionEstablished(const EncryptedConnectionStatus connectionStatus) +{ + return static_cast(connectionStatus) > 0; +} + +void EspnowEncryptionBroker::setReceivedEncryptedTransmission(const bool receivedEncryptedTransmission) { _receivedEncryptedTransmission = receivedEncryptedTransmission; } +bool EspnowEncryptionBroker::receivedEncryptedTransmission() const {return _receivedEncryptedTransmission;} + +bool EspnowEncryptionBroker::verifyEncryptionRequestHmac(const String &encryptionRequestHmacMessage, const uint8_t *requesterStaMac, const uint8_t *requesterApMac, + const uint8_t *hashKey, const uint8_t hashKeyLength) +{ + using MeshCryptoInterface::verifyMeshHmac; + using namespace JsonTranslator; + + String hmac; + if(getHmac(encryptionRequestHmacMessage, hmac)) + { + int32_t hmacStartIndex = encryptionRequestHmacMessage.indexOf(String('"') + FPSTR(jsonHmac) + F("\":")); + if(hmacStartIndex < 0) + return false; + + if(hmac.length() == 2*experimental::crypto::SHA256::NATURAL_LENGTH // We know that each HMAC byte should become 2 String characters due to uint8ArrayToHexString. + && verifyMeshHmac(TypeCast::macToString(requesterStaMac) + TypeCast::macToString(requesterApMac) + encryptionRequestHmacMessage.substring(0, hmacStartIndex), hmac, hashKey, hashKeyLength)) + { + return true; + } + } + + return false; +} + +bool EspnowEncryptionBroker::verifyPeerSessionKey(const uint64_t sessionKey, const uint8_t *peerMac, const char messageType) +{ + if(EncryptedConnectionLog *encryptedConnection = EspnowConnectionManager::getEncryptedConnection(peerMac)) + { + return verifyPeerSessionKey(sessionKey, *encryptedConnection, TypeCast::macToUint64(peerMac), messageType); + } + + return false; +} + +bool EspnowEncryptionBroker::verifyPeerSessionKey(const uint64_t sessionKey, const EncryptedConnectionLog &encryptedConnection, const uint64_t uint64PeerMac, const char messageType) +{ + using namespace EspnowProtocolInterpreter; + + if(usesEncryption(sessionKey)) + { + if(sessionKey == encryptedConnection.getPeerSessionKey() + || EspnowDatabase::receivedEspnowTransmissions().find(std::make_pair(createMacAndTypeValue(uint64PeerMac, messageType), sessionKey)) + != EspnowDatabase::receivedEspnowTransmissions().end()) + { + // If sessionKey is correct or sessionKey is one part of a multi-part transmission. + return true; + } + } + + return false; +} + +bool EspnowEncryptionBroker::synchronizePeerSessionKey(const uint64_t sessionKey, const uint8_t *peerMac) +{ + if(EncryptedConnectionLog *encryptedConnection = EspnowConnectionManager::getEncryptedConnection(peerMac)) + { + return synchronizePeerSessionKey(sessionKey, *encryptedConnection); + } + + return false; +} + +bool EspnowEncryptionBroker::synchronizePeerSessionKey(const uint64_t sessionKey, EncryptedConnectionLog &encryptedConnection) +{ + if(EspnowProtocolInterpreter::usesEncryption(sessionKey)) + { + if(sessionKey == encryptedConnection.getPeerSessionKey()) + { + uint8_t hashKey[hashKeyLength] {0}; + encryptedConnection.setPeerSessionKey(EncryptedConnectionLog::incrementSessionKey(sessionKey, encryptedConnection.getHashKey(hashKey), hashKeyLength)); + return true; + } + } + + return false; +} + +uint8_t EspnowEncryptionBroker::reservedEncryptedConnections() +{ + if(!_ongoingPeerRequestNonce.isEmpty()) + if(!EspnowConnectionManager::getEncryptedConnection(_ongoingPeerRequestMac)) + return EspnowConnectionManager::encryptedConnections().size() + 1; // Reserve one connection spot if we are currently making a peer request to a new node. + + return EspnowConnectionManager::encryptedConnections().size(); +} + +EncryptedConnectionStatus EspnowEncryptionBroker::requestEncryptedConnectionKernel(const uint8_t *peerMac, const encryptionRequestBuilderType &encryptionRequestBuilder, EspnowMeshBackend &espnowInstance) +{ + using namespace EspnowProtocolInterpreter; + + assert(EspnowConnectionManager::encryptedConnections().size() <= maxEncryptedConnections); // If this is not the case, ESP-NOW is no longer in sync with the library + + MutexTracker mutexTracker(EspnowTransmitter::captureEspnowTransmissionMutex(EspnowConnectionManager::handlePostponedRemovals)); + if(!mutexTracker.mutexCaptured()) + { + assert(false && String(F("ERROR! Transmission in progress. Don't call requestEncryptedConnection from callbacks as this may corrupt program state! Aborting."))); + return EncryptedConnectionStatus::REQUEST_TRANSMISSION_FAILED; + } + + EncryptedConnectionLog *existingEncryptedConnection = EspnowConnectionManager::getEncryptedConnection(peerMac); + ExpiringTimeTracker existingTimeTracker = existingEncryptedConnection && existingEncryptedConnection->temporary() ? + *existingEncryptedConnection->temporary() : ExpiringTimeTracker(0); + + if(!existingEncryptedConnection && EspnowConnectionManager::encryptedConnections().size() >= maxEncryptedConnections) + { + assert(EspnowConnectionManager::encryptedConnections().size() == maxEncryptedConnections); + + // No capacity for more encrypted connections. + return EncryptedConnectionStatus::MAX_CONNECTIONS_REACHED_SELF; + } + + String requestNonce = TypeCast::macToString(peerMac) + TypeCast::uint64ToString(MeshUtilityFunctions::randomUint64()) + + TypeCast::uint64ToString(MeshUtilityFunctions::randomUint64()); + _ongoingPeerRequestResult = EncryptedConnectionStatus::REQUEST_TRANSMISSION_FAILED; + _ongoingPeerRequestNonce = requestNonce; + _ongoingPeerRequester = &espnowInstance; + _reciprocalPeerRequestConfirmation = false; + std::copy_n(peerMac, 6, _ongoingPeerRequestMac); + String requestMessage = encryptionRequestBuilder(requestNonce, existingTimeTracker); + + _conditionalPrinter.verboseModePrint(String(F("Sending encrypted connection request to: ")) + TypeCast::macToString(peerMac)); + + if(EspnowTransmitter::espnowSendToNode(requestMessage, peerMac, 'P') == TransmissionStatusType::TRANSMISSION_COMPLETE) + { + ExpiringTimeTracker requestTimeout([](){ return EspnowDatabase::getEncryptionRequestTimeout(); }); + // _ongoingPeerRequestNonce is set to "" when a peer confirmation response from the mac is received + while(!requestTimeout && !_ongoingPeerRequestNonce.isEmpty()) + { + // For obvious reasons dividing by exactly 10 is a good choice. + ExpiringTimeTracker maxDurationTracker = ExpiringTimeTracker(EspnowDatabase::getEncryptionRequestTimeout()/10); + sendPeerRequestConfirmations(&maxDurationTracker); // Must be called before delay() to ensure !_ongoingPeerRequestNonce.isEmpty() is still true, so reciprocal peer request order is preserved. + delay(1); + } + } + + if(!_ongoingPeerRequestNonce.isEmpty()) + { + // If nonce != "" we only received the basic connection info, so the pairing process is incomplete + _ongoingPeerRequestResult = EncryptedConnectionStatus::REQUEST_TRANSMISSION_FAILED; + _ongoingPeerRequestNonce.clear(); + } + else if(encryptedConnectionEstablished(_ongoingPeerRequestResult)) + { + if(_ongoingPeerRequestResult == EncryptedConnectionStatus::CONNECTION_ESTABLISHED) + // Give the builder a chance to update the message + requestMessage = encryptionRequestBuilder(requestNonce, existingTimeTracker); + else if(_ongoingPeerRequestResult == EncryptedConnectionStatus::SOFT_LIMIT_CONNECTION_ESTABLISHED) + // We will only get a soft limit connection. Adjust future actions based on this. + requestMessage = Serializer::createEncryptionRequestHmacMessage(FPSTR(temporaryEncryptionRequestHeader), requestNonce, _connectionManager.getEspnowHashKey(), + hashKeyLength, _database.getAutoEncryptionDuration()); + else + assert(false && String(F("Unknown _ongoingPeerRequestResult during encrypted connection finalization!"))); + + int32_t messageHeaderEndIndex = requestMessage.indexOf(':'); + String messageHeader = requestMessage.substring(0, messageHeaderEndIndex + 1); + String messageBody = requestMessage.substring(messageHeaderEndIndex + 1); + + // If we do not get an ack within getEncryptionRequestTimeout() the peer has probably had the time to delete the temporary encrypted connection. + if(EspnowTransmitter::espnowSendToNode(String(FPSTR(encryptedConnectionVerificationHeader)) + requestMessage, peerMac, 'P') == TransmissionStatusType::TRANSMISSION_COMPLETE + && !_ongoingPeerRequestEncryptionTimeout) + { + EncryptedConnectionLog *encryptedConnection = EspnowConnectionManager::getEncryptedConnection(peerMac); + if(!encryptedConnection) + { + assert(encryptedConnection && String(F("requestEncryptedConnectionKernel cannot find an encrypted connection!"))); + // requestEncryptedConnectionRemoval received. + _ongoingPeerRequestResult = EncryptedConnectionStatus::REQUEST_TRANSMISSION_FAILED; + } + else if(encryptedConnection->removalScheduled() || (encryptedConnection->temporary() && encryptedConnection->temporary()->expired())) + { + // Could possibly be caused by a simultaneous temporary peer request from the peer. + _ongoingPeerRequestResult = EncryptedConnectionStatus::REQUEST_TRANSMISSION_FAILED; + } + else + { + // Finalize connection + if(messageHeader == FPSTR(encryptionRequestHeader)) + { + EspnowConnectionManager::temporaryEncryptedConnectionToPermanent(peerMac); + } + else if(messageHeader == FPSTR(temporaryEncryptionRequestHeader)) + { + if(encryptedConnection->temporary()) + { + // Should not change duration of existing permanent connections. + uint32_t connectionDuration = 0; + bool durationFound = JsonTranslator::getDuration(messageBody, connectionDuration); + assert(durationFound); + encryptedConnection->setRemainingDuration(connectionDuration); + } + } + else + { + assert(false && String(F("Unknown messageHeader during encrypted connection finalization!"))); + _ongoingPeerRequestResult = EncryptedConnectionStatus::API_CALL_FAILED; + } + } + } + else + { + _ongoingPeerRequestResult = EncryptedConnectionStatus::REQUEST_TRANSMISSION_FAILED; + } + } + + if(!encryptedConnectionEstablished(_ongoingPeerRequestResult)) + { + if(!existingEncryptedConnection && !_reciprocalPeerRequestConfirmation) + { + // Remove any connection that was added during the request attempt and is no longer in use. + EspnowConnectionManager::removeEncryptedConnectionUnprotected(peerMac); + } + } + + _ongoingPeerRequester = nullptr; + + return _ongoingPeerRequestResult; +} + +EncryptedConnectionStatus EspnowEncryptionBroker::initiateAutoEncryptingConnection(const EspnowNetworkInfo &recipientInfo, const bool requestPermanentConnection, uint8_t *targetBSSID, EncryptedConnectionLog **existingEncryptedConnection, EspnowMeshBackend &espnowInstance) +{ + assert(recipientInfo.BSSID() != nullptr); // We need at least the BSSID to connect + recipientInfo.getBSSID(targetBSSID); + + if(_conditionalPrinter.verboseMode()) // Avoid string generation if not required + { + espnowInstance.printAPInfo(recipientInfo); + _conditionalPrinter.verboseModePrint(emptyString); + } + + *existingEncryptedConnection = EspnowConnectionManager::getEncryptedConnection(targetBSSID); + EncryptedConnectionStatus connectionStatus = EncryptedConnectionStatus::MAX_CONNECTIONS_REACHED_SELF; + + if(requestPermanentConnection) + connectionStatus = requestEncryptedConnection(targetBSSID, espnowInstance); + else + connectionStatus = requestFlexibleTemporaryEncryptedConnection(targetBSSID, _database.getAutoEncryptionDuration(), espnowInstance); + + return connectionStatus; +} + +void EspnowEncryptionBroker::finalizeAutoEncryptingConnection(const uint8_t *targetBSSID, const EncryptedConnectionLog *existingEncryptedConnection, const bool requestPermanentConnection) +{ + if(!existingEncryptedConnection && !requestPermanentConnection && !_reciprocalPeerRequestConfirmation) + { + // Remove any connection that was added during the transmission attempt and is no longer in use. + EspnowConnectionManager::removeEncryptedConnectionUnprotected(targetBSSID); + } +} + +String EspnowEncryptionBroker::defaultEncryptionRequestBuilder(const String &requestHeader, const uint32_t durationMs, const uint8_t *hashKey, + const String &requestNonce, const ExpiringTimeTracker &existingTimeTracker) +{ + (void)existingTimeTracker; // This removes a "unused parameter" compiler warning. Does nothing else. + + return Serializer::createEncryptionRequestHmacMessage(requestHeader, requestNonce, hashKey, hashKeyLength, durationMs); +} + +String EspnowEncryptionBroker::flexibleEncryptionRequestBuilder(const uint32_t minDurationMs, const uint8_t *hashKey, + const String &requestNonce, const ExpiringTimeTracker &existingTimeTracker) +{ + using namespace JsonTranslator; + using EspnowProtocolInterpreter::temporaryEncryptionRequestHeader; + + uint32_t connectionDuration = minDurationMs >= existingTimeTracker.remainingDuration() ? + minDurationMs : existingTimeTracker.remainingDuration(); + + return Serializer::createEncryptionRequestHmacMessage(FPSTR(temporaryEncryptionRequestHeader), requestNonce, hashKey, hashKeyLength, connectionDuration); +} diff --git a/libraries/ESP8266WiFiMesh/src/EspnowEncryptionBroker.h b/libraries/ESP8266WiFiMesh/src/EspnowEncryptionBroker.h new file mode 100644 index 0000000000..25f4720979 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/EspnowEncryptionBroker.h @@ -0,0 +1,105 @@ +/* + Copyright (C) 2020 Anders Löfgren + + 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 __ESPNOWENCRYPTIONBROKER_H__ +#define __ESPNOWENCRYPTIONBROKER_H__ + +#include +#include "ConditionalPrinter.h" +#include "EspnowDatabase.h" +#include "EspnowConnectionManager.h" +#include "EspnowTransmitter.h" + +class EspnowMeshBackend; + +class EspnowEncryptionBroker +{ + +public: + + EspnowEncryptionBroker(ConditionalPrinter &conditionalPrinterInstance, EspnowDatabase &databaseInstance, EspnowConnectionManager &connectionManagerInstance, EspnowTransmitter &transmitterInstance); + + static void handlePeerRequest(const uint8_t *macaddr, uint8_t *dataArray, const uint8_t len, const uint64_t uint64StationMac, const uint64_t receivedMessageID); + static void handlePeerRequestConfirmation(uint8_t *macaddr, uint8_t *dataArray, const uint8_t len); + + /* + * @param estimatedMaxDurationTracker A pointer to an ExpiringTimeTracker initialized with the desired max duration for the method. If set to nullptr there is no duration limit. + * Note that setting the estimatedMaxDuration too low may result in missed ESP-NOW transmissions because of too little time for maintenance. + * Also note that although the method will try to respect the max duration limit, there is no guarantee. Overshoots by tens of milliseconds are possible. + */ + static void sendPeerRequestConfirmations(const ExpiringTimeTracker *estimatedMaxDurationTracker = nullptr); + + EncryptedConnectionStatus requestEncryptedConnection(const uint8_t *peerMac, EspnowMeshBackend &espnowInstance); + EncryptedConnectionStatus requestTemporaryEncryptedConnection(const uint8_t *peerMac, const uint32_t durationMs, EspnowMeshBackend &espnowInstance); + EncryptedConnectionStatus requestFlexibleTemporaryEncryptedConnection(const uint8_t *peerMac, const uint32_t minDurationMs, EspnowMeshBackend &espnowInstance); + EncryptedConnectionRemovalOutcome requestEncryptedConnectionRemoval(const uint8_t *peerMac); + + static bool encryptedConnectionEstablished(const EncryptedConnectionStatus connectionStatus); + + static bool verifyPeerSessionKey(const uint64_t sessionKey, const uint8_t *peerMac, const char messageType); + static bool verifyPeerSessionKey(const uint64_t sessionKey, const EncryptedConnectionLog &encryptedConnection, const uint64_t uint64PeerMac, const char messageType); + + static bool synchronizePeerSessionKey(const uint64_t sessionKey, const uint8_t *peerMac); + static bool synchronizePeerSessionKey(const uint64_t sessionKey, EncryptedConnectionLog &encryptedConnection); + + /** + * Set whether the most recently received ESP-NOW request, response or broadcast is presented as having been sent over an encrypted connection or not + * + * @param receivedEncryptedTransmission If true, the request, response or broadcast is presented as having been sent over an encrypted connection. + */ + void setReceivedEncryptedTransmission(const bool receivedEncryptedTransmission); + bool receivedEncryptedTransmission() const; + + /** + * reservedEncryptedConnections never underestimates but sometimes temporarily overestimates. + * numberOfEncryptedConnections sometimes temporarily underestimates but never overestimates. + * + * @return The current number of encrypted ESP-NOW connections, but with an encrypted connection immediately reserved if required while making a peer request. + */ + static uint8_t reservedEncryptedConnections(); + + EncryptedConnectionStatus initiateAutoEncryptingConnection(const EspnowNetworkInfo &recipientInfo, const bool requestPermanentConnection, uint8_t *targetBSSID, EncryptedConnectionLog **existingEncryptedConnection, EspnowMeshBackend &espnowInstance); + void finalizeAutoEncryptingConnection(const uint8_t *targetBSSID, const EncryptedConnectionLog *existingEncryptedConnection, const bool requestPermanentConnection); + +private: + + ConditionalPrinter & _conditionalPrinter; + EspnowDatabase & _database; + EspnowConnectionManager & _connectionManager; + EspnowTransmitter & _transmitter; + + using encryptionRequestBuilderType = std::function; + static String defaultEncryptionRequestBuilder(const String &requestHeader, const uint32_t durationMs, const uint8_t *hashKey, const String &requestNonce, const ExpiringTimeTracker &existingTimeTracker); + static String flexibleEncryptionRequestBuilder(const uint32_t minDurationMs, const uint8_t *hashKey, const String &requestNonce, const ExpiringTimeTracker &existingTimeTracker); + + /** + * Contains the core logic used for requesting an encrypted connection to a peerMac. + * + * @param peerMac The MAC of the node with which an encrypted connection should be established. + * @param encryptionRequestBuilder A function which is responsible for constructing the request message to send. + * Called twice when the request is successful. First to build the initial request message and then to build the connection verification message. + * The request message should typically be of the form found in Serializer::createEncryptionRequestHmacMessage. + * @param espnowInstance The EspnowMeshBackend instance that is requesting the encrypted connection. + * @return The ultimate status of the requested encrypted connection, as EncryptedConnectionStatus. + */ + EncryptedConnectionStatus requestEncryptedConnectionKernel(const uint8_t *peerMac, const encryptionRequestBuilderType &encryptionRequestBuilder, EspnowMeshBackend &espnowInstance); + + static bool verifyEncryptionRequestHmac(const String &encryptionRequestHmacMessage, const uint8_t *requesterStaMac, const uint8_t *requesterApMac, const uint8_t *hashKey, const uint8_t hashKeyLength); + + bool _receivedEncryptedTransmission = false; +}; + +#endif diff --git a/libraries/ESP8266WiFiMesh/src/EspnowMeshBackend.cpp b/libraries/ESP8266WiFiMesh/src/EspnowMeshBackend.cpp new file mode 100644 index 0000000000..f89f300813 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/EspnowMeshBackend.cpp @@ -0,0 +1,1044 @@ +/* + EspnowMeshBackend + + Copyright (C) 2019 Anders Löfgren + + 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 +extern "C" { + #include +} + +#include "EspnowMeshBackend.h" +#include "TypeConversionFunctions.h" +#include "UtilityFunctions.h" +#include "MutexTracker.h" +#include "JsonTranslator.h" +#include "MeshCryptoInterface.h" +#include "Serializer.h" + +namespace +{ + using EspnowProtocolInterpreter::encryptedConnectionKeyLength; + using EspnowProtocolInterpreter::hashKeyLength; + + namespace TypeCast = MeshTypeConversionFunctions; + + EspnowMeshBackend *_espnowRequestManager = nullptr; +} + +void espnowDelay(uint32_t durationMs) +{ + ExpiringTimeTracker timeout(durationMs); + + do + { + // We want to delay before performEspnowMaintenance() so background tasks can be managed first. + // Initial while combined with YieldAndDelayMs polledTimeout::YieldPolicy is not suitable since the delay then occurs before evaluating the condition (meaning durationMs = 1 never executes the loop interior). + delay(1); + EspnowMeshBackend::performEspnowMaintenance(); + } + while(!timeout); +} + +EspnowMeshBackend::EspnowMeshBackend(const requestHandlerType requestHandler, const responseHandlerType responseHandler, const networkFilterType networkFilter, + const broadcastFilterType broadcastFilter, const String &meshPassword, const String &ssidPrefix, const String &ssidSuffix, const bool verboseMode, + const uint8 meshWiFiChannel) + : MeshBackendBase(requestHandler, responseHandler, networkFilter, MeshBackendType::ESP_NOW), + _database(*getConditionalPrinter(), meshWiFiChannel), _connectionManager(*getConditionalPrinter(), *getDatabase()), + _transmitter(*getConditionalPrinter(), *getDatabase(), *getConnectionManager()), + _encryptionBroker(*getConditionalPrinter(), *getDatabase(), *getConnectionManager(), *getTransmitter()) +{ + setBroadcastFilter(broadcastFilter); + setSSID(ssidPrefix, emptyString, ssidSuffix); + setMeshPassword(meshPassword); + setVerboseModeState(verboseMode); + EspnowMeshBackend::setWiFiChannel(meshWiFiChannel); +} + +EspnowMeshBackend::EspnowMeshBackend(const requestHandlerType requestHandler, const responseHandlerType responseHandler, const networkFilterType networkFilter, + const broadcastFilterType broadcastFilter, const String &meshPassword, const uint8_t espnowEncryptedConnectionKey[encryptedConnectionKeyLength], + const uint8_t espnowHashKey[hashKeyLength], const String &ssidPrefix, const String &ssidSuffix, const bool verboseMode, + const uint8 meshWiFiChannel) + : EspnowMeshBackend(requestHandler, responseHandler, networkFilter, broadcastFilter, meshPassword, ssidPrefix, ssidSuffix, verboseMode, meshWiFiChannel) +{ + setEspnowEncryptedConnectionKey(espnowEncryptedConnectionKey); + setEspnowHashKey(espnowHashKey); +} + +EspnowMeshBackend::EspnowMeshBackend(const requestHandlerType requestHandler, const responseHandlerType responseHandler, const networkFilterType networkFilter, + const broadcastFilterType broadcastFilter, const String &meshPassword, const String &espnowEncryptedConnectionKeySeed, + const String &espnowHashKeySeed, const String &ssidPrefix, const String &ssidSuffix, const bool verboseMode, + const uint8 meshWiFiChannel) + : EspnowMeshBackend(requestHandler, responseHandler, networkFilter, broadcastFilter, meshPassword, ssidPrefix, ssidSuffix, verboseMode, meshWiFiChannel) +{ + setEspnowEncryptedConnectionKey(espnowEncryptedConnectionKeySeed); + setEspnowHashKey(espnowHashKeySeed); +} + +EspnowMeshBackend::~EspnowMeshBackend() +{ + if(isEspnowRequestManager()) + { + setEspnowRequestManager(nullptr); + } + + _database.deleteSentRequestsByOwner(this); +} + +std::vector & EspnowMeshBackend::connectionQueue() +{ + return EspnowDatabase::connectionQueue(); +} + +const std::vector & EspnowMeshBackend::constConnectionQueue() +{ + return EspnowDatabase::constConnectionQueue(); +} + +std::vector & EspnowMeshBackend::latestTransmissionOutcomes() +{ + return EspnowDatabase::latestTransmissionOutcomes(); +} + +bool EspnowMeshBackend::latestTransmissionSuccessful() +{ + return latestTransmissionSuccessfulBase(latestTransmissionOutcomes()); +} + +void EspnowMeshBackend::begin() +{ + if(!getAPController()) // If there is no active AP controller + WiFi.mode(WIFI_STA); // WIFI_AP_STA mode automatically sets up an AP, so we can't use that as default. + + activateEspnow(); +} + +bool EspnowMeshBackend::activateEspnow() +{ + if (esp_now_init()==0) + { + if(!EspnowConnectionManager::initializeEncryptionKok()) + warningPrint(String(F("Failed to set ESP-NOW KoK!"))); + + if(getEspnowRequestManager() == nullptr) + { + setEspnowRequestManager(this); + } + + esp_now_register_recv_cb(espnowReceiveCallbackWrapper); + esp_now_register_send_cb(EspnowTransmitter::espnowSendCallback); + + // Role must be set before adding peers. Cannot be changed while having peers. + // With ESP_NOW_ROLE_CONTROLLER, we always transmit from the station interface, which gives predictability. + if(esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER)) // esp_now_set_self_role returns 0 on success. + warningPrint(String(F("Failed to set ESP-NOW role! Maybe ESP-NOW peers are already added?"))); + + verboseModePrint(String(F("ESP-NOW activated."))); + verboseModePrint(String(F("My ESP-NOW STA MAC: ")) + WiFi.macAddress() + '\n'); // Get the station MAC address. The softAP MAC is different. + + return true; + } + else + { + warningPrint(String(F("ESP-NOW init failed!"))); + return false; + } +} + +bool EspnowMeshBackend::deactivateEspnow() +{ + // esp_now_deinit() clears all ESP-NOW API settings, including receive callback, send callback, Kok and peers. + // The node will however continue to give acks to received ESP-NOW transmissions as long as the receiving interface (AP or STA) is active, even though the transmissions will not be processed. + if(esp_now_deinit() == 0) + { + EspnowDatabase::responsesToSend().clear(); + EspnowDatabase::peerRequestConfirmationsToSend().clear(); + EspnowDatabase::receivedEspnowTransmissions().clear(); + EspnowDatabase::sentRequests().clear(); + EspnowDatabase::receivedRequests().clear(); + EspnowConnectionManager::encryptedConnections().clear(); + EncryptedConnectionLog::setNewRemovalsScheduled(false); + + return true; + } + else + { + return false; + } +} + +void EspnowMeshBackend::performEspnowMaintenance(const uint32_t estimatedMaxDuration) +{ + ExpiringTimeTracker estimatedMaxDurationTracker = ExpiringTimeTracker(estimatedMaxDuration); + + // Doing this during an ESP-NOW transmission could invalidate iterators + MutexTracker mutexTracker(EspnowTransmitter::captureEspnowTransmissionMutex(EspnowConnectionManager::handlePostponedRemovals)); + if(!mutexTracker.mutexCaptured()) + { + assert(false && String(F("ERROR! Transmission in progress. Don't call performEspnowMaintenance from callbacks as this may corrupt program state! Aborting."))); + return; + } + + EspnowDatabase::clearOldLogEntries(false); + + if(EncryptedConnectionLog::getSoonestExpiringConnectionTracker() && EncryptedConnectionLog::getSoonestExpiringConnectionTracker()->expired()) + { + EspnowConnectionManager::updateTemporaryEncryptedConnections(); + } + + if(estimatedMaxDuration > 0) + { + if(estimatedMaxDurationTracker.expired()) + return; + else + sendStoredEspnowMessages(&estimatedMaxDurationTracker); + } + else + { + sendStoredEspnowMessages(); + } +} + +void EspnowMeshBackend::espnowReceiveCallbackWrapper(uint8_t *macaddr, uint8_t *dataArray, const uint8_t len) +{ + using namespace EspnowProtocolInterpreter; + + // Since this callback can be called during any delay(), we should always consider all mutexes captured. + // This provides a consistent mutex environment, which facilitates development and debugging. + // Otherwise we get issues such as EspnowTransmitter::_espnowTransmissionMutex will usually be free, but occasionally taken (when callback occurs in a delay() during attemptTransmission). + MutexTracker captureBanTracker(MutexTracker::captureBan()); + + if(len >= metadataSize()) // If we do not receive at least the metadata bytes, the transmission is invalid. + { + //uint32_t callbackStart = millis(); + + // If there is a espnowRequestManager, get it + EspnowMeshBackend *currentEspnowRequestManager = getEspnowRequestManager(); + + char messageType = getMessageType(dataArray); + uint64_t receivedMessageID = getMessageID(dataArray); + + if(currentEspnowRequestManager && !currentEspnowRequestManager->acceptsUnverifiedRequests() + && !usesConstantSessionKey(messageType) && !EspnowEncryptionBroker::verifyPeerSessionKey(receivedMessageID, macaddr, messageType)) + { + return; + } + + if(EspnowTransmitter::useEncryptedMessages()) + { + // chacha20Poly1305Decrypt decrypts dataArray in place. + // We are using the protocol bytes as a key salt. + if(!experimental::crypto::ChaCha20Poly1305::decrypt(dataArray + metadataSize(), len - metadataSize(), getEspnowMessageEncryptionKey(), dataArray, + protocolBytesSize, dataArray + protocolBytesSize, dataArray + protocolBytesSize + 12)) + { + return; // Decryption of message failed. + } + } + + uint64_t uint64StationMac = TypeCast::macToUint64(macaddr); + bool transmissionEncrypted = usesEncryption(receivedMessageID); + + // Useful when debugging the protocol + //Serial.print("Received from Mac: " + TypeCast::macToString(macaddr) + " ID: " + TypeCast::uint64ToString(receivedMessageID)); + //Serial.println(transmissionEncrypted ? " Encrypted" : " Unencrypted"); + + if(messageType == 'Q' || messageType == 'B') // Question (request) or Broadcast + { + if(ESP.getFreeHeap() <= criticalHeapLevel()) + { + warningPrint("WARNING! Free heap below critical level. Suspending ESP-NOW request processing until the situation improves."); + return; + } + + if(currentEspnowRequestManager) + { + if(!EspnowDatabase::requestReceived(uint64StationMac, receivedMessageID)) // If the request has not already been received + { + if(transmissionEncrypted) + { + EncryptedConnectionLog *encryptedConnection = EspnowConnectionManager::getEncryptedConnection(macaddr); + + if(!encryptedConnection || (!EspnowEncryptionBroker::synchronizePeerSessionKey(receivedMessageID, *encryptedConnection) && + !EspnowEncryptionBroker::verifyPeerSessionKey(receivedMessageID, *encryptedConnection, uint64StationMac, messageType))) + { + // We received an encrypted transmission + // and we have no encrypted connection to the transmitting node (in which case we want to avoid sending the secret session key back in an unencrypted response) + // or the transmission has the wrong session key + // and it doesn't have a session key that matches any multi-part transmission we are currently receiving (in which case the transmission is invalid). + return; + } + } + + //Serial.println("espnowReceiveCallbackWrapper before internal callback " + String(millis() - callbackStart)); + + currentEspnowRequestManager->espnowReceiveCallback(macaddr, dataArray, len); + } + } + } + else if(messageType == 'A') // Answer (response) + { + EspnowMeshBackend *requestSender = nullptr; + uint64_t requestMac = 0; + + if(transmissionEncrypted) + { + // An encrypted transmission can only be sent to the station interface, since it otherwise won't arrive (because of ESP_NOW_ROLE_CONTROLLER). + requestMac = uint64StationMac; + requestSender = EspnowDatabase::getOwnerOfSentRequest(requestMac, receivedMessageID); + } + else + { + // An unencrypted transmission was probably sent to the AP interface as a result of a scan. + requestMac = getTransmissionMac(dataArray); + requestSender = EspnowDatabase::getOwnerOfSentRequest(requestMac, receivedMessageID); + + // But if not, also check if it was sent to the station interface. + if(!requestSender) + { + requestMac = uint64StationMac; + requestSender = EspnowDatabase::getOwnerOfSentRequest(requestMac, receivedMessageID); + } + + // Or if it was sent as a broadcast. (A broadcast can never be encrypted) + if(!requestSender) + { + requestSender = EspnowDatabase::getOwnerOfSentRequest(uint64BroadcastMac, receivedMessageID); + } + } + + // If this node sent the request and it has not already been answered. + if(requestSender) + { + uint8_t macArray[6] = { 0 }; + + requestSender->espnowReceiveCallback(TypeCast::uint64ToMac(requestMac, macArray), dataArray, len); + } + } + else if(messageType == 'S') // Synchronization request + { + EspnowEncryptionBroker::synchronizePeerSessionKey(receivedMessageID, macaddr); + } + else if(messageType == 'P') // Peer request + { + EspnowEncryptionBroker::handlePeerRequest(macaddr, dataArray, len, uint64StationMac, receivedMessageID); + } + else if(messageType == 'C') // peer request Confirmation + { + EspnowEncryptionBroker::handlePeerRequestConfirmation(macaddr, dataArray, len); + } + else + { + assert(messageType == 'Q' || messageType == 'A' || messageType == 'B' || messageType == 'S' || messageType == 'P' || messageType == 'C'); + } + + //Serial.println("espnowReceiveCallbackWrapper duration " + String(millis() - callbackStart)); + } +} + +void EspnowMeshBackend::espnowReceiveCallback(const uint8_t *macaddr, uint8_t *dataArray, const uint8_t len) +{ + using namespace EspnowProtocolInterpreter; + + ////// ////// + /* + if(messageStart) + { + storeTransmission + } + else + { + if(messageFound) + storeTransmission or (erase and return) + else + return + } + + if(transmissionsRemaining != 0) + return + + processMessage + */ + ////// ////// + + char messageType = getMessageType(dataArray); + uint8_t transmissionsRemaining = getTransmissionsRemaining(dataArray); + uint64_t uint64Mac = TypeCast::macToUint64(macaddr); + + // The MAC is 6 bytes so two bytes of uint64Mac are free. We must include the messageType there since it is possible that we will + // receive both a request and a response that shares the same messageID from the same uint64Mac, being distinguished only by the messageType. + // This would otherwise potentially cause the request and response to be mixed into one message when they are multi-part transmissions sent roughly at the same time. + macAndType_td macAndType = createMacAndTypeValue(uint64Mac, messageType); + uint64_t messageID = getMessageID(dataArray); + + //uint32_t methodStart = millis(); + + if(isMessageStart(dataArray)) + { + if(messageType == 'B') + { + auto key = std::make_pair(macAndType, messageID); + if(EspnowDatabase::receivedEspnowTransmissions().find(key) != EspnowDatabase::receivedEspnowTransmissions().end()) + return; // Should not call BroadcastFilter more than once for an accepted message + + String message = getHashKeyLength(dataArray, len); + _database.setSenderMac(macaddr); + uint8_t senderAPMac[6] {0}; + _database.setSenderAPMac(getTransmissionMac(dataArray, senderAPMac)); + _encryptionBroker.setReceivedEncryptedTransmission(usesEncryption(messageID)); + bool acceptBroadcast = getBroadcastFilter()(message, *this); + if(acceptBroadcast) + { + // Does nothing if key already in receivedEspnowTransmissions + EspnowDatabase::receivedEspnowTransmissions().insert(std::make_pair(key, MessageData(message, getTransmissionsRemaining(dataArray)))); + } + else + { + return; + } + } + else + { + // Does nothing if key already in receivedEspnowTransmissions + EspnowDatabase::receivedEspnowTransmissions().insert(std::make_pair(std::make_pair(macAndType, messageID), MessageData(dataArray, len))); + } + } + else + { + std::map, MessageData>::iterator storedMessageIterator = EspnowDatabase::receivedEspnowTransmissions().find(std::make_pair(macAndType, messageID)); + + if(storedMessageIterator == EspnowDatabase::receivedEspnowTransmissions().end()) // If we have not stored the key already, we missed the first message part. + { + return; + } + + if(!storedMessageIterator->second.addToMessage(dataArray, len)) + { + // If we received the wrong message part, remove the whole message if we have missed a part. + // Otherwise just ignore the received part since it has already been stored. + + uint8_t transmissionsRemainingExpected = storedMessageIterator->second.getTransmissionsRemaining() - 1; + + if(transmissionsRemaining < transmissionsRemainingExpected) + { + EspnowDatabase::receivedEspnowTransmissions().erase(storedMessageIterator); + return; + } + } + } + + //Serial.println("methodStart storage done " + String(millis() - methodStart)); + + if(transmissionsRemaining != 0) + { + return; + } + + std::map, MessageData>::iterator storedMessageIterator = EspnowDatabase::receivedEspnowTransmissions().find(std::make_pair(macAndType, messageID)); + assert(storedMessageIterator != EspnowDatabase::receivedEspnowTransmissions().end()); + + // Copy totalMessage in case user callbacks (request/responseHandler) do something odd with receivedEspnowTransmissions list. + String totalMessage = storedMessageIterator->second.getTotalMessage(); // https://stackoverflow.com/questions/134731/returning-a-const-reference-to-an-object-instead-of-a-copy It is likely that most compilers will perform Named Value Return Value Optimisation in this case + + EspnowDatabase::receivedEspnowTransmissions().erase(storedMessageIterator); // Erase the extra copy of the totalMessage, to save RAM. + + //Serial.println("methodStart erase done " + String(millis() - methodStart)); + + if(messageType == 'Q' || messageType == 'B') // Question (request) or Broadcast + { + EspnowDatabase::storeReceivedRequest(uint64Mac, messageID, TimeTracker(millis())); + //Serial.println("methodStart request stored " + String(millis() - methodStart)); + + _database.setSenderMac(macaddr); + uint8_t senderAPMac[6] {0}; + _database.setSenderAPMac(getTransmissionMac(dataArray, senderAPMac)); + _encryptionBroker.setReceivedEncryptedTransmission(usesEncryption(messageID)); + String response = getRequestHandler()(totalMessage, *this); + //Serial.println("methodStart response acquired " + String(millis() - methodStart)); + + if(response.length() > 0) + { + EspnowDatabase::responsesToSend().emplace_back(response, macaddr, messageID); + + //Serial.println("methodStart Q done " + String(millis() - methodStart)); + } + } + else if(messageType == 'A') // Answer (response) + { + EspnowDatabase::deleteSentRequest(uint64Mac, messageID); // Request has been answered, so stop accepting new answers about it. + + if(EncryptedConnectionLog *encryptedConnection = EspnowConnectionManager::getEncryptedConnection(macaddr)) + { + if(encryptedConnection->getOwnSessionKey() == messageID) + { + encryptedConnection->setDesync(false); // We just received an answer to the latest request we sent to the node, so the node sending the answer must now be in sync. + encryptedConnection->incrementOwnSessionKey(); + } + } + + _database.setSenderMac(macaddr); + uint8_t senderAPMac[6] {0}; + _database.setSenderAPMac(getTransmissionMac(dataArray, senderAPMac)); + _encryptionBroker.setReceivedEncryptedTransmission(usesEncryption(messageID)); + getResponseHandler()(totalMessage, *this); + } + else + { + assert(messageType == 'Q' || messageType == 'A' || messageType == 'B'); + } + + ESP.wdtFeed(); // Prevents WDT reset in case we receive a lot of transmissions without break. + + //Serial.println("methodStart wdtFeed done " + String(millis() - methodStart)); +} + +void EspnowMeshBackend::setEspnowRequestManager(EspnowMeshBackend *espnowMeshInstance) +{ + _espnowRequestManager = espnowMeshInstance; +} + +EspnowMeshBackend *EspnowMeshBackend::getEspnowRequestManager() {return _espnowRequestManager;} + +bool EspnowMeshBackend::isEspnowRequestManager() const +{ + return (this == getEspnowRequestManager()); +} + +void EspnowMeshBackend::setLogEntryLifetimeMs(const uint32_t logEntryLifetimeMs) +{ + EspnowDatabase::setLogEntryLifetimeMs(logEntryLifetimeMs); +} +uint32_t EspnowMeshBackend::logEntryLifetimeMs() { return EspnowDatabase::logEntryLifetimeMs(); } + +void EspnowMeshBackend::setBroadcastResponseTimeoutMs(const uint32_t broadcastResponseTimeoutMs) +{ + EspnowDatabase::setBroadcastResponseTimeoutMs(broadcastResponseTimeoutMs); +} +uint32_t EspnowMeshBackend::broadcastResponseTimeoutMs() { return EspnowDatabase::broadcastResponseTimeoutMs(); } + +void EspnowMeshBackend::setCriticalHeapLevelBuffer(const uint32_t bufferInBytes) +{ + EspnowDatabase::setCriticalHeapLevelBuffer(bufferInBytes); +} + +uint32_t EspnowMeshBackend::criticalHeapLevelBuffer() +{ + return EspnowDatabase::criticalHeapLevelBuffer(); +} + +uint32_t EspnowMeshBackend::criticalHeapLevel() +{ + return EspnowDatabase::criticalHeapLevel(); +} + +void EspnowMeshBackend::setEspnowTransmissionTimeout(const uint32_t timeoutMs) +{ + EspnowTransmitter::setEspnowTransmissionTimeout(timeoutMs); +} +uint32_t EspnowMeshBackend::getEspnowTransmissionTimeout() {return EspnowTransmitter::getEspnowTransmissionTimeout();} + +void EspnowMeshBackend::setEspnowRetransmissionInterval(const uint32_t intervalMs) +{ + EspnowTransmitter::setEspnowRetransmissionInterval(intervalMs); +} +uint32_t EspnowMeshBackend::getEspnowRetransmissionInterval() {return EspnowTransmitter::getEspnowRetransmissionInterval();} + +void EspnowMeshBackend::setEncryptionRequestTimeout(const uint32_t timeoutMs) +{ + EspnowDatabase::setEncryptionRequestTimeout(timeoutMs); +} +uint32_t EspnowMeshBackend::getEncryptionRequestTimeout() {return EspnowDatabase::getEncryptionRequestTimeout();} + +void EspnowMeshBackend::setAutoEncryptionDuration(const uint32_t duration) +{ + _database.setAutoEncryptionDuration(duration); +} +uint32_t EspnowMeshBackend::getAutoEncryptionDuration() const {return _database.getAutoEncryptionDuration();} + +void EspnowMeshBackend::setBroadcastFilter(const broadcastFilterType broadcastFilter) {_broadcastFilter = broadcastFilter;} +EspnowMeshBackend::broadcastFilterType EspnowMeshBackend::getBroadcastFilter() const {return _broadcastFilter;} + +void EspnowMeshBackend::setEspnowEncryptedConnectionKey(const uint8_t espnowEncryptedConnectionKey[encryptedConnectionKeyLength]) +{ + _connectionManager.setEspnowEncryptedConnectionKey(espnowEncryptedConnectionKey); +} + +void EspnowMeshBackend::setEspnowEncryptedConnectionKey(const String &espnowEncryptedConnectionKeySeed) +{ + _connectionManager.setEspnowEncryptedConnectionKey(espnowEncryptedConnectionKeySeed); +} + +const uint8_t *EspnowMeshBackend::getEspnowEncryptedConnectionKey() const +{ + return _connectionManager.getEspnowEncryptedConnectionKey(); +} + +uint8_t *EspnowMeshBackend::getEspnowEncryptedConnectionKey(uint8_t resultArray[encryptedConnectionKeyLength]) const +{ + return _connectionManager.getEspnowEncryptedConnectionKey(resultArray); +} + +bool EspnowMeshBackend::setEspnowEncryptionKok(uint8_t espnowEncryptionKok[encryptedConnectionKeyLength]) +{ + return EspnowConnectionManager::setEspnowEncryptionKok(espnowEncryptionKok); +} + +bool EspnowMeshBackend::setEspnowEncryptionKok(const String &espnowEncryptionKokSeed) +{ + return EspnowConnectionManager::setEspnowEncryptionKok(espnowEncryptionKokSeed); +} + +const uint8_t *EspnowMeshBackend::getEspnowEncryptionKok() +{ + return EspnowConnectionManager::getEspnowEncryptionKok(); +} + +void EspnowMeshBackend::setEspnowHashKey(const uint8_t espnowHashKey[hashKeyLength]) +{ + _connectionManager.setEspnowHashKey(espnowHashKey); +} + +void EspnowMeshBackend::setEspnowHashKey(const String &espnowHashKeySeed) +{ + _connectionManager.setEspnowHashKey(espnowHashKeySeed); +} + +const uint8_t *EspnowMeshBackend::getEspnowHashKey() const +{ + return _connectionManager.getEspnowHashKey(); +} + +void EspnowMeshBackend::setUseEncryptedMessages(const bool useEncryptedMessages) +{ + EspnowTransmitter::setUseEncryptedMessages(useEncryptedMessages); +} +bool EspnowMeshBackend::useEncryptedMessages() { return EspnowTransmitter::useEncryptedMessages(); } + +void EspnowMeshBackend::setEspnowMessageEncryptionKey(const uint8_t espnowMessageEncryptionKey[experimental::crypto::ENCRYPTION_KEY_LENGTH]) +{ + EspnowTransmitter::setEspnowMessageEncryptionKey(espnowMessageEncryptionKey); +} + +void EspnowMeshBackend::setEspnowMessageEncryptionKey(const String &espnowMessageEncryptionKeySeed) +{ + EspnowTransmitter::setEspnowMessageEncryptionKey(espnowMessageEncryptionKeySeed); +} + +const uint8_t *EspnowMeshBackend::getEspnowMessageEncryptionKey() +{ + return EspnowTransmitter::getEspnowMessageEncryptionKey(); +} + +String EspnowMeshBackend::getScheduledResponseMessage(const uint32_t responseIndex) +{ + return EspnowDatabase::getScheduledResponseMessage(responseIndex); +} + +const uint8_t *EspnowMeshBackend::getScheduledResponseRecipient(const uint32_t responseIndex) +{ + return EspnowDatabase::getScheduledResponseRecipient(responseIndex); +} + +uint32_t EspnowMeshBackend::numberOfScheduledResponses() {return EspnowDatabase::numberOfScheduledResponses();} + +void EspnowMeshBackend::clearAllScheduledResponses() +{ + EspnowDatabase::clearAllScheduledResponses(); +} + +void EspnowMeshBackend::deleteScheduledResponsesByRecipient(const uint8_t *recipientMac, const bool encryptedOnly) +{ + EspnowDatabase::deleteScheduledResponsesByRecipient(recipientMac, encryptedOnly); +} + +String EspnowMeshBackend::getSenderMac() const {return _database.getSenderMac();} +uint8_t *EspnowMeshBackend::getSenderMac(uint8_t *macArray) const +{ + return _database.getSenderMac(macArray); +} + +String EspnowMeshBackend::getSenderAPMac() const {return _database.getSenderAPMac();} +uint8_t *EspnowMeshBackend::getSenderAPMac(uint8_t *macArray) const +{ + return _database.getSenderAPMac(macArray); +} + +bool EspnowMeshBackend::receivedEncryptedTransmission() const {return _encryptionBroker.receivedEncryptedTransmission();} + +bool EspnowMeshBackend::addUnencryptedConnection(const String &serializedConnectionState) +{ + return EspnowConnectionManager::addUnencryptedConnection(serializedConnectionState); +} + +EncryptedConnectionStatus EspnowMeshBackend::addEncryptedConnection(uint8_t *peerStaMac, uint8_t *peerApMac, const uint64_t peerSessionKey, const uint64_t ownSessionKey) +{ + return _connectionManager.addEncryptedConnection(peerStaMac, peerApMac, peerSessionKey, ownSessionKey); +} + +EncryptedConnectionStatus EspnowMeshBackend::addEncryptedConnection(const String &serializedConnectionState, const bool ignoreDuration) +{ + return _connectionManager.addEncryptedConnection(serializedConnectionState, ignoreDuration); +} + +EncryptedConnectionStatus EspnowMeshBackend::addTemporaryEncryptedConnection(uint8_t *peerStaMac, uint8_t *peerApMac, const uint64_t peerSessionKey, const uint64_t ownSessionKey, const uint32_t duration) +{ + return _connectionManager.addTemporaryEncryptedConnection(peerStaMac, peerApMac, peerSessionKey, ownSessionKey, duration); +} + +EncryptedConnectionStatus EspnowMeshBackend::addTemporaryEncryptedConnection(const String &serializedConnectionState, const uint32_t duration) +{ + return _connectionManager.addTemporaryEncryptedConnection(serializedConnectionState, duration); +} + +EncryptedConnectionStatus EspnowMeshBackend::requestEncryptedConnection(const uint8_t *peerMac) +{ + return _encryptionBroker.requestEncryptedConnection(peerMac, *this); +} + +EncryptedConnectionStatus EspnowMeshBackend::requestTemporaryEncryptedConnection(const uint8_t *peerMac, const uint32_t durationMs) +{ + return _encryptionBroker.requestTemporaryEncryptedConnection(peerMac, durationMs, *this); +} + +EncryptedConnectionStatus EspnowMeshBackend::requestFlexibleTemporaryEncryptedConnection(const uint8_t *peerMac, const uint32_t minDurationMs) +{ + return _encryptionBroker.requestFlexibleTemporaryEncryptedConnection(peerMac, minDurationMs, *this); +} + +EncryptedConnectionRemovalOutcome EspnowMeshBackend::removeEncryptedConnection(const uint8_t *peerMac) +{ + return EspnowConnectionManager::removeEncryptedConnection(peerMac); +} + +EncryptedConnectionRemovalOutcome EspnowMeshBackend::requestEncryptedConnectionRemoval(const uint8_t *peerMac) +{ + return _encryptionBroker.requestEncryptedConnectionRemoval(peerMac); +} + +void EspnowMeshBackend::setAcceptsUnverifiedRequests(const bool acceptsUnverifiedRequests) { _acceptsUnverifiedRequests = acceptsUnverifiedRequests; } +bool EspnowMeshBackend::acceptsUnverifiedRequests() const { return _acceptsUnverifiedRequests; } + +void EspnowMeshBackend::setEncryptedConnectionsSoftLimit(const uint8_t softLimit) +{ + _connectionManager.setEncryptedConnectionsSoftLimit(softLimit); +} + +uint8_t EspnowMeshBackend::encryptedConnectionsSoftLimit() const { return _connectionManager.encryptedConnectionsSoftLimit(); } + +uint8_t *EspnowMeshBackend::getEncryptedMac(const uint8_t *peerMac, uint8_t *resultArray) +{ + return EspnowConnectionManager::getEncryptedMac(peerMac, resultArray); +} + +void EspnowMeshBackend::prepareForTransmission(const String &message, const bool scan, const bool scanAllWiFiChannels) +{ + setMessage(message); + + latestTransmissionOutcomes().clear(); + + if(scan) + { + connectionQueue().clear(); + scanForNetworks(scanAllWiFiChannels); + } +} + +TransmissionStatusType EspnowMeshBackend::initiateTransmission(const String &message, const EspnowNetworkInfo &recipientInfo) +{ + uint8_t targetBSSID[6] {0}; + + assert(recipientInfo.BSSID() != nullptr); // We need at least the BSSID to connect + recipientInfo.getBSSID(targetBSSID); + + if(verboseMode()) // Avoid string generation if not required + { + printAPInfo(recipientInfo); + verboseModePrint(emptyString); + } + + return initiateTransmissionKernel(message, targetBSSID); +} + +TransmissionStatusType EspnowMeshBackend::initiateTransmissionKernel(const String &message, const uint8_t *targetBSSID) +{ + uint32_t transmissionStartTime = millis(); + TransmissionStatusType transmissionResult = _transmitter.sendRequest(message, targetBSSID, this); + + uint32_t transmissionDuration = millis() - transmissionStartTime; + + if(verboseMode() && transmissionResult == TransmissionStatusType::TRANSMISSION_COMPLETE) // Avoid calculations if not required + { + totalDurationWhenSuccessful_AT += transmissionDuration; + ++successfulTransmissions_AT; + if(transmissionDuration > maxTransmissionDuration_AT) + { + maxTransmissionDuration_AT = transmissionDuration; + } + } + + return transmissionResult; +} + +void EspnowMeshBackend::printTransmissionStatistics() const +{ + if(verboseMode() && successfulTransmissions_AT > 0) // Avoid calculations if not required + { + verboseModePrint(String(F("Average duration of successful transmissions: ")) + String(totalDurationWhenSuccessful_AT/successfulTransmissions_AT) + String(F(" ms."))); + verboseModePrint(String(F("Maximum duration of successful transmissions: ")) + String(maxTransmissionDuration_AT) + String(F(" ms."))); + } + else + { + verboseModePrint(String(F("No successful transmission."))); + } +} + +void EspnowMeshBackend::attemptTransmission(const String &message, const bool scan, const bool scanAllWiFiChannels) +{ + MutexTracker mutexTracker(EspnowTransmitter::captureEspnowTransmissionMutex(EspnowConnectionManager::handlePostponedRemovals)); + if(!mutexTracker.mutexCaptured()) + { + assert(false && String(F("ERROR! Transmission in progress. Don't call attemptTransmission from callbacks as this may corrupt program state! Aborting."))); + return; + } + + prepareForTransmission(message, scan, scanAllWiFiChannels); + + MutexTracker connectionQueueMutexTracker(EspnowDatabase::captureEspnowConnectionQueueMutex()); + if(!connectionQueueMutexTracker.mutexCaptured()) + { + assert(false && String(F("ERROR! connectionQueue locked. Don't call attemptTransmission from callbacks as this may corrupt program state! Aborting."))); + } + else + { + for(const EspnowNetworkInfo ¤tNetwork : constConnectionQueue()) + { + TransmissionStatusType transmissionResult = initiateTransmission(getMessage(), currentNetwork); + + latestTransmissionOutcomes().push_back(TransmissionOutcome{.origin = currentNetwork, .transmissionStatus = transmissionResult}); + + if(!getTransmissionOutcomesUpdateHook()(*this)) + break; + } + } + + printTransmissionStatistics(); +} + +TransmissionStatusType EspnowMeshBackend::attemptTransmission(const String &message, const EspnowNetworkInfo &recipientInfo) +{ + MutexTracker mutexTracker(EspnowTransmitter::captureEspnowTransmissionMutex(EspnowConnectionManager::handlePostponedRemovals)); + if(!mutexTracker.mutexCaptured()) + { + assert(false && String(F("ERROR! Transmission in progress. Don't call attemptTransmission from callbacks as this may corrupt program state! Aborting."))); + return TransmissionStatusType::CONNECTION_FAILED; + } + + return initiateTransmission(message, recipientInfo); +} + +TransmissionStatusType EspnowMeshBackend::initiateAutoEncryptingTransmission(const String &message, uint8_t *targetBSSID, EncryptedConnectionStatus connectionStatus) +{ + TransmissionStatusType transmissionResult = TransmissionStatusType::CONNECTION_FAILED; + + if(EspnowEncryptionBroker::encryptedConnectionEstablished(connectionStatus)) + { + uint8_t encryptedMac[6] {0}; + assert(getEncryptedMac(targetBSSID, encryptedMac) && esp_now_is_peer_exist(encryptedMac) > 0 && String(F("ERROR! Attempting to send content marked as encrypted via unencrypted connection!"))); + transmissionResult = initiateTransmissionKernel(message, targetBSSID); + } + + return transmissionResult; +} + +void EspnowMeshBackend::attemptAutoEncryptingTransmission(const String &message, const bool requestPermanentConnections, const bool scan, const bool scanAllWiFiChannels) +{ + MutexTracker outerMutexTracker(EspnowTransmitter::captureEspnowTransmissionMutex(EspnowConnectionManager::handlePostponedRemovals)); + if(!outerMutexTracker.mutexCaptured()) + { + assert(false && String(F("ERROR! Transmission in progress. Don't call attemptAutoEncryptingTransmission from callbacks as this may corrupt program state! Aborting."))); + return; + } + + prepareForTransmission(message, scan, scanAllWiFiChannels); + + outerMutexTracker.releaseMutex(); + + MutexTracker connectionQueueMutexTracker(EspnowDatabase::captureEspnowConnectionQueueMutex()); + if(!connectionQueueMutexTracker.mutexCaptured()) + { + assert(false && String(F("ERROR! connectionQueue locked. Don't call attemptAutoEncryptingTransmission from callbacks as this may corrupt program state! Aborting."))); + } + else + { + for(const EspnowNetworkInfo ¤tNetwork : constConnectionQueue()) + { + uint8_t currentBSSID[6] {0}; + EncryptedConnectionLog *existingEncryptedConnection = nullptr; + EncryptedConnectionStatus connectionStatus = _encryptionBroker.initiateAutoEncryptingConnection(currentNetwork, requestPermanentConnections, currentBSSID, &existingEncryptedConnection, *this); + + MutexTracker innerMutexTracker(EspnowTransmitter::captureEspnowTransmissionMutex()); + if(!innerMutexTracker.mutexCaptured()) + { + assert(false && String(F("ERROR! Unable to recapture Mutex in attemptAutoEncryptingTransmission. Aborting."))); + return; + } + + TransmissionStatusType transmissionResult = initiateAutoEncryptingTransmission(getMessage(), currentBSSID, connectionStatus); + + latestTransmissionOutcomes().push_back(TransmissionOutcome{.origin = currentNetwork, .transmissionStatus = transmissionResult}); + + _encryptionBroker.finalizeAutoEncryptingConnection(currentBSSID, existingEncryptedConnection, requestPermanentConnections); + + if(!getTransmissionOutcomesUpdateHook()(*this)) + break; + } + } + + printTransmissionStatistics(); +} + +TransmissionStatusType EspnowMeshBackend::attemptAutoEncryptingTransmission(const String &message, const EspnowNetworkInfo &recipientInfo, const bool requestPermanentConnection) +{ + uint8_t targetBSSID[6] {0}; + EncryptedConnectionLog *existingEncryptedConnection = nullptr; + EncryptedConnectionStatus connectionStatus = _encryptionBroker.initiateAutoEncryptingConnection(recipientInfo, requestPermanentConnection, targetBSSID, &existingEncryptedConnection, *this); + + MutexTracker mutexTracker(EspnowTransmitter::captureEspnowTransmissionMutex(EspnowConnectionManager::handlePostponedRemovals)); + if(!mutexTracker.mutexCaptured()) + { + assert(false && String(F("ERROR! Transmission in progress. Don't call attemptTransmission from callbacks as this may corrupt program state! Aborting."))); + return TransmissionStatusType::CONNECTION_FAILED; + } + + TransmissionStatusType transmissionResult = initiateAutoEncryptingTransmission(message, targetBSSID, connectionStatus); + + _encryptionBroker.finalizeAutoEncryptingConnection(targetBSSID, existingEncryptedConnection, requestPermanentConnection); + + return transmissionResult; +} + +void EspnowMeshBackend::broadcast(const String &message) +{ + MutexTracker mutexTracker(EspnowTransmitter::captureEspnowTransmissionMutex(EspnowConnectionManager::handlePostponedRemovals)); + if(!mutexTracker.mutexCaptured()) + { + assert(false && String(F("ERROR! Transmission in progress. Don't call broadcast from callbacks as this may corrupt program state! Aborting."))); + return; + } + + EspnowTransmitter::espnowSendToNode(message, EspnowProtocolInterpreter::broadcastMac, 'B', this); +} + +void EspnowMeshBackend::setBroadcastTransmissionRedundancy(const uint8_t redundancy) { _transmitter.setBroadcastTransmissionRedundancy(redundancy); } +uint8_t EspnowMeshBackend::getBroadcastTransmissionRedundancy() const { return _transmitter.getBroadcastTransmissionRedundancy(); } + +void EspnowMeshBackend::setResponseTransmittedHook(const EspnowTransmitter::responseTransmittedHookType responseTransmittedHook) { _transmitter.setResponseTransmittedHook(responseTransmittedHook); } +EspnowTransmitter::responseTransmittedHookType EspnowMeshBackend::getResponseTransmittedHook() const { return _transmitter.getResponseTransmittedHook(); } + +EspnowDatabase *EspnowMeshBackend::getDatabase() { return &_database; } +const EspnowDatabase *EspnowMeshBackend::getDatabaseConst() const { return &_database; } +EspnowConnectionManager *EspnowMeshBackend::getConnectionManager() { return &_connectionManager; } +const EspnowConnectionManager *EspnowMeshBackend::getConnectionManagerConst() const { return &_connectionManager; } +EspnowTransmitter *EspnowMeshBackend::getTransmitter() { return &_transmitter; } +const EspnowTransmitter *EspnowMeshBackend::getTransmitterConst() const { return &_transmitter; } +EspnowEncryptionBroker *EspnowMeshBackend::getEncryptionBroker() { return &_encryptionBroker; } +const EspnowEncryptionBroker *EspnowMeshBackend::getEncryptionBrokerConst() const { return &_encryptionBroker; } + +void EspnowMeshBackend::sendStoredEspnowMessages(const ExpiringTimeTracker *estimatedMaxDurationTracker) +{ + EspnowEncryptionBroker::sendPeerRequestConfirmations(estimatedMaxDurationTracker); + + if(estimatedMaxDurationTracker && estimatedMaxDurationTracker->expired()) + return; + + EspnowTransmitter::sendEspnowResponses(estimatedMaxDurationTracker); +} + +uint32_t EspnowMeshBackend::getMaxMessageBytesPerTransmission() +{ + return EspnowProtocolInterpreter::getMaxMessageBytesPerTransmission(); +} + +void EspnowMeshBackend::setMaxTransmissionsPerMessage(const uint8_t maxTransmissionsPerMessage) +{ + EspnowTransmitter::setMaxTransmissionsPerMessage(maxTransmissionsPerMessage); +} + +uint8_t EspnowMeshBackend::getMaxTransmissionsPerMessage() {return EspnowTransmitter::getMaxTransmissionsPerMessage();} + +uint32_t EspnowMeshBackend::getMaxMessageLength() +{ + return EspnowTransmitter::getMaxMessageLength(); +} + +void EspnowMeshBackend::setVerboseModeState(const bool enabled) {(*getConditionalPrinter()).setVerboseModeState(enabled); ConditionalPrinter::setStaticVerboseModeState(enabled);} +bool EspnowMeshBackend::verboseMode() const {return ConditionalPrinter::staticVerboseMode();} + +void EspnowMeshBackend::verboseModePrint(const String &stringToPrint, const bool newline) const +{ + (*getConditionalPrinterConst()).verboseModePrint(stringToPrint, newline); +} + +bool EspnowMeshBackend::staticVerboseMode() {return ConditionalPrinter::staticVerboseMode();} +void EspnowMeshBackend::staticVerboseModePrint(const String &stringToPrint, const bool newline) +{ + ConditionalPrinter::staticVerboseModePrint(stringToPrint, newline); +} + +uint8_t EspnowMeshBackend::numberOfEncryptedConnections() +{ + return EspnowConnectionManager::numberOfEncryptedConnections(); +} + +ConnectionType EspnowMeshBackend::getConnectionInfo(uint8_t *peerMac, uint32_t *remainingDuration) +{ + return EspnowConnectionManager::getConnectionInfo(peerMac, remainingDuration); +} + +ConnectionType EspnowMeshBackend::getConnectionInfo(const uint32_t connectionIndex, uint32_t *remainingDuration, uint8_t *peerMac) +{ + return EspnowConnectionManager::getConnectionInfo(connectionIndex, remainingDuration, peerMac); +} + +double EspnowMeshBackend::getTransmissionFailRate() +{ + return EspnowTransmitter::getTransmissionFailRate(); +} + +void EspnowMeshBackend::resetTransmissionFailRate() +{ + EspnowTransmitter::resetTransmissionFailRate(); +} + +String EspnowMeshBackend::serializeUnencryptedConnection() +{ + return EspnowConnectionManager::serializeUnencryptedConnection(); +} + +String EspnowMeshBackend::serializeEncryptedConnection(const uint8_t *peerMac) +{ + return EspnowConnectionManager::serializeEncryptedConnection(peerMac); +} + +String EspnowMeshBackend::serializeEncryptedConnection(const uint32_t connectionIndex) +{ + return EspnowConnectionManager::serializeEncryptedConnection(connectionIndex); +} + +void EspnowMeshBackend::setWiFiChannel(const uint8 newWiFiChannel) +{ + MeshBackendBase::setWiFiChannel(newWiFiChannel); + _database.setWiFiChannel(newWiFiChannel); +} diff --git a/libraries/ESP8266WiFiMesh/src/EspnowMeshBackend.h b/libraries/ESP8266WiFiMesh/src/EspnowMeshBackend.h new file mode 100644 index 0000000000..08a99673bc --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/EspnowMeshBackend.h @@ -0,0 +1,1015 @@ +/* + EspnowMeshBackend + + Copyright (C) 2019 Anders Löfgren + + 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 +*/ + +// ESP-NOW is faster for small data payloads (up to a few kB, split over multiple messages). Transfer of up to 234 bytes takes 4 ms. +// In general ESP-NOW transfer time can be approximated with the following function: transferTime = ceil(bytesToTransfer / 234.0)*3 ms. +// If you only transfer 234 bytes at a time, this adds up to around 56kB/s. Finally a chance to relive the glory of the olden days +// when people were restricted to V90 dial-up modems for internet access! +// TCP-IP takes longer to connect (around 1000 ms), and an AP has to disconnect all connected stations in order to transfer data to another AP, +// but this backend has a much higher data transfer speed than ESP-NOW once connected (100x faster or so). + +/** + * This ESP-NOW framework uses a few different message types to enable easier interpretation of transmissions. + * The message type is stored in the first transmission byte, see EspnowProtocolInterpreter.h for more detailed information on the protocol. + * Available message types are 'Q' for question (request), 'A' for answer (response), + * 'B' for broadcast, 'S' for synchronization request, 'P' for peer request and 'C' for peer request confirmation. + * + * 'B', 'Q' and 'A' are the message types that are assigned to data transmitted by the user. + * 'S', 'P' and 'C' are used only for internal framework transmissions. + * + * Messages with type 'B' are only used for broadcasts. They cannot be encrypted. + * + * Messages with type 'Q' are used for requests sent by the user. They can be encrypted. + * + * Messages with type 'A' are used for responses given by the user when 'B' or 'Q' messages have been received. They can be encrypted. + * + * Messages with type 'P' and 'C' are used exclusively for automatically pairing two ESP-NOW nodes to each other. + * This enables flexible easy-to-use encrypted ESP-NOW communication. 'P' and 'C' messages can be encrypted. + * The encryption pairing process works as follows (from top to bottom): + * + * Encrypted connection pairing process, schematic overview: + * + * Connection | Peer sends ('C'): | Peer requester sends ('P'): | Connection + * encrypted: | | | encrypted: + * | | Peer request + Nonce + HMAC | + * | StaMac + Nonce + HMAC | | + * | | Ack | + * X | SessionKeys + Nonce + Password | | X + * X | | Ack | X + * X | | SessionKey | X + * X | Ack | | X + * | | | + * + * + * The ESP-NOW CCMP encryption should have replay attack protection built in, + * but since there is no official documentation from Espressif about this a 128 bit random nonce is included in encrypted connection requests. + * + * Messages with type 'S' are used exclusively when we try to send an encrypted 'R' or 'P' transmission and the last such transmission we tried failed to receive an ack. + * Since we then do not know if the receiving node has incremented its corresponding session key or not, we first send an 'S' request to make sure the key is incremented. + * Once we get an ack for our 'S' request we send the new encrypted 'R' or 'P' transmission. 'S' messages are always encrypted. + * + * Messages of type 'A' and 'C' are response types, and thus use the same session key as the corresponding 'R' and 'P' message they are responding to. + * This means they can never cause a desynchronization to occur, and therefore they do not trigger 'S' messages. + * + * In addition to using encrypted ESP-NOW connections the framework can also send automatically encrypted messages (AEAD) over both encrypted and unencrypted connections. + * Using AEAD will only encrypt the message content, not the transmission metadata. + * The AEAD encryption does not require any pairing, and is thus faster for single messages than establishing a new encrypted connection before transfer. + * AEAD encryption also works with ESP-NOW broadcasts and supports an unlimited number of nodes, which is not true for encrypted connections. + * Encrypted ESP-NOW connections do however come with built in replay attack protection, which is not provided by the framework when using AEAD encryption, + * and allow EspnowProtocolInterpreter::aeadMetadataSize extra message bytes per transmission. + * Transmissions via encrypted connections are also slightly faster than via AEAD once a connection has been established. + */ + +#ifndef __ESPNOWMESHBACKEND_H__ +#define __ESPNOWMESHBACKEND_H__ + +#include "EspnowDatabase.h" +#include "EspnowConnectionManager.h" +#include "EspnowTransmitter.h" +#include "EspnowEncryptionBroker.h" +#include "MeshBackendBase.h" +#include "EspnowProtocolInterpreter.h" +#include "EncryptedConnectionLog.h" +#include "PeerRequestLog.h" +#include "RequestData.h" +#include "ResponseData.h" +#include "MessageData.h" +#include +#include +#include "EspnowNetworkInfo.h" + +/** + * An alternative to standard delay(). Will continuously call performEspnowMaintenance() during the waiting time, so that the ESP-NOW node remains responsive. + * Note that if there is a lot of ESP-NOW transmission activity to the node during the espnowDelay, the desired duration may be overshot by several ms. + * Thus, if precise timing is required, use standard delay() instead. + * + * Should not be used inside responseHandler, requestHandler, networkFilter or broadcastFilter callbacks since performEspnowMaintenance() can alter the ESP-NOW state. + * + * @param durationMs The shortest allowed delay duration, in milliseconds. + */ +void espnowDelay(const uint32_t durationMs); + +class EspnowMeshBackend : public MeshBackendBase { + +public: + + using broadcastFilterType = std::function; + + /** + * ESP-NOW constructor method. Creates an ESP-NOW node, ready to be initialised. + * + * @param requestHandler The callback handler for dealing with received requests. Takes a string as an argument which + * is the request string received from another node and returns the string to send back. + * @param responseHandler The callback handler for dealing with received responses. Takes a string as an argument which + * is the response string received from another node. Returns a transmission status code as a TransmissionStatusType. + * @param networkFilter The callback handler for deciding which WiFi networks to connect to. + * @param broadcastFilter The callback handler for deciding which ESP-NOW broadcasts to accept. + * @param meshPassword The WiFi password for the mesh network. + * @param espnowEncryptedConnectionKey An uint8_t array containing the secret key used by this EspnowMeshBackend instance for creating encrypted ESP-NOW connections. + * @param espnowHashKey An uint8_t array containing the secret key used by this EspnowMeshBackend to generate HMACs for encrypted ESP-NOW connections. + * @param ssidPrefix The prefix (first part) of the node SSID. + * @param ssidSuffix The suffix (last part) of the node SSID. + * @param verboseMode Determines if we should print the events occurring in the library to Serial. Off by default. This setting is shared by all EspnowMeshBackend instances. + * @param meshWiFiChannel The WiFi channel used by the mesh network. Valid values are integers from 1 to 13. Defaults to 1. + * WARNING: The ESP8266 has only one WiFi channel, and the station/client mode is always prioritized for channel selection. + * This can cause problems if several mesh instances exist on the same ESP8266 and use different WiFi channels. + * In such a case, whenever the station of one mesh instance connects to an AP, it will silently force the + * WiFi channel of any active AP on the ESP8266 to match that of the station. This will cause disconnects and possibly + * make it impossible for other stations to detect the APs whose WiFi channels have changed. + * + */ + EspnowMeshBackend(const requestHandlerType requestHandler, const responseHandlerType responseHandler, const networkFilterType networkFilter, const broadcastFilterType broadcastFilter, + const String &meshPassword, const uint8_t espnowEncryptedConnectionKey[EspnowProtocolInterpreter::encryptedConnectionKeyLength], + const uint8_t espnowHashKey[EspnowProtocolInterpreter::hashKeyLength], const String &ssidPrefix, + const String &ssidSuffix, const bool verboseMode = false, const uint8 meshWiFiChannel = 1); + + /** + * ESP-NOW constructor method. Creates an ESP-NOW node, ready to be initialised. + * + * @param requestHandler The callback handler for dealing with received requests. Takes a string as an argument which + * is the request string received from another node and returns the string to send back. + * @param responseHandler The callback handler for dealing with received responses. Takes a string as an argument which + * is the response string received from another node. Returns a transmission status code as a TransmissionStatusType. + * @param networkFilter The callback handler for deciding which WiFi networks to connect to. + * @param broadcastFilter The callback handler for deciding which ESP-NOW broadcasts to accept. + * @param meshPassword The WiFi password for the mesh network. + * @param espnowEncryptedConnectionKeySeed A string containing the seed that will generate the secret key used by this EspnowMeshBackend instance for creating encrypted ESP-NOW connections. + * @param espnowHashKeySeed A string containing the seed that will generate the secret key used by this EspnowMeshBackend to generate HMACs for encrypted ESP-NOW connections. + * @param ssidPrefix The prefix (first part) of the node SSID. + * @param ssidSuffix The suffix (last part) of the node SSID. + * @param verboseMode Determines if we should print the events occurring in the library to Serial. Off by default. This setting is shared by all EspnowMeshBackend instances. + * @param meshWiFiChannel The WiFi channel used by the mesh network. Valid values are integers from 1 to 13. Defaults to 1. + * WARNING: The ESP8266 has only one WiFi channel, and the station/client mode is always prioritized for channel selection. + * This can cause problems if several mesh instances exist on the same ESP8266 and use different WiFi channels. + * In such a case, whenever the station of one mesh instance connects to an AP, it will silently force the + * WiFi channel of any active AP on the ESP8266 to match that of the station. This will cause disconnects and possibly + * make it impossible for other stations to detect the APs whose WiFi channels have changed. + * + */ + EspnowMeshBackend(const requestHandlerType requestHandler, const responseHandlerType responseHandler, const networkFilterType networkFilter, const broadcastFilterType broadcastFilter, + const String &meshPassword, const String &espnowEncryptedConnectionKeySeed, const String &espnowHashKeySeed, const String &ssidPrefix, + const String &ssidSuffix, const bool verboseMode = false, const uint8 meshWiFiChannel = 1); + + ~EspnowMeshBackend() override; + + /** + * Returns a vector that contains the NetworkInfo for each WiFi network to connect to. + * This vector is unique for each mesh backend, but NetworkInfo elements can be directly transferred between the vectors as long as both SSID and BSSID are present. + * The connectionQueue vector is cleared before each new scan and filled via the networkFilter callback function once the scan completes. + * WiFi connections will start with connectionQueue[0] and then incrementally proceed to higher vector positions. + * Note that old network indices often are invalidated whenever a new WiFi network scan occurs. + * + * Since the connectionQueue() is iterated over during transmissions, always use constConnectionQueue() from callbacks other than NetworkFilter. + */ + static std::vector & connectionQueue(); + + /** + * Same as connectionQueue(), but can be called from all callbacks since the returned reference is const. + */ + static const std::vector & constConnectionQueue(); + + /** + * Returns a vector with the TransmissionOutcome for each AP to which a transmission was attempted during the latest attemptTransmission call. + * This vector is unique for each mesh backend. + * The latestTransmissionOutcomes vector is cleared before each new transmission attempt. + * Connection attempts are indexed in the same order they were attempted. + * Note that old network indices often are invalidated whenever a new WiFi network scan occurs. + */ + static std::vector & latestTransmissionOutcomes(); + + /** + * @return True if latest transmission was successful (i.e. latestTransmissionOutcomes is not empty and all entries have transmissionStatus TransmissionStatusType::TRANSMISSION_COMPLETE). False otherwise. + * The result is unique for each mesh backend. + */ + static bool latestTransmissionSuccessful(); + + /** + * Initialises the node. + */ + void begin() override; + + /** + * This method performs all the background operations for the EspnowMeshBackend. + * It is recommended to place it in the beginning of the loop(), unless there is a need to put it elsewhere. + * Among other things, the method cleans up old Espnow log entries (freeing up RAM) and sends the responses you provide to Espnow requests. + * Note that depending on the amount of responses to send and their length, this method can take tens or even hundreds of milliseconds to complete. + * More intense transmission activity and less frequent calls to performEspnowMaintenance will likely cause the method to take longer to complete, so plan accordingly. + * + * Should not be used inside responseHandler, requestHandler, networkFilter or broadcastFilter callbacks since performEspnowMaintenance() can alter the ESP-NOW state. + * + * @param estimatedMaxDuration The desired max duration for the method. If set to 0 there is no duration limit. + * Note that setting the estimatedMaxDuration too low may result in missed ESP-NOW transmissions because of too little time for maintenance. + * Also note that although the method will try to respect the max duration limit, there is no guarantee. Overshoots by tens of milliseconds are possible. + */ + static void performEspnowMaintenance(const uint32_t estimatedMaxDuration = 0); + + /** + * At critical heap level no more incoming requests are accepted. + */ + static uint32_t criticalHeapLevel(); + + /** + * At critical heap level no more incoming requests are accepted. + * This method sets the maximum number of bytes above the critical heap level that will trigger an early ESP-NOW log clearing in an attempt to increase available heap size. + * A too high value may cause very frequent early log clearings, which will slow things down. Especially if you are using a lot of heap in other parts of your program. + * A too low value may cause some incoming requests to be lost and/or an increase in heap fragmentation, + * especially if you quickly fill the heap by receiving a lot of large ESP-NOW messages or sending a lot of large ESP-NOW responses. + * The buffer is set to 6000 bytes by default, which should be enough to prevent lost incoming requests while giving plenty of heap to fill up before early clearing in most circumstances. + * + * The buffer can be set lower than the default if you are running low on heap, since it may otherwise be hard to get responses sent. + * However, lower values tend to result in more heap fragmentation during intense transmission activity. + * Depending on your situation (message size, transmission frequency), values below 2000-3000 bytes will also start to cause lost incoming requests due to heap shortage. + * + * If the buffer is set to 0 bytes a significant number of incoming requests are likely to be lost during intense transmission activity, + * and there is a greater risk of heap space completely running out before log clearing occurs (which may cause crashes or empty transmissions). + */ + static void setCriticalHeapLevelBuffer(const uint32_t bufferInBytes); + static uint32_t criticalHeapLevelBuffer(); + + /** + * Deactivates Espnow for this node. Call begin() on a EspnowMeshBackend instance to reactivate Espnow. + * + * @return True if deactivation was successful. False otherwise. + */ + static bool deactivateEspnow(); + + void attemptTransmission(const String &message, const bool scan = true, const bool scanAllWiFiChannels = false) override; + + /** + * Transmit message to a single recipient without changing the local transmission state. + * Will not change connectionQueue, latestTransmissionOutcomes or stored message. + * + * @param recipientInfo The recipient information. + */ + TransmissionStatusType attemptTransmission(const String &message, const EspnowNetworkInfo &recipientInfo); + + /* + * Will ensure that an encrypted connection exists to each target node before sending the message, + * establishing a temporary encrypted connection with duration getAutoEncryptionDuration() first if necessary. + * If an encrypted connection cannot be established to a target node, no message will be sent to that node. + * Note that if an encrypted connection to a target node is not present before this method is called, the response from said node will likely not be received + * since it will be encrypted and the auto encrypted connection to the node is immediately removed after transmission (unless the requestPermanentConnections argument is set to true). + * Also note that if a temporary encrypted connection already exists to a target node, this method will slightly extend the connection duration + * depending on the time it takes to verify the connection to the node. This can substantially increase the connection duration if many auto encrypting + * transmissions occurs. + * + * @param message The message to send to other nodes. It will be stored in the class instance until replaced via attemptTransmission or setMessage. + * @param requestPermanentConnections If true, the method will request that encrypted connections used for this transmission become permanent so they are not removed once the transmission is complete. + * This means that encrypted responses to the transmission are received, as long as the encrypted connection is not removed by other means. + * The receiving node has no obligation to obey the request, although it normally will. + * If encryptedConnectionsSoftLimit() is set to less than 6 for the transmission receiver, + * it is possible that a short lived autoEncryptionConnection is created instead of a permanent encrypted connection. + * Note that a maximum of 6 encrypted ESP-NOW connections can be maintained at the same time by the node. + * Defaults to false. + * @param scan Scan for new networks and call the networkFilter function with the scan results. When set to false, only the data already in connectionQueue will be used for the transmission. + * @param scanAllWiFiChannels Scan all WiFi channels during a WiFi scan, instead of just the channel the MeshBackendBase instance is using. + * Scanning all WiFi channels takes about 2100 ms, compared to just 60 ms if only channel 1 (standard) is scanned. + * Note that if the ESP8266 has an active AP, that AP will switch WiFi channel to match that of any other AP the ESP8266 connects to. + * This can make it impossible for other nodes to detect the AP if they are scanning the wrong WiFi channel. + */ + void attemptAutoEncryptingTransmission(const String &message, const bool requestPermanentConnections = false, const bool scan = true, const bool scanAllWiFiChannels = false); + + /** + * Transmit message to a single recipient without changing the local transmission state (apart from encrypted connections). + * Will not change connectionQueue, latestTransmissionOutcomes or stored message. + */ + TransmissionStatusType attemptAutoEncryptingTransmission(const String &message, const EspnowNetworkInfo &recipientInfo, const bool requestPermanentConnection = false); + + /** + * Send a message simultaneously to all nearby nodes which have ESP-NOW activated. + * A broadcast is always treated as a request by the receiving node. + * There is no limit to the number of responses a node can get when sending a broadcast, it will always accept new responses until the broadcastResponseTimeout is reached. + * This also means that the broadcaster can receive duplicate responses from the same node if transmission conditions are poor and an ack is lost. + * A broadcast can never be sent encrypted. + * + * Note that the node needs to have its AP active to be able to receive broadcasts. Nodes can send broadcasts even if their AP is off. + * + * @param message The message to send to the other nodes. Unlike the attemptTransmission method, the message will not be stored in the class instance, since there is no certain way to change the message during an ongoing broadcast. + */ + void broadcast(const String &message); + + /** + * Set the number of redundant transmissions that will be made for every broadcast. + * A greater number increases the likelihood that the broadcast is received, but also means it takes longer time to send. + * + * @param redundancy The number of extra transmissions to make of each broadcast. Defaults to 1. + */ + void setBroadcastTransmissionRedundancy(const uint8_t redundancy); + uint8_t getBroadcastTransmissionRedundancy() const; + + /** + * Set the EspnowMeshBackend instance responsible for handling incoming requests. The requestHandler of the instance will be called upon receiving ESP-NOW requests. + * + * Set to nullptr to stop processing the ESP-NOW requests received by this node (requests will be ignored, but still received (ack will be sent)). + * The node can still send ESP-NOW transmissions to other nodes, even when the espnowRequestManager is nullptr. + */ + static void setEspnowRequestManager(EspnowMeshBackend *espnowMeshInstance); + + static EspnowMeshBackend *getEspnowRequestManager(); + + /** + * Check if this EspnowMeshBackend instance is the espnowRequestManager. + * + * @return True if this EspnowMeshBackend is the espnowRequestManager. False otherwise. + */ + bool isEspnowRequestManager() const; + + /** + * Set the duration of most ESP-NOW log entries. Used for all ESP-NOW communication except for broadcasts and encrypted connection requests. + * Setting the duration too long may cause the node to run out of RAM, especially if there is intense transmission activity. + * Setting the duration too short may cause ESP-NOW transmissions to stop working, or make the node receive the same transmission multiple times. + * + * Set to 2500 ms by default. + * + * @param logEntryLifetimeMs The duration to use for most ESP-NOW log entries, in milliseconds. + */ + static void setLogEntryLifetimeMs(const uint32_t logEntryLifetimeMs); + static uint32_t logEntryLifetimeMs(); + + /** + * Set the duration during which sent ESP-NOW broadcast are stored in the log and can receive responses. + * This is shorter by default than logEntryLifetimeMs() in order to preserve RAM since broadcasts are always kept in the log until they expire, + * whereas normal transmissions are only kept till they receive a response. + * Setting the duration too long may cause the node to run out of RAM, especially if there is intense broadcast activity. + * Setting the duration too short may cause ESP-NOW broadcasts to stop working, or make the node never receive responses to broadcasts. + * + * Set to 1000 ms by default. + * + * @param broadcastResponseTimeoutMs The duration sent ESP-NOW broadcasts will be stored in the log, in milliseconds. + */ + static void setBroadcastResponseTimeoutMs(const uint32_t broadcastResponseTimeoutMs); + static uint32_t broadcastResponseTimeoutMs(); + + /** + * Change the key used by this EspnowMeshBackend instance for creating encrypted ESP-NOW connections. + * Will apply to any new received requests for encrypted connection if this EspnowMeshBackend instance is the current request manager. + * Will apply to any new encrypted connections requested or added by this EspnowMeshBackend instance. + * + * NOTE: Encrypted connections added before the encryption key change will retain their old encryption key. + * Only changes the encryption key used by this EspnowMeshBackend instance, so each instance can use a separate key. + * Both Kok and encrypted connection key must match in an encrypted connection pair for encrypted communication to be possible. + * Otherwise the transmissions will never reach the recipient, even though acks are received by the sender. + * + * @param espnowEncryptedConnectionKey An array containing the encryptedConnectionKeyLength bytes that will be used as the encryption key. + */ + void setEspnowEncryptedConnectionKey(const uint8_t espnowEncryptedConnectionKey[EspnowProtocolInterpreter::encryptedConnectionKeyLength]); + + /** + * Change the key used by this EspnowMeshBackend instance for creating encrypted ESP-NOW connections. + * Will apply to any new received requests for encrypted connection if this EspnowMeshBackend instance is the current request manager. + * Will apply to any new encrypted connections requested or added by this EspnowMeshBackend instance. + * + * NOTE: Encrypted connections added before the encryption key change will retain their old encryption key. + * Only changes the encryption key used by this EspnowMeshBackend instance, so each instance can use a separate key. + * Both Kok and encrypted connection key must match in an encrypted connection pair for encrypted communication to be possible. + * Otherwise the transmissions will never reach the recipient, even though acks are received by the sender. + * + * @param espnowHashKeySeed A string that will be used to generate the encryption key. The same string will always generate the same key. + * A minimum of 8 random characters are recommended to ensure sufficient key variation. + */ + void setEspnowEncryptedConnectionKey(const String &espnowEncryptedConnectionKeySeed); + + /** + * Get the encryption key used by this EspnowMeshBackend instance for creating encrypted ESP-NOW connections. + * + * @return The current espnowEncryptedConnectionKey for this EspnowMeshBackend instance. + */ + const uint8_t *getEspnowEncryptedConnectionKey() const; + uint8_t *getEspnowEncryptedConnectionKey(uint8_t resultArray[EspnowProtocolInterpreter::encryptedConnectionKeyLength]) const; + + /** + * Change the key used to encrypt/decrypt the encrypted connection key when creating encrypted ESP-NOW connections. (Kok = key of keys, perhaps) If no Kok is provided by the user, a default Kok is used. + * Will apply to any new encrypted connections. + * Must be called after begin() to take effect. + * + * NOTE: Encrypted connections added before the Kok change will retain their old Kok. + * This changes the Kok for all EspnowMeshBackend instances on this ESP8266. + * Both Kok and encrypted connection key must match in an encrypted connection pair for encrypted communication to be possible. + * Otherwise the transmissions will never reach the recipient, even though acks are received by the sender. + * + * @param espnowEncryptionKok An array containing the encryptedConnectionKeyLength bytes that will be used as the Kok. + * @return True if Kok was changed successfully. False if Kok was not changed. + */ + static bool setEspnowEncryptionKok(uint8_t espnowEncryptionKok[EspnowProtocolInterpreter::encryptedConnectionKeyLength]); + + /** + * Change the key used to encrypt/decrypt the encryption key when creating encrypted ESP-NOW connections. (Kok = key of keys, perhaps) If no Kok is provided by the user, a default Kok is used. + * Will apply to any new encrypted connections. + * Must be called after begin() to take effect. + * + * NOTE: Encrypted connections added before the Kok change will retain their old Kok. + * This changes the Kok for all EspnowMeshBackend instances on this ESP8266. + * Both Kok and encrypted connection key must match in an encrypted connection pair for encrypted communication to be possible. + * Otherwise the transmissions will never reach the recipient, even though acks are received by the sender. + * + * @param espnowEncryptionKokSeed A string that will be used to generate the KoK. The same string will always generate the same KoK. + * A minimum of 8 random characters are recommended to ensure sufficient KoK variation. + * @return True if Kok was changed successfully. False if Kok was not changed. + */ + static bool setEspnowEncryptionKok(const String &espnowEncryptionKokSeed); + + /** + * Get the key used to encrypt the encryption keys when creating encrypted ESP-NOW connections. (Kok = key of keys, perhaps) Returns nullptr if no Kok has been provided by the user. + * + * @return nullptr if default Kok is used, or current espnowEncryptionKok if a custom Kok has been set via the setEspnowEncryptionKok method. + */ + static const uint8_t *getEspnowEncryptionKok(); + + /** + * Change the secret key used to generate HMACs for encrypted ESP-NOW connections. + * Will apply to any new received requests for encrypted connection if this EspnowMeshBackend instance is the current request manager. + * Will apply to any new encrypted connections requested or added by this EspnowMeshBackend instance. + * + * NOTE: Encrypted connections added before the key change will retain their old key. + * Only changes the secret hash key used by this EspnowMeshBackend instance, so each instance can use a separate secret key. + * + * @param espnowHashKey An array containing the hashKeyLength bytes that will be used as the HMAC key. + */ + void setEspnowHashKey(const uint8_t espnowHashKey[EspnowProtocolInterpreter::hashKeyLength]); + + /** + * Change the secret key used to generate HMACs for encrypted ESP-NOW connections. + * Will apply to any new received requests for encrypted connection if this EspnowMeshBackend instance is the current request manager. + * Will apply to any new encrypted connections requested or added by this EspnowMeshBackend instance. + * + * NOTE: Encrypted connections added before the key change will retain their old key. + * Only changes the secret hash key used by this EspnowMeshBackend instance, so each instance can use a separate secret key. + * + * @param espnowHashKeySeed A string that will be used to generate the HMAC key. The same string will always generate the same key. + * A minimum of 8 random characters are recommended to ensure sufficient key variation. + */ + void setEspnowHashKey(const String &espnowHashKeySeed); + + const uint8_t *getEspnowHashKey() const; + + /** + * If true, AEAD will be used to encrypt/decrypt all messages sent/received by this node via ESP-NOW, regardless of whether the connection is encrypted or not. + * All nodes this node wishes to communicate with must then also use encrypted messages with the same getEspnowMessageEncryptionKey(), or messages will not be accepted. + * Note that using encrypted messages will reduce the number of message bytes that can be transmitted. + * + * Using AEAD will only encrypt the message content, not the transmission metadata. + * The AEAD encryption does not require any pairing, and is thus faster for single messages than establishing a new encrypted connection before transfer. + * AEAD encryption also works with ESP-NOW broadcasts and supports an unlimited number of nodes, which is not true for encrypted connections. + * Encrypted ESP-NOW connections do however come with built in replay attack protection, which is not provided by the framework when using AEAD encryption, + * and allow EspnowProtocolInterpreter::aeadMetadataSize extra message bytes per transmission. + * Transmissions via encrypted connections are also slightly faster than via AEAD once a connection has been established. + * + * useEncryptedMessages() is false by default. + * + * @param useEncryptedMessages If true, AEAD encryption/decryption is enabled. If false, AEAD encryption/decryption is disabled. + */ + static void setUseEncryptedMessages(const bool useEncryptedMessages); + static bool useEncryptedMessages(); + + /** + * Change the key used to encrypt/decrypt messages when using AEAD encryption. + * If no message encryption key is provided by the user, a default key consisting of all zeroes is used. + * + * This changes the message encryption key for all EspnowMeshBackend instances on this ESP8266. + * + * @param espnowMessageEncryptionKey An array containing the experimental::crypto::ENCRYPTION_KEY_LENGTH bytes that will be used as the message encryption key. + */ + static void setEspnowMessageEncryptionKey(const uint8_t espnowMessageEncryptionKey[experimental::crypto::ENCRYPTION_KEY_LENGTH]); + + /** + * Change the key used to encrypt/decrypt messages when using AEAD encryption. + * If no message encryption key is provided by the user, a default key consisting of all zeroes is used. + * + * This changes the message encryption key for all EspnowMeshBackend instances on this ESP8266. + * + * @param espnowMessageEncryptionKeySeed A string that will be used to generate the message encryption key. The same string will always generate the same key. + * A minimum of 8 random characters are recommended to ensure sufficient key variation. + */ + static void setEspnowMessageEncryptionKey(const String &espnowMessageEncryptionKeySeed); + + /** + * Get the key used to encrypt/decrypt messages when using AEAD encryption. + * + * @return An uint8_t array with size experimental::crypto::ENCRYPTION_KEY_LENGTH containing the currently used message encryption key. + */ + static const uint8_t *getEspnowMessageEncryptionKey(); + + /** + * Hint: Use String.length() to get the ASCII length of a String. + * + * @return The maximum number of bytes (or ASCII characters) a transmission can contain. + * Note that non-ASCII characters usually require the space of at least two ASCII characters each. + * Also note that this value will be reduced by EspnowProtocolInterpreter::aeadMetadataSize if useEncryptedMessages() is true. + */ + static uint32_t getMaxMessageBytesPerTransmission(); + + /** + * Set the maximum acceptable message length, in terms of transmissions, when sending a message from this node. + * This has no effect when receiving messages, the limit for receiving is always 256 transmissions per message. + * Note that although values up to 128 are possible, this would in practice fill almost all the RAM available on the ESP8266 with just one message. + * Thus, if this value is set higher than the default, make sure there is enough heap available to store the messages + * and don't send messages more frequently than they can be processed. + * Also note that a higher value will make the node less responsive as it will be spending a long time transmitting. + * + * Typical symptoms of running out of heap are crashes and messages that become empty even though they shouldn't be. Keep this in mind if going beyond the default. + * + * @param maxTransmissionsPerMessage The maximum acceptable message length, in terms of transmissions, when sending a message from this node. Valid values are 1 to 128. Defaults to 3. + */ + static void setMaxTransmissionsPerMessage(const uint8_t maxTransmissionsPerMessage); + static uint8_t getMaxTransmissionsPerMessage(); + + /** + * Hint: Use String.length() to get the ASCII length of a String. + * + * @return The maximum length in bytes an ASCII message is allowed to be when transmitted/broadcasted by this node. + * Note that non-ASCII characters usually require at least two bytes each. + * Also note that this value will be reduced if useEncryptedMessages() is true. + */ + static uint32_t getMaxMessageLength(); + + /** + * Set whether the normal events occurring in the library will be printed to Serial or not. Off by default. + * This setting is shared by all EspnowMeshBackend instances. + * + * @param enabled If true, library Serial prints are activated. + */ + void setVerboseModeState(const bool enabled) override; + bool verboseMode() const override; + + /** + * Only print stringToPrint if verboseMode() returns true. + * + * @param stringToPrint String to print. + * @param newline If true, will end the print with a newline. True by default. + */ + void verboseModePrint(const String &stringToPrint, const bool newline = true) const override; + + /** + * Same as verboseMode(), but used for printing from static functions. + * + * @return True if the normal events occurring in the library will be printed to Serial. False otherwise. + */ + static bool staticVerboseMode(); + + /** + * Only print stringToPrint if staticVerboseMode() returns true. + * + * @param stringToPrint String to print. + * @param newline If true, will end the print with a newline. True by default. + */ + static void staticVerboseModePrint(const String &stringToPrint, const bool newline = true); + + /** + * Get the message of the response at responseIndex among the responses that are scheduled for transmission from this node. + * + * @param responseIndex The index of the response. Must be lower than numberOfScheduledResponses(). + * @return A String containing the message of the response at responseIndex. + */ + static String getScheduledResponseMessage(const uint32_t responseIndex); + + /** + * Get the MAC address for the recipient of the response at responseIndex among the responses that are scheduled for transmission from this node. + * + * @param responseIndex The index of the response. Must be lower than numberOfScheduledResponses(). + * @return An array with six bytes containing the MAC address for the recipient of the response at responseIndex. + */ + static const uint8_t *getScheduledResponseRecipient(const uint32_t responseIndex); + + /** + * Get the number of ESP-NOW responses that are scheduled for transmission from this node. + * + * @return The number of ESP-NOW responses scheduled for transmission. + */ + static uint32_t numberOfScheduledResponses(); + + /** + * Remove all responses that have been scheduled for transmission but are not yet transmitted. + * Note that cleared responses will not be received by their recipient. + */ + static void clearAllScheduledResponses(); + + /** + * Remove all responses targeting recipientMac that have been scheduled for transmission but are not yet transmitted. + * Optionally deletes only responses to encrypted requests. + * Note that deleted responses will not be received by their recipient. + * + * @param recipientMac The MAC address of the response recipient. + * @param encryptedOnly If true, only responses to encrypted requests will be deleted. + */ + static void deleteScheduledResponsesByRecipient(const uint8_t *recipientMac, const bool encryptedOnly); + + /** + * Set the timeout to use for each ESP-NOW transmission when transmitting. + * Note that for multi-part transmissions (where message length is greater than getMaxMessageBytesPerTransmission()), the timeout is reset for each transmission part. + * The default timeouts should fit most use cases, but in case you do a lot of time consuming processing when the node receives a message, you may need to relax them a bit. + * + * @param timeoutMs The timeout that should be used for each ESP-NOW transmission, in milliseconds. Defaults to 40 ms. + */ + static void setEspnowTransmissionTimeout(const uint32_t timeoutMs); + static uint32_t getEspnowTransmissionTimeout(); + + /** + * Set the time to wait for an ack after having made an ESP-NOW transmission. If no ack is received within said time, a new transmission attempt is made. + * Note that if a retransmission causes duplicate transmissions to reach the receiver, the duplicates will be detected and ignored automatically. + * The default timeouts should fit most use cases, but in case you do a lot of time consuming processing when the node receives a message, you may need to relax them a bit. + * + * @param intervalMs The time to wait for an ack after having made an ESP-NOW transmission, in milliseconds. Defaults to 15 ms. + */ + static void setEspnowRetransmissionInterval(const uint32_t intervalMs); + static uint32_t getEspnowRetransmissionInterval(); + + // The maximum amount of time each of the two stages in an encrypted connection request may take. + static void setEncryptionRequestTimeout(const uint32_t timeoutMs); + static uint32_t getEncryptionRequestTimeout(); + + void setAutoEncryptionDuration(const uint32_t duration); + uint32_t getAutoEncryptionDuration() const; + + void setBroadcastFilter(const broadcastFilterType broadcastFilter); + broadcastFilterType getBroadcastFilter() const; + + /** + * Set a function that should be called after each attempted ESP-NOW response transmission. + * In case of a successful response transmission, the call happens just before the response is removed from the waiting list. + * Only the hook of the EspnowMeshBackend instance that is getEspnowRequestManager() will be called. + * + * The hook should return a bool. + * If this return value is true, the response transmission process will continue with the next response in the waiting list. + * If it is false, the response transmission process will stop once processing of the just sent response is complete. + * The default responseTransmittedHook always returns true. + */ + void setResponseTransmittedHook(const EspnowTransmitter::responseTransmittedHookType responseTransmittedHook); + EspnowTransmitter::responseTransmittedHookType getResponseTransmittedHook() const; + + /** + * Get the MAC address of the sender of the most recently received ESP-NOW request, response or broadcast to this EspnowMeshBackend instance. + * Returns a String. + * By default the MAC will be that of the sender's station interface. The only exception is for unencrypted + * responses to requests sent to an AP interface, which will return the response sender's AP interface MAC. + * + * @return A String filled with a hexadecimal representation of the MAC, without delimiters. + */ + String getSenderMac() const; + + /** + * Get the MAC address of the sender of the most recently received ESP-NOW request, response or broadcast to this EspnowMeshBackend instance. + * Returns a uint8_t array. + * By default the MAC will be that of the sender's station interface. The only exception is for unencrypted + * responses to requests sent to an AP interface, which will return the response sender's AP interface MAC. + * + * @param macArray The array that should store the MAC address. Must be at least 6 bytes. + * @return macArray filled with the sender MAC. + */ + uint8_t *getSenderMac(uint8_t *macArray) const; + + /** + * Get the AP MAC address of the sender of the most recently received ESP-NOW request, response or broadcast to this EspnowMeshBackend instance. + * Returns a String. + * + * @return A String filled with a hexadecimal representation of the AP MAC, without delimiters. + */ + String getSenderAPMac() const; + + /** + * Get the AP MAC address of the sender of the most recently received ESP-NOW request, response or broadcast to this EspnowMeshBackend instance. + * Returns a uint8_t array. + * + * @param macArray The array that should store the MAC address. Must be at least 6 bytes. + * @return macArray filled with the sender AP MAC. + */ + uint8_t *getSenderAPMac(uint8_t *macArray) const; + + /** + * Get whether the ESP-NOW request, response or broadcast which was most recently received by this EspnowMeshBackend instance was sent over an encrypted connection or not. + * + * @return If true, the request, response or broadcast was sent over an encrypted connection. If false, the connection was unencrypted. + */ + bool receivedEncryptedTransmission() const; + + /** + * Should be used together with serializeUnencryptedConnection() if the node sends unencrypted transmissions + * and will go to sleep for less than logEntryLifetimeMs() while other nodes stay awake. + * Otherwise the message ID will be reset after sleep, which means that the nodes that stayed awake may ignore new unencrypted transmissions until logEntryLifetimeMs() ms has passed. + * + * @param serializedConnectionState A serialized state of an unencrypted ESP-NOW connection. + * + * @return True if connection was added. False otherwise (e.g. if there is faulty input). + */ + static bool addUnencryptedConnection(const String &serializedConnectionState); + + /** + * Adds a new permanent encrypted ESP-NOW connection, or makes the duration of an existing temporary connection permanent. + * Note that this will not add an encrypted ESP-NOW connection automatically to the other node. Thus the same method will need to be called on the other node as well to establish an encrypted connection. + * Methods such as requestEncryptedConnection creates an encrypted connection automatically in both nodes, but requires information exchange between the nodes before the connection is established (and is thus much slower). + * + * When called, the method will update an existing encrypted ESP-NOW connection with the current stored encrypted connection key. (in case it has changed since the connection was established) + * + * The maximum number of simultaneous encrypted connections is restricted by the ESP-NOW API and is EspnowProtocolInterpreter::maxEncryptedConnections (6 by default). + * + * @param peerStaMac The station MAC of the other node. + * @param peerApMac The AP MAC of the other node. + * @param peerSessionKey The session key of the other node. At least one of the leftmost 32 bits should be 1, since the key otherwise indicates the connection is unencrypted. + * @param peerSessionKey The session key of this node. At least one of the leftmost 32 bits should be 1, since the key otherwise indicates the connection is unencrypted. + * + * @return EncryptedConnectionStatus::CONNECTION_ESTABLISHED if the connection was created. Otherwise another status code based on the outcome. + */ + EncryptedConnectionStatus addEncryptedConnection(uint8_t *peerStaMac, uint8_t *peerApMac, const uint64_t peerSessionKey, const uint64_t ownSessionKey); + + /** + * Create an encrypted ESP-NOW connection on this node based on the information stored in serializedConnectionState. + * Note that this will not add an encrypted ESP-NOW connection automatically to the other node. Thus the same method will need to be called on the other node as well to establish an encrypted connection. + * Methods such as requestEncryptedConnection creates an encrypted connection automatically in both nodes, but requires information exchange between the nodes before the connection is established (and is thus much slower). + * + * When called, the method will update an existing encrypted ESP-NOW connection with the current stored encrypted connection key. (in case it has changed since the connection was established) + * + * The maximum number of simultaneous encrypted connections is restricted by the ESP-NOW API and is EspnowProtocolInterpreter::maxEncryptedConnections (6 by default). + * + * Note that the espnowEncryptedConnectionKey, espnowEncryptionKok, espnowHashKey and espnowMessageEncryptionKey are not serialized. + * These will be set to the values of the EspnowMeshBackend instance that is adding the serialized encrypted connection. + * + * @param serializedConnectionState A String containing the serialized connection state. + * @param ignoreDuration Ignores any stored duration in serializedConnectionState, guaranteeing that the created connection will be permanent. + * + * @return EncryptedConnectionStatus::CONNECTION_ESTABLISHED if the connection was created. Otherwise another status code based on the outcome. EncryptedConnectionStatus::REQUEST_TRANSMISSION_FAILED indicates a malformed serializedConnectionState. + */ + EncryptedConnectionStatus addEncryptedConnection(const String &serializedConnectionState, const bool ignoreDuration = false); + + /** + * Adds a new temporary encrypted ESP-NOW connection, or changes the duration of an existing temporary connection (only updates keys, not duration, for existing permanent connections). + * Note that this will not add an encrypted ESP-NOW connection automatically to the other node. Thus the same method will need to be called on the other node as well to establish an encrypted connection. + * Methods such as requestEncryptedConnection creates an encrypted connection automatically in both nodes, but requires information exchange between the nodes before the connection is established (and is thus much slower). + * + * When called, the method will update an existing encrypted ESP-NOW connection with the current stored encrypted connection key. (in case it has changed since the connection was established) + * + * The maximum number of simultaneous encrypted connections is restricted by the ESP-NOW API and is EspnowProtocolInterpreter::maxEncryptedConnections (6 by default). + * + * As with all these methods, changes will only take effect once the requester proves it has the ability to decrypt the session key. + * + * @param peerStaMac The station MAC of the other node. + * @param peerApMac The AP MAC of the other node. + * @param peerSessionKey The session key of the other node. At least one of the leftmost 32 bits should be 1, since the key otherwise indicates the connection is unencrypted. + * @param peerSessionKey The session key of this node. At least one of the leftmost 32 bits should be 1, since the key otherwise indicates the connection is unencrypted. + * @param duration The desired duration of the connection. + * + * @return EncryptedConnectionStatus::CONNECTION_ESTABLISHED if the connection was created. Otherwise another status code based on the outcome. + */ + EncryptedConnectionStatus addTemporaryEncryptedConnection(uint8_t *peerStaMac, uint8_t *peerApMac, const uint64_t peerSessionKey, const uint64_t ownSessionKey, const uint32_t duration); + + /** + * Create a temporary encrypted ESP-NOW connection on this node based on the information stored in serializedConnectionState. + * Note that this will not add an encrypted ESP-NOW connection automatically to the other node. Thus the same method will need to be called on the other node as well to establish an encrypted connection. + * Methods such as requestEncryptedConnection creates an encrypted connection automatically in both nodes, but requires information exchange between the nodes before the connection is established (and is thus much slower). + * + * When called, the method will update an existing encrypted ESP-NOW connection with the current stored encrypted connection key. (in case it has changed since the connection was established) + * + * The maximum number of simultaneous encrypted connections is restricted by the ESP-NOW API and is EspnowProtocolInterpreter::maxEncryptedConnections (6 by default). + * + * Note that the espnowEncryptedConnectionKey, espnowEncryptionKok, espnowHashKey and espnowMessageEncryptionKey are not serialized. + * These will be set to the values of the EspnowMeshBackend instance that is adding the serialized encrypted connection. + * + * @param serializedConnectionState A String containing the serialized connection state. + * @param ignoreDuration Ignores any stored duration in serializedConnectionState, guaranteeing that the created connection will be permanent. + * @param duration The desired duration of the connection. Overrides any stored duration in the serializedConnectionState. + * + * @return EncryptedConnectionStatus::CONNECTION_ESTABLISHED if the connection was created. Otherwise another status code based on the outcome. EncryptedConnectionStatus::REQUEST_TRANSMISSION_FAILED indicates a malformed serializedConnectionState. + */ + EncryptedConnectionStatus addTemporaryEncryptedConnection(const String &serializedConnectionState, const uint32_t duration); + + /** + * Request a permanent encrypted ESP-NOW connection with the node that uses peerMac. + * If an encrypted connection to peerMac already exists, only connection duration is updated. All other settings are kept as is. Use removeEncryptedConnection/requestEncryptedConnectionRemoval first if encryption keys should be updated. + * The method makes sure both nodes have an encrypted connection to each other that's permanent. + * + * The maximum number of simultaneous encrypted connections is restricted by the ESP-NOW API and is EspnowProtocolInterpreter::maxEncryptedConnections (6 by default). + * + * @param peerMac The MAC of the other node to which the request should be sent. + * + * @return EncryptedConnectionStatus::CONNECTION_ESTABLISHED if the permanent connection was created. EncryptedConnectionStatus::SOFT_LIMIT_CONNECTION_ESTABLISHED if only a temporary soft limit connection could be established (see the setEncryptedConnectionsSoftLimit method documentation for details). Otherwise another status code based on the outcome. + */ + EncryptedConnectionStatus requestEncryptedConnection(const uint8_t *peerMac); + + /** + * Request a temporary encrypted ESP-NOW connection with the node that uses peerMac. + * If a temporary encrypted connection to peerMac already exists, only connection duration is updated. All other settings are kept as is. Permanent connections are not modified. Use removeEncryptedConnection/requestEncryptedConnectionRemoval first if encryption keys should be updated. + * The method makes sure both nodes have an encrypted connection to each other that's either permanent or has exactly the duration specified. + * + * The maximum number of simultaneous encrypted connections is restricted by the ESP-NOW API and is EspnowProtocolInterpreter::maxEncryptedConnections (6 by default). + * + * @param peerMac The MAC of the other node to which the request should be sent. + * @param durationMs The desired duration of the connection. + * + * @return EncryptedConnectionStatus::CONNECTION_ESTABLISHED if the request was successful. EncryptedConnectionStatus::SOFT_LIMIT_CONNECTION_ESTABLISHED if only a temporary soft limit connection could be established (see the setEncryptedConnectionsSoftLimit method documentation for details). Otherwise another status code based on the outcome. + */ + EncryptedConnectionStatus requestTemporaryEncryptedConnection(const uint8_t *peerMac, const uint32_t durationMs); + + /** + * Request a flexible temporary encrypted ESP-NOW connection with the node that uses peerMac. + * If a temporary encrypted connection to peerMac with a shorter duration already exists, connection duration is updated. All other settings are kept as is. Permanent connections are not modified. Use removeEncryptedConnection/requestEncryptedConnectionRemoval first if encryption keys should be updated. + * The method makes sure both nodes have an encrypted connection to each other that's either permanent or has at least the duration specified. + * + * The maximum number of simultaneous encrypted connections is restricted by the ESP-NOW API and is EspnowProtocolInterpreter::maxEncryptedConnections (6 by default). + * + * Note that if a temporary encrypted connection already exists to a target node, this method will slightly extend the connection duration + * depending on the time it takes to verify the connection to the node. + * + * @param peerMac The MAC of the other node to which the request should be sent. + * @param minDurationMs The desired minimum duration of the connection. + * + * @return EncryptedConnectionStatus::CONNECTION_ESTABLISHED if the request was successful. EncryptedConnectionStatus::SOFT_LIMIT_CONNECTION_ESTABLISHED if only a temporary soft limit connection could be established (see the setEncryptedConnectionsSoftLimit method documentation for details). Otherwise another status code based on the outcome. + */ + EncryptedConnectionStatus requestFlexibleTemporaryEncryptedConnection(const uint8_t *peerMac, const uint32_t minDurationMs); + + /** + * Remove the encrypted ESP-NOW connection to peerMac from this node. + * Note that this will not remove the encrypted ESP-NOW connection automatically from the other node. Thus the same method will need to be called on the other node as well to complete the encrypted connection removal. + * The method requestEncryptedConnectionRemoval removes the encrypted connection automatically in both nodes, but requires extra information exchange between the nodes (and is thus much slower). + * + * @param peerMac The MAC of the other node. + * + * @return EncryptedConnectionRemovalOutcome::REMOVAL_SUCCEEDED if the removal succeeded. EncryptedConnectionRemovalOutcome::REMOVAL_SCHEDULED if the removal is scheduled to occur as soon as it is safe to do so (generally as soon as an ongoing transmission is complete, or at the latest during the next performEspnowMaintenance call). Otherwise another status code based on the outcome. + */ + static EncryptedConnectionRemovalOutcome removeEncryptedConnection(const uint8_t *peerMac); + + /** + * Request the removal of the encrypted ESP-NOW connection between this node and the node that uses peerMac. + * The method makes sure both nodes remove the encrypted connection to each other. + * + * @param peerMac The MAC of the other node to which the request should be sent. + * + * @return EncryptedConnectionRemovalOutcome::REMOVAL_SUCCEEDED if the removal succeeded. Otherwise another status code based on the outcome (never REMOVAL_SCHEDULED). + */ + EncryptedConnectionRemovalOutcome requestEncryptedConnectionRemoval(const uint8_t *peerMac); + + /** + * Set whether this EspnowMeshBackend instance will accept ESP-NOW requests from unencrypted connections or not, when acting as EspnowRequestManager. + * When set to false and combined with already existing encrypted connections, this can be used to ensure only encrypted transmissions are processed. + * When set to false it will also make it impossible to send requests for encrypted connection to the node over an unencrypted connection, + * which can be useful if too many such requests could otherwise be expected. + * + * True by default. + * + * @param acceptsUnverifiedRequests If and only if true, requests from unencrypted connections will be processed when this EspnowMeshBackend instance is acting as EspnowRequestManager. + */ + void setAcceptsUnverifiedRequests(const bool acceptsUnverifiedRequests); + bool acceptsUnverifiedRequests() const; + + /** + * Set a soft upper limit on the number of encrypted connections this node can have when receiving encrypted connection requests. + * The soft limit can be used to ensure there is normally a pool of free encrypted connection slots that can be used if required. + * Each EspnowMeshBackend instance can have a separate value. The value used is that of the current EspnowRequestManager. + * The hard upper limit is 6 encrypted connections, mandated by the ESP-NOW API. + * + * Default is 6. + * + * When a request for encrypted connection is received from a node to which there is no existing permanent encrypted connection, + * and the number of encrypted connections exceeds the soft limit, + * this request will automatically be converted to an autoEncryptionRequest. + * This means it will be a temporary connection with very short duration (with default framework settings). + * + * @param softLimit The new soft limit. Valid values are 0 to 6. + */ + void setEncryptedConnectionsSoftLimit(const uint8_t softLimit); + uint8_t encryptedConnectionsSoftLimit() const; + + /** + * @return The current number of encrypted ESP-NOW connections. + */ + static uint8_t numberOfEncryptedConnections(); + + /** + * @return resultArray filled with the MAC to the encrypted interface of the node, if an encrypted connection exists. nulltpr otherwise. + */ + static uint8_t *getEncryptedMac(const uint8_t *peerMac, uint8_t *resultArray); + + /** + * Should be used together with addUnencryptedConnection if the node sends unencrypted transmissions + * and will go to sleep for less than logEntryLifetimeMs() while other nodes stay awake. + * Otherwise the message ID will be reset after sleep, which means that the nodes that stayed awake may ignore new unencrypted transmissions until logEntryLifetimeMs() ms has passed. + * + * @return The serialized state of the unencrypted ESP-NOW connection. + */ + static String serializeUnencryptedConnection(); + + /** + * Create a string containing the current state of the encrypted connection for this node. The result can be used as input to addEncryptedConnection. + * Note that transferring the serialized state over an unencrypted connection will compromise the security of the stored connection. + * Also note that this saves the current state only, so if encrypted communication between the nodes happen after this, the stored state is invalid. + * @return A String containing the serialized encrypted connection, or an empty String if there is no matching encrypted connection. + */ + static String serializeEncryptedConnection(const uint8_t *peerMac); + static String serializeEncryptedConnection(const uint32_t connectionIndex); + + /** + * Get information about any current ESP-NOW connection with another node. + * + * @param peerMac The node MAC for which to get information. Both MAC for AP interface and MAC for STA interface can be used (and will yield the same result). + * Use the getEncryptedMac method or the indexed based getConnectionInfo if there is a need to find the actual encrypted interface. + * @param remainingDuration An optional pointer to a uint32_t variable. + * If supplied and the connection type is ConnectionType::TEMPORARY_CONNECTION the variable will be set to the remaining duration of the connection. + * Otherwise the variable value is not modified. + * @return The ConnectionType of the connection with peerMac. + */ + static ConnectionType getConnectionInfo(uint8_t *peerMac, uint32_t *remainingDuration = nullptr); + + /** + * Get information about any current ESP-NOW connection with another node. + * + * @param connectionIndex The connection index of the node for which to get information. Valid values are limited by numberOfEncryptedConnections(). + * @param remainingDuration An optional pointer to a uint32_t variable. + * If supplied and the connection type is ConnectionType::TEMPORARY_CONNECTION the variable will be set to the remaining duration of the connection. + * Otherwise the variable value is not modified. + * @param peerMac An optional pointer to an uint8_t array with at least size 6. It will be filled with the MAC of the encrypted peer interface if an encrypted connection exists. + * Otherwise the array is not modified. + * @return The ConnectionType of the connection given by connectionIndex. + */ + static ConnectionType getConnectionInfo(const uint32_t connectionIndex, uint32_t *remainingDuration = nullptr, uint8_t *peerMac = nullptr); + + /** + * @return The proportion of ESP-NOW requests made by this node that have failed, since power on or latest reset. + */ + static double getTransmissionFailRate(); + + /** + * Reset TransmissionFailRate back to 0. + */ + static void resetTransmissionFailRate(); + + void setWiFiChannel(const uint8 newWiFiChannel) override; + +protected: + + EspnowDatabase *getDatabase(); + const EspnowDatabase *getDatabaseConst() const; + EspnowConnectionManager *getConnectionManager(); + const EspnowConnectionManager *getConnectionManagerConst() const; + EspnowTransmitter *getTransmitter(); + const EspnowTransmitter *getTransmitterConst() const; + EspnowEncryptionBroker *getEncryptionBroker(); + const EspnowEncryptionBroker *getEncryptionBrokerConst() const; + + bool activateEspnow(); + + /* + * Note that ESP-NOW is not perfect and in rare cases messages may be dropped. + * This needs to be compensated for in the application via extra verification + * (e.g. by always sending a response such as a message hash), if message delivery must be guaranteed. + * + * Note that although responses will generally be sent in the order they were created, this is not guaranteed to be the case. + * For example, response order will be mixed up if some responses fail to transmit while others transmit successfully. + * + * @param estimatedMaxDurationTracker A pointer to an ExpiringTimeTracker initialized with the desired max duration for the method. If set to nullptr there is no duration limit. + * Note that setting the estimatedMaxDuration too low may result in missed ESP-NOW transmissions because of too little time for maintenance. + * Also note that although the method will try to respect the max duration limit, there is no guarantee. Overshoots by tens of milliseconds are possible. + */ + static void sendStoredEspnowMessages(const ExpiringTimeTracker *estimatedMaxDurationTracker = nullptr); + + using macAndType_td = EspnowProtocolInterpreter::macAndType_td; + using messageID_td = EspnowProtocolInterpreter::messageID_td; + using peerMac_td = EspnowProtocolInterpreter::peerMac_td; + +private: + + EspnowMeshBackend(const requestHandlerType requestHandler, const responseHandlerType responseHandler, const networkFilterType networkFilter, const broadcastFilterType broadcastFilter, + const String &meshPassword, const String &ssidPrefix, const String &ssidSuffix, const bool verboseMode, const uint8 meshWiFiChannel); + + EspnowDatabase _database; + EspnowConnectionManager _connectionManager; + EspnowTransmitter _transmitter; + EspnowEncryptionBroker _encryptionBroker; + + void prepareForTransmission(const String &message, const bool scan, const bool scanAllWiFiChannels); + TransmissionStatusType initiateTransmission(const String &message, const EspnowNetworkInfo &recipientInfo); + TransmissionStatusType initiateTransmissionKernel(const String &message, const uint8_t *targetBSSID); + TransmissionStatusType initiateAutoEncryptingTransmission(const String &message, uint8_t *targetBSSID, const EncryptedConnectionStatus connectionStatus); + void printTransmissionStatistics() const; + + // Used for verboseMode printing in attemptTransmission, _AT suffix used to reduce namespace clutter + uint32_t totalDurationWhenSuccessful_AT = 0; + uint32_t successfulTransmissions_AT = 0; + uint32_t maxTransmissionDuration_AT = 0; + + /** + * We can't feed esp_now_register_recv_cb our EspnowMeshBackend instance's espnowReceiveCallback method directly, so this callback wrapper is a workaround. + * + * This method is very time critical so avoid Serial printing in it and in methods called from it (such as espnowReceiveCallback) as much as possible. + * Otherwise transmission fail rate is likely to skyrocket. + */ + static void espnowReceiveCallbackWrapper(uint8_t *macaddr, uint8_t *dataArray, const uint8_t len); + void espnowReceiveCallback(const uint8_t *macaddr, uint8_t *data, const uint8_t len); + + broadcastFilterType _broadcastFilter; + + bool _acceptsUnverifiedRequests = true; +}; + +#endif diff --git a/libraries/ESP8266WiFiMesh/src/EspnowNetworkInfo.cpp b/libraries/ESP8266WiFiMesh/src/EspnowNetworkInfo.cpp new file mode 100644 index 0000000000..6e52925e39 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/EspnowNetworkInfo.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#include "EspnowNetworkInfo.h" +#include + +EspnowNetworkInfo::EspnowNetworkInfo(const int networkIndex) : NetworkInfoBase(networkIndex) { }; + +EspnowNetworkInfo::EspnowNetworkInfo(const NetworkInfoBase &originalNetworkInfo) : NetworkInfoBase(originalNetworkInfo) +{ + assert(BSSID() != defaultBSSID); // We need at least BSSID to be able to connect. +}; + +EspnowNetworkInfo::EspnowNetworkInfo(const uint8_t BSSID[6], const String &SSID, const int32_t wifiChannel, const uint8_t encryptionType, const int32_t RSSI , const bool isHidden) + : NetworkInfoBase(SSID, wifiChannel, BSSID, encryptionType, RSSI, isHidden) +{ } + diff --git a/libraries/ESP8266WiFiMesh/src/EspnowNetworkInfo.h b/libraries/ESP8266WiFiMesh/src/EspnowNetworkInfo.h new file mode 100644 index 0000000000..06b39b987c --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/EspnowNetworkInfo.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#ifndef __ESPNOWNETWORKINFO_H__ +#define __ESPNOWNETWORKINFO_H__ + +#include "NetworkInfoBase.h" + +class EspnowNetworkInfo : public NetworkInfoBase { + +public: + + /** + * Automatically fill in the rest of the network info using networkIndex and the WiFi scan results. + */ + EspnowNetworkInfo(const int networkIndex); + + EspnowNetworkInfo(const NetworkInfoBase &originalNetworkInfo); + + EspnowNetworkInfo(const uint8_t BSSID[6], const String &SSID = defaultSSID, const int32_t wifiChannel = defaultWifiChannel, const uint8_t encryptionType = defaultEncryptionType, + const int32_t RSSI = defaultRSSI, const bool isHidden = defaultIsHidden); +}; + +#endif diff --git a/libraries/ESP8266WiFiMesh/src/EspnowProtocolInterpreter.cpp b/libraries/ESP8266WiFiMesh/src/EspnowProtocolInterpreter.cpp new file mode 100644 index 0000000000..6bf1568ea1 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/EspnowProtocolInterpreter.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#include "EspnowProtocolInterpreter.h" +#include "TypeConversionFunctions.h" +#include +#include "EspnowTransmitter.h" +#include "UtilityFunctions.h" + +namespace +{ + namespace TypeCast = MeshTypeConversionFunctions; +} + +namespace EspnowProtocolInterpreter +{ + uint8_t metadataSize() + { + return protocolBytesSize + (EspnowTransmitter::useEncryptedMessages() ? aeadMetadataSize : 0); + } + + uint32_t getMaxBytesPerTransmission() + { + return 250; + } + + uint32_t getMaxMessageBytesPerTransmission() + { + return getMaxBytesPerTransmission() - metadataSize(); + } + + String getHashKeyLength(uint8_t *transmissionDataArray, const uint8_t transmissionLength) + { + String messageContent = emptyString; + + if(transmissionLength >= metadataSize()) + { + uint8_t messageSize = transmissionLength - metadataSize(); + + messageContent = TypeCast::uint8ArrayToMultiString(transmissionDataArray + metadataSize(), messageSize); + } + + return messageContent; + } + + char getMessageType(const uint8_t *transmissionDataArray) + { + return char(transmissionDataArray[messageTypeIndex]); + } + + uint8_t getTransmissionsRemaining(const uint8_t *transmissionDataArray) + { + return (transmissionDataArray[transmissionsRemainingIndex] & 0x7F); + } + + bool isMessageStart(const uint8_t *transmissionDataArray) + { + return (transmissionDataArray[transmissionsRemainingIndex] & 0x80); // If MSB is one we have messageStart + } + + uint64_t getTransmissionMac(const uint8_t *transmissionDataArray) + { + return TypeCast::macToUint64(transmissionDataArray + transmissionMacIndex); + } + + uint8_t *getTransmissionMac(const uint8_t *transmissionDataArray, uint8_t *resultArray) + { + std::copy_n((transmissionDataArray + transmissionMacIndex), 6, resultArray); + return resultArray; + } + + uint64_t getMessageID(const uint8_t *transmissionDataArray) + { + return TypeCast::uint8ArrayToUint64(transmissionDataArray + messageIDIndex); + } + + uint8_t *setMessageID(uint8_t *transmissionDataArray, const uint64_t messageID) + { + return TypeCast::uint64ToUint8Array(messageID, transmissionDataArray + messageIDIndex); + } + + bool usesEncryption(const uint64_t messageID) + { + // At least one of the leftmost half of bits in messageID is 1 if the transmission is encrypted. + return messageID & uint64LeftmostBits; + } + + bool usesConstantSessionKey(const char messageType) + { + return messageType == 'A' || messageType == 'C'; + } + + uint64_t createSessionKey() + { + uint64_t newSessionKey = MeshUtilityFunctions::randomUint64(); + return usesEncryption(newSessionKey) ? newSessionKey : (newSessionKey | ((uint64_t)ESP.random()) << 32 | uint64MSB); + } + + macAndType_td createMacAndTypeValue(const uint64_t uint64Mac, const char messageType) + { + return static_cast(uint64Mac << 8 | (uint64_t)messageType); + } + + uint64_t macAndTypeToUint64Mac(const macAndType_td &macAndTypeValue) + { + return static_cast(macAndTypeValue) >> 8; + } +} diff --git a/libraries/ESP8266WiFiMesh/src/EspnowProtocolInterpreter.h b/libraries/ESP8266WiFiMesh/src/EspnowProtocolInterpreter.h new file mode 100644 index 0000000000..705637868a --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/EspnowProtocolInterpreter.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#ifndef __ESPNOWPROTOCOLINTERPRETER_H__ +#define __ESPNOWPROTOCOLINTERPRETER_H__ + +#include + +// The following protocol is used on top of ESP-NOW (for the bits and bytes in each transmission): +// Bit 0-7: Message type. The type for requests must be different from the type for responses if they may require more than one transmission. Otherwise multi-part requests and responses with the same ID may be mixed together. +// Bit 8: Flag for message start. +// Bit 9-15: Transmissions remaining for the message. +// Byte 2-7: Transmission sender MAC address for AP interface. Since we always transmit from the station interface, this ensures both sender MAC addresses are available to the receiver. +// Byte 8-15: Message ID. 32 rightmost bits used for unencrypted messages (the rest is 0). 64 bits used for encrypted messages (with at least one of the leftmost 32 bits set to 1). +// This distinction based on encryption is required since the ESP-NOW API does not provide information about whether a received transmission is encrypted or not. +// Byte 16-249: The message. +// Each message can be split in up to EspnowMeshBackend::getMaxTransmissionsPerMessage() transmissions, based on message size. (max three transmissions per message is the default) + +namespace EspnowProtocolInterpreter +{ + constexpr char synchronizationRequestHeader[] PROGMEM = "Synchronization request."; + constexpr char encryptionRequestHeader[] PROGMEM = "AddEC:"; // Add encrypted connection + constexpr char temporaryEncryptionRequestHeader[] PROGMEM = "AddTEC:"; // Add temporary encrypted connection + constexpr char basicConnectionInfoHeader[] PROGMEM = "BasicCI:"; // Basic connection info + constexpr char encryptedConnectionInfoHeader[] PROGMEM = "EncryptedCI:"; // Encrypted connection info + constexpr char softLimitEncryptedConnectionInfoHeader[] PROGMEM = "SLEncryptedCI:"; // Soft limit encrypted connection info + constexpr char maxConnectionsReachedHeader[] PROGMEM = "MAX_CONNECTIONS_REACHED_PEER:"; + constexpr char encryptedConnectionVerificationHeader[] PROGMEM = "ECVerified:"; // Encrypted connection verified + constexpr char encryptedConnectionRemovalRequestHeader[] PROGMEM = "RemoveEC:"; // Remove encrypted connection + + constexpr uint8_t messageTypeIndex = 0; + constexpr uint8_t transmissionsRemainingIndex = 1; + constexpr uint8_t transmissionMacIndex = 2; + constexpr uint8_t messageIDIndex = 8; + + constexpr uint8_t maxEncryptedConnections = 6; // This is limited by the ESP-NOW API. Max 6 in AP or AP+STA mode. Max 10 in STA mode. See "ESP-NOW User Guide" for more info. + + constexpr uint8_t protocolBytesSize = 16; + constexpr uint8_t aeadMetadataSize = 28; + uint8_t metadataSize(); + uint32_t getMaxBytesPerTransmission(); + uint32_t getMaxMessageBytesPerTransmission(); + + constexpr uint64_t uint64BroadcastMac = 0xFFFFFFFFFFFF; + constexpr uint8_t broadcastMac[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + + constexpr uint8_t encryptedConnectionKeyLength = 16; // This is restricted to exactly 16 bytes by the ESP-NOW API. It should not be changed unless the ESP-NOW API is changed. + constexpr uint8_t hashKeyLength = 16; // This can be changed to any value up to 255. Common values are 16 and 32. + + constexpr uint64_t uint64LeftmostBits = 0xFFFFFFFF00000000; + constexpr uint64_t uint64MSB = 0x8000000000000000; + + String getHashKeyLength(uint8_t *transmissionDataArray, const uint8_t transmissionLength); + char getMessageType(const uint8_t *transmissionDataArray); + uint8_t getTransmissionsRemaining(const uint8_t *transmissionDataArray); + bool isMessageStart(const uint8_t *transmissionDataArray); + uint64_t getTransmissionMac(const uint8_t *transmissionDataArray); + uint8_t *getTransmissionMac(const uint8_t *transmissionDataArray, uint8_t *resultArray); + uint64_t getMessageID(const uint8_t *transmissionDataArray); + // @return a pointer to transmissionDataArray + uint8_t *setMessageID(uint8_t *transmissionDataArray, const uint64_t messageID); + + bool usesEncryption(const uint64_t messageID); + bool usesConstantSessionKey(const char messageType); + + /** + * Create a new session key for an encrypted connection using the built in RANDOM_REG32/ESP.random() of the ESP8266. + * Should only be used when initializing a new connection. + * Use generateMessageID instead when the encrypted connection is already initialized to keep the connection synchronized. + * + * @return A uint64_t containing a new session key for an encrypted connection. + */ + uint64_t createSessionKey(); + + enum class macAndType_td : uint64_t {}; + using messageID_td = uint64_t; + using peerMac_td = uint64_t; + + macAndType_td createMacAndTypeValue(const uint64_t uint64Mac, const char messageType); + uint64_t macAndTypeToUint64Mac(const macAndType_td &macAndTypeValue); +} + +#endif diff --git a/libraries/ESP8266WiFiMesh/src/EspnowTransmitter.cpp b/libraries/ESP8266WiFiMesh/src/EspnowTransmitter.cpp new file mode 100644 index 0000000000..7909a7be06 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/EspnowTransmitter.cpp @@ -0,0 +1,428 @@ +/* + Copyright (C) 2020 Anders Löfgren + + 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 +extern "C" { + #include +} + +#include "EspnowTransmitter.h" +#include "EspnowMeshBackend.h" +#include "TypeConversionFunctions.h" +#include "UtilityFunctions.h" +#include "MeshCryptoInterface.h" +#include "JsonTranslator.h" + +namespace +{ + namespace TypeCast = MeshTypeConversionFunctions; + + double _transmissionsTotal = 0; + double _transmissionsFailed = 0; + + std::shared_ptr _espnowTransmissionMutex = std::make_shared(false); + std::shared_ptr _espnowSendToNodeMutex = std::make_shared(false); + + uint32_t _espnowTransmissionTimeoutMs = 40; + uint32_t _espnowRetransmissionIntervalMs = 15; + + uint8_t _espnowMessageEncryptionKey[experimental::crypto::ENCRYPTION_KEY_LENGTH] = { 0 }; + bool _useEncryptedMessages = false; + + uint8_t _transmissionTargetBSSID[6] = {0}; + + bool _espnowSendConfirmed = false; + + uint8_t _maxTransmissionsPerMessage = 3; +} + +EspnowTransmitter::EspnowTransmitter(ConditionalPrinter &conditionalPrinterInstance, EspnowDatabase &databaseInstance, EspnowConnectionManager &connectionManagerInstance) + : _conditionalPrinter(conditionalPrinterInstance), _database(databaseInstance), _connectionManager(connectionManagerInstance) +{ +} + +void EspnowTransmitter::espnowSendCallback(uint8_t* mac, uint8_t sendStatus) +{ + if(_espnowSendConfirmed) + return; + else if(!sendStatus && MeshUtilityFunctions::macEqual(mac, _transmissionTargetBSSID)) // sendStatus == 0 when send was OK. + _espnowSendConfirmed = true; // We do not want to reset this to false. That only happens before transmissions. Otherwise subsequent failed send attempts may obscure an initial successful one. +} + +void EspnowTransmitter::setUseEncryptedMessages(const bool useEncryptedMessages) +{ + MutexTracker mutexTracker(_espnowSendToNodeMutex); + if(!mutexTracker.mutexCaptured()) + { + assert(false && String(F("ERROR! espnowSendToNode in progress. Don't call setUseEncryptedMessages from non-hook callbacks since this may modify the ESP-NOW transmission parameters during ongoing transmissions! Aborting."))); + } + + _useEncryptedMessages = useEncryptedMessages; +} +bool EspnowTransmitter::useEncryptedMessages() { return _useEncryptedMessages; } + +void EspnowTransmitter::setEspnowMessageEncryptionKey(const uint8_t espnowMessageEncryptionKey[experimental::crypto::ENCRYPTION_KEY_LENGTH]) +{ + assert(espnowMessageEncryptionKey != nullptr); + + for(int i = 0; i < experimental::crypto::ENCRYPTION_KEY_LENGTH; ++i) + { + _espnowMessageEncryptionKey[i] = espnowMessageEncryptionKey[i]; + } +} + +void EspnowTransmitter::setEspnowMessageEncryptionKey(const String &espnowMessageEncryptionKeySeed) +{ + MeshCryptoInterface::initializeKey(_espnowMessageEncryptionKey, experimental::crypto::ENCRYPTION_KEY_LENGTH, espnowMessageEncryptionKeySeed); +} + +const uint8_t *EspnowTransmitter::getEspnowMessageEncryptionKey() +{ + return _espnowMessageEncryptionKey; +} + +void EspnowTransmitter::setBroadcastTransmissionRedundancy(const uint8_t redundancy) { _broadcastTransmissionRedundancy = redundancy; } +uint8_t EspnowTransmitter::getBroadcastTransmissionRedundancy() const { return _broadcastTransmissionRedundancy; } + +void EspnowTransmitter::setResponseTransmittedHook(const responseTransmittedHookType responseTransmittedHook) { _responseTransmittedHook = responseTransmittedHook; } +EspnowTransmitter::responseTransmittedHookType EspnowTransmitter::getResponseTransmittedHook() const { return _responseTransmittedHook; } + +void EspnowTransmitter::setMaxTransmissionsPerMessage(const uint8_t maxTransmissionsPerMessage) +{ + assert(1 <= maxTransmissionsPerMessage && maxTransmissionsPerMessage <= 128); + + _maxTransmissionsPerMessage = maxTransmissionsPerMessage; +} + +uint8_t EspnowTransmitter::getMaxTransmissionsPerMessage() {return _maxTransmissionsPerMessage;} + +uint32_t EspnowTransmitter::getMaxMessageLength() +{ + return getMaxTransmissionsPerMessage() * EspnowProtocolInterpreter::getMaxMessageBytesPerTransmission(); +} + +void EspnowTransmitter::setEspnowTransmissionTimeout(const uint32_t timeoutMs) +{ + _espnowTransmissionTimeoutMs = timeoutMs; +} +uint32_t EspnowTransmitter::getEspnowTransmissionTimeout() {return _espnowTransmissionTimeoutMs;} + +void EspnowTransmitter::setEspnowRetransmissionInterval(const uint32_t intervalMs) +{ + _espnowRetransmissionIntervalMs = intervalMs; +} +uint32_t EspnowTransmitter::getEspnowRetransmissionInterval() {return _espnowRetransmissionIntervalMs;} + +double EspnowTransmitter::getTransmissionFailRate() +{ + if(_transmissionsTotal == 0) + return 0; + + return _transmissionsFailed/_transmissionsTotal; +} + +void EspnowTransmitter::resetTransmissionFailRate() +{ + _transmissionsFailed = 0; + _transmissionsTotal = 0; +} + +void EspnowTransmitter::sendEspnowResponses(const ExpiringTimeTracker *estimatedMaxDurationTracker) +{ + uint32_t bufferedCriticalHeapLevel = EspnowDatabase::criticalHeapLevel() + EspnowDatabase::criticalHeapLevelBuffer(); // We preferably want to start clearing the logs a bit before things get critical. + + MutexTracker responsesToSendMutexTracker(EspnowDatabase::captureResponsesToSendMutex()); + if(!responsesToSendMutexTracker.mutexCaptured()) + { + assert(false && String(F("ERROR! responsesToSend locked. Don't call sendEspnowResponses from callbacks as this may corrupt program state! Aborting."))); + } + + uint32_t responseIndex = 0; + for(std::list::iterator responseIterator = EspnowDatabase::responsesToSend().begin(); responseIterator != EspnowDatabase::responsesToSend().end(); ++responseIndex) + { + if(responseIterator->getTimeTracker().timeSinceCreation() > EspnowDatabase::logEntryLifetimeMs()) + { + // If the response is older than logEntryLifetimeMs(), the corresponding request log entry has been deleted at the request sender, + // so the request sender will not accept our response any more. + // This probably happens because we have a high transmission activity and more requests coming in than we can handle. + ++responseIterator; + continue; + } + + // Note that callbacks can be called during delay time, so it is possible to receive a transmission during espnowSendToNode + // (which may add an element to the responsesToSend list). + bool transmissionSuccessful = espnowSendToNodeUnsynchronized(responseIterator->getMessage(), responseIterator->getRecipientMac(), 'A', responseIterator->getRequestID()) + == TransmissionStatusType::TRANSMISSION_COMPLETE; + + bool hookOutcome = true; + if(EspnowMeshBackend *currentEspnowRequestManager = EspnowMeshBackend::getEspnowRequestManager()) + hookOutcome = currentEspnowRequestManager->getResponseTransmittedHook()(transmissionSuccessful, responseIterator->getMessage(), responseIterator->getRecipientMac(), responseIndex, *currentEspnowRequestManager); + + if(transmissionSuccessful) + { + responseIterator = EspnowDatabase::responsesToSend().erase(responseIterator); + --responseIndex; + } + else + { + ++responseIterator; + } + + if(ESP.getFreeHeap() <= bufferedCriticalHeapLevel) + { + // Heap is getting very low, which probably means we are receiving a lot of transmissions while trying to transmit responses. + // Clear all old data to try to avoid running out of memory. + ConditionalPrinter::warningPrint("WARNING! Free heap below chosen minimum. Performing emergency log clearing."); + EspnowDatabase::clearOldLogEntries(true); + return; // responseIterator may be invalid now. Also, we should give the main loop a chance to respond to the situation. + } + + if(!hookOutcome || (estimatedMaxDurationTracker && estimatedMaxDurationTracker->expired())) + return; + } +} + +MutexTracker EspnowTransmitter::captureEspnowTransmissionMutex() +{ + // Syntax like this will move the resulting value into its new position (similar to NRVO): https://stackoverflow.com/a/11540204 + return MutexTracker(_espnowTransmissionMutex); +} + +MutexTracker EspnowTransmitter::captureEspnowTransmissionMutex(const std::function destructorHook) { return MutexTracker(_espnowTransmissionMutex, destructorHook); } + +bool EspnowTransmitter::transmissionInProgress(){return *_espnowTransmissionMutex;} + +TransmissionStatusType EspnowTransmitter::espnowSendToNode(const String &message, const uint8_t *targetBSSID, const char messageType, EspnowMeshBackend *espnowInstance) +{ + using EspnowProtocolInterpreter::synchronizationRequestHeader; + + EncryptedConnectionLog *encryptedConnection = EspnowConnectionManager::getEncryptedConnection(targetBSSID); + + if(encryptedConnection) + { + uint8_t encryptedMac[6] {0}; + encryptedConnection->getEncryptedPeerMac(encryptedMac); + + assert(esp_now_is_peer_exist(encryptedMac) > 0 && String(F("ERROR! Attempting to send content marked as encrypted via unencrypted connection!"))); + + if(encryptedConnection->desync()) + { + espnowSendToNodeUnsynchronized(FPSTR(synchronizationRequestHeader), encryptedMac, 'S', EspnowConnectionManager::generateMessageID(encryptedConnection), espnowInstance); + + if(encryptedConnection->desync()) + { + return TransmissionStatusType::TRANSMISSION_FAILED; + } + } + + return espnowSendToNodeUnsynchronized(message, encryptedMac, messageType, EspnowConnectionManager::generateMessageID(encryptedConnection), espnowInstance); + } + + return espnowSendToNodeUnsynchronized(message, targetBSSID, messageType, EspnowConnectionManager::generateMessageID(encryptedConnection), espnowInstance); +} + +TransmissionStatusType EspnowTransmitter::espnowSendToNodeUnsynchronized(const String message, const uint8_t *targetBSSID, const char messageType, const uint64_t messageID, EspnowMeshBackend *espnowInstance) +{ + using namespace EspnowProtocolInterpreter; + + MutexTracker mutexTracker(_espnowSendToNodeMutex); + if(!mutexTracker.mutexCaptured()) + { + assert(false && String(F("ERROR! espnowSendToNode already in progress. Don't call espnowSendToNode from callbacks as this will make it impossible to know which transmissions succeed! Aborting."))); + return TransmissionStatusType::TRANSMISSION_FAILED; + } + + // We copy the message String and bssid array from the arguments in this method to make sure they are + // not modified by a callback during the delay(1) calls further down. + // This also makes it possible to get the current _transmissionTargetBSSID outside of the method. + std::copy_n(targetBSSID, 6, _transmissionTargetBSSID); + + EncryptedConnectionLog *encryptedConnection = EspnowConnectionManager::getEncryptedConnection(_transmissionTargetBSSID); + + int32_t transmissionsRequired = ceil((double)message.length() / getMaxMessageBytesPerTransmission()); + int32_t transmissionsRemaining = transmissionsRequired > 1 ? transmissionsRequired - 1 : 0; + + _transmissionsTotal++; + + // Though it is possible to handle messages requiring more than 3 transmissions with the current design, transmission fail rates would increase dramatically. + // Messages composed of up to 128 transmissions can be handled without modification, but RAM limitations on the ESP8266 would make this hard in practice. + // We thus prefer to keep the code simple and performant instead. + // Very large messages can always be split by the user as required. + assert(transmissionsRequired <= getMaxTransmissionsPerMessage()); + assert(messageType == 'Q' || messageType == 'A' || messageType == 'B' || messageType == 'S' || messageType == 'P' || messageType == 'C'); + if(messageType == 'P' || messageType == 'C') + { + assert(transmissionsRequired == 1); // These messages are assumed to be contained in one message by the receive callbacks. + } + + uint8_t transmissionSize = 0; + bool messageStart = true; + uint8_t espnowMetadataSize = metadataSize(); + + do + { + ////// Manage logs ////// + + if(transmissionsRemaining == 0 && (messageType == 'Q' || messageType == 'B')) + { + assert(espnowInstance); // espnowInstance required when transmitting 'Q' and 'B' type messages. + // If we are sending the last transmission of a request we should store the sent request in the log no matter if we receive an ack for the final transmission or not. + // That way we will always be ready to receive the response to the request when there is a chance the request message was transmitted successfully, + // even if the final ack for the request message was lost. + EspnowDatabase::storeSentRequest(TypeCast::macToUint64(_transmissionTargetBSSID), messageID, RequestData(*espnowInstance)); + } + + ////// Create transmission array ////// + + if(transmissionsRemaining > 0) + { + transmissionSize = getMaxBytesPerTransmission(); + } + else + { + transmissionSize = espnowMetadataSize; + + if(message.length() > 0) + { + uint32_t remainingLength = message.length() % getMaxMessageBytesPerTransmission(); + transmissionSize += (remainingLength == 0 ? getMaxMessageBytesPerTransmission() : remainingLength); + } + } + + uint8_t transmission[transmissionSize]; + + ////// Fill protocol bytes ////// + + transmission[messageTypeIndex] = messageType; + + if(messageStart) + { + transmission[transmissionsRemainingIndex] = (char)(transmissionsRemaining | 0x80); + } + else + { + transmission[transmissionsRemainingIndex] = (char)transmissionsRemaining; + } + + // Fills indices in range [transmissionMacIndex, transmissionMacIndex + 5] (6 bytes) with the MAC address of the WiFi AP interface. + // We always transmit from the station interface (due to using ESP_NOW_ROLE_CONTROLLER), so this makes it possible to always know both interface MAC addresses of a node that sends a transmission. + WiFi.softAPmacAddress(transmission + transmissionMacIndex); + + setMessageID(transmission, messageID); + + ////// Fill message bytes ////// + + int32_t transmissionStartIndex = (transmissionsRequired - transmissionsRemaining - 1) * getMaxMessageBytesPerTransmission(); + + std::copy_n(message.begin() + transmissionStartIndex, transmissionSize - espnowMetadataSize, transmission + espnowMetadataSize); + + if(useEncryptedMessages()) + { + // chacha20Poly1305Encrypt encrypts transmission in place. + // We are using the protocol bytes as a key salt. + experimental::crypto::ChaCha20Poly1305::encrypt(transmission + espnowMetadataSize, transmissionSize - espnowMetadataSize, getEspnowMessageEncryptionKey(), transmission, + protocolBytesSize, transmission + protocolBytesSize, transmission + protocolBytesSize + 12); + } + + ////// Transmit ////// + + uint32_t retransmissions = 0; + if(messageType == 'B') + retransmissions = espnowInstance->getBroadcastTransmissionRedundancy(); + + for(uint32_t i = 0; i <= retransmissions; ++i) + { + _espnowSendConfirmed = false; + ExpiringTimeTracker transmissionTimeout([](){ return getEspnowTransmissionTimeout(); }); + + while(!_espnowSendConfirmed && !transmissionTimeout) + { + if(esp_now_send(_transmissionTargetBSSID, transmission, transmissionSize) == 0) // == 0 => Success + { + ExpiringTimeTracker retransmissionTime([](){ return getEspnowRetransmissionInterval(); }); + while(!_espnowSendConfirmed && !retransmissionTime && !transmissionTimeout) + { + delay(1); // Note that callbacks can be called during delay time, so it is possible to receive a transmission during this delay. + } + } + + if(_espnowSendConfirmed) + { + if(messageStart) + { + if(encryptedConnection && !usesConstantSessionKey(messageType) && encryptedConnection->getOwnSessionKey() == messageID) + { + encryptedConnection->setDesync(false); + encryptedConnection->incrementOwnSessionKey(); + } + + messageStart = false; + } + + break; + } + } + } + + if(!_espnowSendConfirmed) + { + ++_transmissionsFailed; + + ConditionalPrinter::staticVerboseModePrint(String(F("espnowSendToNode failed!"))); + ConditionalPrinter::staticVerboseModePrint(String(F("Transmission #: ")) + String(transmissionsRequired - transmissionsRemaining) + String('/') + String(transmissionsRequired)); + ConditionalPrinter::staticVerboseModePrint(String(F("Transmission fail rate (up) ")) + String(getTransmissionFailRate())); + + if(messageStart && encryptedConnection && !usesConstantSessionKey(messageType) && encryptedConnection->getOwnSessionKey() == messageID) + encryptedConnection->setDesync(true); + + return TransmissionStatusType::TRANSMISSION_FAILED; + } + + --transmissionsRemaining; // This is used when transferring multi-transmission messages. + + } while(transmissionsRemaining >= 0); + + // Useful when debugging the protocol + //_conditionalPrinter.staticVerboseModePrint("Sent to Mac: " + TypeCast::macToString(_transmissionTargetBSSID) + " ID: " + TypeCast::uint64ToString(messageID)); + + return TransmissionStatusType::TRANSMISSION_COMPLETE; +} + +TransmissionStatusType EspnowTransmitter::espnowSendPeerRequestConfirmationsUnsynchronized(const String message, const uint8_t *targetBSSID, const char messageType, EspnowMeshBackend *espnowInstance) +{ + return espnowSendToNodeUnsynchronized(message, targetBSSID, messageType, EspnowConnectionManager::generateMessageID(nullptr), espnowInstance); +} + +TransmissionStatusType EspnowTransmitter::sendRequest(const String &message, const uint8_t *targetBSSID, EspnowMeshBackend *espnowInstance) +{ + TransmissionStatusType transmissionStatus = espnowSendToNode(message, targetBSSID, 'Q', espnowInstance); + + return transmissionStatus; +} + +TransmissionStatusType EspnowTransmitter::sendResponse(const String &message, const uint64_t requestID, const uint8_t *targetBSSID, EspnowMeshBackend *espnowInstance) +{ + EncryptedConnectionLog *encryptedConnection = EspnowConnectionManager::getEncryptedConnection(targetBSSID); + uint8_t encryptedMac[6] {0}; + + if(encryptedConnection) + { + encryptedConnection->getEncryptedPeerMac(encryptedMac); + assert(esp_now_is_peer_exist(encryptedMac) > 0 && String(F("ERROR! Attempting to send content marked as encrypted via unencrypted connection!"))); + } + + return espnowSendToNodeUnsynchronized(message, encryptedConnection ? encryptedMac : targetBSSID, 'A', requestID, espnowInstance); +} diff --git a/libraries/ESP8266WiFiMesh/src/EspnowTransmitter.h b/libraries/ESP8266WiFiMesh/src/EspnowTransmitter.h new file mode 100644 index 0000000000..403bea3027 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/EspnowTransmitter.h @@ -0,0 +1,109 @@ +/* + Copyright (C) 2020 Anders Löfgren + + 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 __ESPNOWTRANSMITTER_H__ +#define __ESPNOWTRANSMITTER_H__ + +#include +#include +#include "ExpiringTimeTracker.h" +#include "EspnowDatabase.h" +#include "EspnowConnectionManager.h" +#include "ConditionalPrinter.h" + +class EspnowMeshBackend; + +class EspnowTransmitter +{ + +public: + + using responseTransmittedHookType = std::function; + + EspnowTransmitter(ConditionalPrinter &conditionalPrinterInstance, EspnowDatabase &databaseInstance, EspnowConnectionManager &connectionManagerInstance); + + static void espnowSendCallback(uint8_t* mac, uint8_t sendStatus); + + /** + * Send an ESP-NOW message to the ESP8266 that has the MAC address specified in targetBSSID. + * + * @param messageType The identifier character for the type of message to send. Choices are 'Q' for question (request), + * 'A' for answer (response), 'B' for broadcast, 'S' for synchronization request, 'P' for peer request and 'C' for peer request confirmation. + * @return The transmission status for the transmission. + */ + // Send a message to the node having targetBSSID as mac, changing targetBSSID to the mac of the encrypted connection if it exists and ensuring such an encrypted connection is synchronized. + static TransmissionStatusType espnowSendToNode(const String &message, const uint8_t *targetBSSID, const char messageType, EspnowMeshBackend *espnowInstance = nullptr); + // Send a message using exactly the arguments given, without consideration for any encrypted connections. + static TransmissionStatusType espnowSendToNodeUnsynchronized(const String message, const uint8_t *targetBSSID, const char messageType, const uint64_t messageID, EspnowMeshBackend *espnowInstance = nullptr); + + // Send a PeerRequestConfirmation using exactly the arguments given, without consideration for any encrypted connections. + static TransmissionStatusType espnowSendPeerRequestConfirmationsUnsynchronized(const String message, const uint8_t *targetBSSID, const char messageType, EspnowMeshBackend *espnowInstance = nullptr); + + TransmissionStatusType sendRequest(const String &message, const uint8_t *targetBSSID, EspnowMeshBackend *espnowInstance); + TransmissionStatusType sendResponse(const String &message, const uint64_t requestID, const uint8_t *targetBSSID, EspnowMeshBackend *espnowInstance); + + static void setUseEncryptedMessages(const bool useEncryptedMessages); + static bool useEncryptedMessages(); + static void setEspnowMessageEncryptionKey(const uint8_t espnowMessageEncryptionKey[experimental::crypto::ENCRYPTION_KEY_LENGTH]); + static void setEspnowMessageEncryptionKey(const String &espnowMessageEncryptionKeySeed); + static const uint8_t *getEspnowMessageEncryptionKey(); + + void setBroadcastTransmissionRedundancy(const uint8_t redundancy); + uint8_t getBroadcastTransmissionRedundancy() const; + void setResponseTransmittedHook(const responseTransmittedHookType responseTransmittedHook); + responseTransmittedHookType getResponseTransmittedHook() const; + static void setMaxTransmissionsPerMessage(const uint8_t maxTransmissionsPerMessage); + static uint8_t getMaxTransmissionsPerMessage(); + static uint32_t getMaxMessageLength(); + static void setEspnowTransmissionTimeout(const uint32_t timeoutMs); + static uint32_t getEspnowTransmissionTimeout(); + static void setEspnowRetransmissionInterval(const uint32_t intervalMs); + static uint32_t getEspnowRetransmissionInterval(); + static double getTransmissionFailRate(); + static void resetTransmissionFailRate(); + + /* + * @param estimatedMaxDurationTracker A pointer to an ExpiringTimeTracker initialized with the desired max duration for the method. If set to nullptr there is no duration limit. + * Note that setting the estimatedMaxDuration too low may result in missed ESP-NOW transmissions because of too little time for maintenance. + * Also note that although the method will try to respect the max duration limit, there is no guarantee. Overshoots by tens of milliseconds are possible. + */ + static void sendEspnowResponses(const ExpiringTimeTracker *estimatedMaxDurationTracker = nullptr); + + /** + * Will be captured if a transmission initiated by a public method is in progress. + */ + static MutexTracker captureEspnowTransmissionMutex(); + static MutexTracker captureEspnowTransmissionMutex(const std::function destructorHook); + + /** + * Check if there is an ongoing ESP-NOW transmission in the library. Used to avoid interrupting transmissions. + * + * @return True if a transmission initiated by a public method is in progress. + */ + static bool transmissionInProgress(); + +private: + + ConditionalPrinter & _conditionalPrinter; + EspnowDatabase & _database; + EspnowConnectionManager & _connectionManager; + + responseTransmittedHookType _responseTransmittedHook = [](bool, const String &, const uint8_t *, uint32_t, EspnowMeshBackend &){ return true; }; + + uint8_t _broadcastTransmissionRedundancy = 1; +}; + +#endif diff --git a/libraries/ESP8266WiFiMesh/src/ExpiringTimeTracker.cpp b/libraries/ESP8266WiFiMesh/src/ExpiringTimeTracker.cpp new file mode 100644 index 0000000000..5151c0bc5a --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/ExpiringTimeTracker.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#include "ExpiringTimeTracker.h" + +ExpiringTimeTracker::ExpiringTimeTracker(const uint32_t duration, const uint32_t creationTimeMs) : + timeoutTemplate(0) +{ + setDuration(duration); + _start = creationTimeMs; +} + +ExpiringTimeTracker::ExpiringTimeTracker(const calculatorType durationCalculator, const uint32_t creationTimeMs) : + timeoutTemplate(0) +{ + setDuration(durationCalculator); + _start = creationTimeMs; +} + +uint32_t ExpiringTimeTracker::duration() const +{ + if(useCalculator) + return _durationCalculator(); + + return getTimeout(); +} + +IRAM_ATTR // called from ISR +void ExpiringTimeTracker::setTimeout(const uint32_t newUserTimeout) +{ + _timeout = newUserTimeout; + _neverExpires = (newUserTimeout > timeMax()); // newUserTimeout < 0 is always false for uint32_t +} + +void ExpiringTimeTracker::setDuration(const uint32_t duration) +{ + setTimeout(duration); + useCalculator = false; +} + +void ExpiringTimeTracker::setDuration(const calculatorType durationCalculator) +{ + _durationCalculator = durationCalculator; + useCalculator = true; +} + +void ExpiringTimeTracker::setRemainingDuration(const uint32_t remainingDuration) +{ + setDuration(elapsedTime() + remainingDuration); +} + +void ExpiringTimeTracker::setRemainingDuration(const calculatorType remainingDurationCalculator) +{ + uint32_t currentElapsedTime = elapsedTime(); + setDuration([remainingDurationCalculator, currentElapsedTime](){ return currentElapsedTime + remainingDurationCalculator(); }); +} + +uint32_t ExpiringTimeTracker::remainingDuration() const +{ + uint32_t remainingDuration = 0; + + if(!expired()) // If expired, overflow will probably occur for remainingDuration calculation. + { + remainingDuration = duration() - elapsedTime(); + } + + return remainingDuration; +} + +uint32_t ExpiringTimeTracker::elapsedTime() const +{ + return millis() - _start; +} + +bool ExpiringTimeTracker::expired() const +{ + if(useCalculator) + return elapsedTime() >= duration(); + + return expiredOneShot(); +} + +void ExpiringTimeTracker::reset() +{ + timeoutTemplate::reset(); +} + +void ExpiringTimeTracker::reset(const uint32_t newDuration) +{ + setDuration(newDuration); + ExpiringTimeTracker::reset(); +} + +void ExpiringTimeTracker::reset(const calculatorType newDurationCalculator) +{ + setDuration(newDurationCalculator); + ExpiringTimeTracker::reset(); +} + +ExpiringTimeTracker::operator bool() const +{ + return ExpiringTimeTracker::expired(); +} diff --git a/libraries/ESP8266WiFiMesh/src/ExpiringTimeTracker.h b/libraries/ESP8266WiFiMesh/src/ExpiringTimeTracker.h new file mode 100644 index 0000000000..4a83f979ae --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/ExpiringTimeTracker.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#ifndef __EXPIRINGTIMETRACKER_H__ +#define __EXPIRINGTIMETRACKER_H__ + +#include +#include + +class ExpiringTimeTracker : private esp8266::polledTimeout::oneShotMs { + +public: + + using calculatorType = std::function; + + virtual ~ExpiringTimeTracker() = default; + + ExpiringTimeTracker(const uint32_t duration, const uint32_t creationTimeMs = millis()); + ExpiringTimeTracker(const calculatorType durationCalculator, const uint32_t creationTimeMs = millis()); + + uint32_t duration() const; + void setDuration(const uint32_t duration); + void setDuration(const calculatorType durationCalculator); + + uint32_t remainingDuration() const; + + /** + * Sets a new duration which includes the current elapsedTime(). This means elapsedTime() is not reset. + * Note that reset() will use this new duration, including the saved elapsedTime(). + */ + void setRemainingDuration(const uint32_t remainingDuration); + + /** + * Sets a new duration which includes the current elapsedTime(). This means elapsedTime() is not reset. + * Note that reset() will use this new duration, including the saved elapsedTime(). + */ + void setRemainingDuration(const calculatorType remainingDurationCalculator); + + /** + * Get the time since the ExpiringTimeTracker instance creation or the last reset(), whichever is more recent. + */ + uint32_t elapsedTime() const; + bool expired() const; + void reset(); + void reset(const uint32_t newDuration); + void reset(const calculatorType newDurationCalculator); + explicit operator bool() const; + +private: + + calculatorType _durationCalculator; + + void setTimeout(const uint32_t newUserTimeout); + + bool useCalculator = false; +}; + +#endif diff --git a/libraries/ESP8266WiFiMesh/src/FloodingMesh.cpp b/libraries/ESP8266WiFiMesh/src/FloodingMesh.cpp new file mode 100644 index 0000000000..1d6f064ae3 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/FloodingMesh.cpp @@ -0,0 +1,575 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#include "FloodingMesh.h" +#include "TypeConversionFunctions.h" +#include "JsonTranslator.h" +#include "Serializer.h" + +namespace +{ + namespace TypeCast = MeshTypeConversionFunctions; + + constexpr uint8_t MESSAGE_ID_LENGTH = 17; // 16 characters and one delimiter + constexpr uint8_t MESSAGE_COMPLETE = 255; + + char _metadataDelimiter = 23; // Defaults to 23 = End-of-Transmission-Block (ETB) control character in ASCII +} + +std::set FloodingMesh::availableFloodingMeshes = {}; + +void floodingMeshDelay(const uint32_t durationMs) +{ + ExpiringTimeTracker timeout(durationMs); + + do + { + // We want to delay before performMeshMaintenance() so background tasks can be managed first. + // Initial while combined with YieldAndDelayMs polledTimeout::YieldPolicy is not suitable since the delay then occurs before evaluating the condition (meaning durationMs = 1 never executes the loop interior). + delay(1); + FloodingMesh::performMeshMaintenance(); + } + while(!timeout); +} + +FloodingMesh::FloodingMesh(messageHandlerType messageHandler, const String &meshPassword, const uint8_t espnowEncryptedConnectionKey[EspnowProtocolInterpreter::encryptedConnectionKeyLength], + const uint8_t espnowHashKey[EspnowProtocolInterpreter::hashKeyLength], const String &ssidPrefix, + const String &ssidSuffix, const bool verboseMode, const uint8 meshWiFiChannel) + : _espnowBackend( + [this](const String &request, MeshBackendBase &meshInstance){ return _defaultRequestHandler(request, meshInstance); }, + [this](const String &response, MeshBackendBase &meshInstance){ return _defaultResponseHandler(response, meshInstance); }, + [this](int numberOfNetworks, MeshBackendBase &meshInstance){ return _defaultNetworkFilter(numberOfNetworks, meshInstance); }, + [this](String &firstTransmission, EspnowMeshBackend &meshInstance){ return _defaultBroadcastFilter(firstTransmission, meshInstance); }, + meshPassword, espnowEncryptedConnectionKey, espnowHashKey, ssidPrefix, ssidSuffix, verboseMode, meshWiFiChannel) +{ + setMessageHandler(messageHandler); + restoreDefaultTransmissionOutcomesUpdateHook(); + restoreDefaultResponseTransmittedHook(); +} + +FloodingMesh::FloodingMesh(messageHandlerType messageHandler, const String &meshPassword, const String &espnowEncryptedConnectionKeySeed, const String &espnowHashKeySeed, + const String &ssidPrefix, const String &ssidSuffix, const bool verboseMode, const uint8 meshWiFiChannel) + : FloodingMesh(messageHandler, meshPassword, (const uint8_t[EspnowProtocolInterpreter::encryptedConnectionKeyLength]){0}, + (const uint8_t[EspnowProtocolInterpreter::hashKeyLength]){0}, ssidPrefix, ssidSuffix, verboseMode, meshWiFiChannel) +{ + getEspnowMeshBackend().setEspnowEncryptedConnectionKey(espnowEncryptedConnectionKeySeed); + getEspnowMeshBackend().setEspnowHashKey(espnowHashKeySeed); +} + +FloodingMesh::FloodingMesh(const String &serializedMeshState, messageHandlerType messageHandler, const String &meshPassword, + const uint8_t espnowEncryptedConnectionKey[EspnowProtocolInterpreter::encryptedConnectionKeyLength], + const uint8_t espnowHashKey[EspnowProtocolInterpreter::hashKeyLength], const String &ssidPrefix, + const String &ssidSuffix, const bool verboseMode, const uint8 meshWiFiChannel) + : FloodingMesh(messageHandler, meshPassword, espnowEncryptedConnectionKey, espnowHashKey, ssidPrefix, ssidSuffix, verboseMode, meshWiFiChannel) +{ + loadMeshState(serializedMeshState); +} + +FloodingMesh::FloodingMesh(const String &serializedMeshState, messageHandlerType messageHandler, const String &meshPassword, + const String &espnowEncryptedConnectionKeySeed, const String &espnowHashKeySeed, const String &ssidPrefix, + const String &ssidSuffix, const bool verboseMode, const uint8 meshWiFiChannel) + : FloodingMesh(messageHandler, meshPassword, espnowEncryptedConnectionKeySeed, espnowHashKeySeed, ssidPrefix, ssidSuffix, verboseMode, meshWiFiChannel) +{ + loadMeshState(serializedMeshState); +} + +FloodingMesh::~FloodingMesh() +{ + availableFloodingMeshes.erase(this); +} + +void FloodingMesh::begin() +{ + // Initialise the mesh node + getEspnowMeshBackend().begin(); + + // Used for encrypted broadcasts + getEspnowMeshBackend().setEncryptedConnectionsSoftLimit(3); + + availableFloodingMeshes.insert(this); // Returns std::pair +} + +void FloodingMesh::activateAP() +{ + getEspnowMeshBackend().activateAP(); +} + +void FloodingMesh::deactivateAP() +{ + MeshBackendBase::deactivateAP(); +} + +void FloodingMesh::performMeshMaintenance() +{ + for(FloodingMesh *meshInstance : availableFloodingMeshes) + { + meshInstance->performMeshInstanceMaintenance(); + } +} + +void FloodingMesh::performMeshInstanceMaintenance() +{ + EspnowMeshBackend::performEspnowMaintenance(); + + for(std::list>::iterator backlogIterator = getForwardingBacklog().begin(); backlogIterator != getForwardingBacklog().end(); ) + { + std::pair &messageData = *backlogIterator; + if(messageData.second) // message encrypted + { + getMacIgnoreList() = messageData.first.substring(0, 12) + ','; // The message should contain the messageID first + encryptedBroadcastKernel(messageData.first); + getMacIgnoreList() = emptyString; + } + else + { + broadcastKernel(messageData.first); + } + + backlogIterator = getForwardingBacklog().erase(backlogIterator); + + EspnowMeshBackend::performEspnowMaintenance(); // It is best to performEspnowMaintenance frequently to keep the Espnow backend responsive. Especially if each encryptedBroadcast takes a lot of time. + } +} + +String FloodingMesh::serializeMeshState() const +{ + String connectionState = getEspnowMeshBackendConst().serializeUnencryptedConnection(); + uint32_t unsyncMsgID = 0; + JsonTranslator::getUnsynchronizedMessageID(connectionState, unsyncMsgID); + + return Serializer::serializeMeshState(String(unsyncMsgID), String(_messageCount)); +} + +void FloodingMesh::loadMeshState(const String &serializedMeshState) +{ + using namespace JsonTranslator; + + if(!getMeshMessageCount(serializedMeshState, _messageCount)) + getEspnowMeshBackend().warningPrint(String(F("WARNING! serializedMeshState did not contain MeshMessageCount. Using default instead."))); + + String connectionState; + if(!getConnectionState(serializedMeshState, connectionState) || !getEspnowMeshBackend().addUnencryptedConnection(connectionState)) + { + getEspnowMeshBackend().warningPrint(String(F("WARNING! serializedMeshState did not contain unsynchronizedMessageID. Using default instead."))); + } +} + +String FloodingMesh::generateMessageID() +{ + char messageCountArray[5] = { 0 }; + snprintf(messageCountArray, 5, "%04X", _messageCount++); + uint8_t apMac[6] {0}; + return TypeCast::macToString(WiFi.softAPmacAddress(apMac)) + String(messageCountArray); // We use the AP MAC address as ID since it is what shows up during WiFi scans +} + +void FloodingMesh::broadcast(const String &message) +{ + assert(message.length() <= maxUnencryptedMessageLength()); + + String messageID = generateMessageID(); + + // Remove getEspnowMeshBackend().getMeshName() from the metadata below to broadcast to all ESP-NOW nodes regardless of MeshName. + String targetMeshName = getEspnowMeshBackend().getMeshName(); + + broadcastKernel(targetMeshName + String(metadataDelimiter()) + messageID + String(metadataDelimiter()) + message); +} + +void FloodingMesh::broadcastKernel(const String &message) +{ + getEspnowMeshBackend().broadcast(message); +} + +void FloodingMesh::setBroadcastReceptionRedundancy(const uint8_t redundancy) +{ + assert(redundancy < 255); + _broadcastReceptionRedundancy = redundancy; +} +uint8_t FloodingMesh::getBroadcastReceptionRedundancy() const { return _broadcastReceptionRedundancy; } + +void FloodingMesh::encryptedBroadcast(const String &message) +{ + assert(message.length() <= maxEncryptedMessageLength()); + + String messageID = generateMessageID(); + + encryptedBroadcastKernel(messageID + String(metadataDelimiter()) + message); +} + +void FloodingMesh::encryptedBroadcastKernel(const String &message) +{ + getEspnowMeshBackend().attemptAutoEncryptingTransmission(message, true); +} + +void FloodingMesh::clearMessageLogs() +{ + _messageIDs.clear(); + std::queue().swap(_messageIdOrder); +} + +void FloodingMesh::clearForwardingBacklog() +{ + getForwardingBacklog().clear(); +} + +void FloodingMesh::setMessageHandler(const messageHandlerType messageHandler) { _messageHandler = messageHandler; } +FloodingMesh::messageHandlerType FloodingMesh::getMessageHandler() const { return _messageHandler; } + +void FloodingMesh::setOriginMac(const uint8_t *macArray) +{ + std::copy_n(macArray, 6, _originMac); +} + +String FloodingMesh::getOriginMac() const { return TypeCast::macToString(_originMac); } +uint8_t *FloodingMesh::getOriginMac(uint8_t *macArray) const +{ + std::copy_n(_originMac, 6, macArray); + return macArray; +} + +std::list> & FloodingMesh::getForwardingBacklog() { return _forwardingBacklog; } + +String & FloodingMesh::getMacIgnoreList() { return _macIgnoreList; } + +uint32_t FloodingMesh::maxUnencryptedMessageLength() const +{ + return getEspnowMeshBackendConst().getMaxMessageLength() - MESSAGE_ID_LENGTH - (getEspnowMeshBackendConst().getMeshName().length() + 1); // Need room for mesh name + delimiter +} + +uint32_t FloodingMesh::maxEncryptedMessageLength() const +{ + // Need 1 extra delimiter character for maximum metadata efficiency (makes it possible to store exactly 18 MACs in metadata by adding an extra transmission) + return getEspnowMeshBackendConst().getMaxMessageLength() - MESSAGE_ID_LENGTH - 1; +} + +void FloodingMesh::setMessageLogSize(const uint16_t messageLogSize) +{ + assert(messageLogSize >= 1); + _messageLogSize = messageLogSize; +} +uint16_t FloodingMesh::messageLogSize() const { return _messageLogSize; } + +void FloodingMesh::setMetadataDelimiter(const char metadataDelimiter) +{ + // Using HEX number characters as a delimiter is a bad idea regardless of broadcast type, since they are always in the broadcast metadata. + // We therefore check for those characters below. + assert(metadataDelimiter < '0' || '9' < metadataDelimiter); + assert(metadataDelimiter < 'A' || 'F' < metadataDelimiter); + assert(metadataDelimiter < 'a' || 'f' < metadataDelimiter); + + // Reserved for encryptedBroadcast for now + assert(metadataDelimiter != ','); + + _metadataDelimiter = metadataDelimiter; +} +char FloodingMesh::metadataDelimiter() { return _metadataDelimiter; } + +EspnowMeshBackend &FloodingMesh::getEspnowMeshBackend() +{ + return _espnowBackend; +} + +const EspnowMeshBackend &FloodingMesh::getEspnowMeshBackendConst() const +{ + return _espnowBackend; +} + +bool FloodingMesh::insertPreliminaryMessageID(const uint64_t messageID) +{ + uint8_t apMacArray[6] = { 0 }; + if(messageID >> 16 == TypeCast::macToUint64(WiFi.softAPmacAddress(apMacArray))) + return false; // The node should not receive its own messages. + + auto insertionResult = _messageIDs.emplace(messageID, 0); // Returns std::pair + + if(insertionResult.second) // Insertion succeeded. + updateMessageQueue(insertionResult.first); + else if(insertionResult.first->second < getBroadcastReceptionRedundancy()) // messageID exists but not with desired redundancy + insertionResult.first->second++; + else + return false; // messageID already existed in _messageIDs with desired redundancy + + return true; +} + +bool FloodingMesh::insertCompletedMessageID(const uint64_t messageID) +{ + uint8_t apMacArray[6] = { 0 }; + if(messageID >> 16 == TypeCast::macToUint64(WiFi.softAPmacAddress(apMacArray))) + return false; // The node should not receive its own messages. + + auto insertionResult = _messageIDs.emplace(messageID, MESSAGE_COMPLETE); // Returns std::pair + + if(insertionResult.second) // Insertion succeeded. + updateMessageQueue(insertionResult.first); + else if(insertionResult.first->second < MESSAGE_COMPLETE) // messageID exists but is not complete + insertionResult.first->second = MESSAGE_COMPLETE; + else + return false; // messageID already existed in _messageIDs and is complete + + return true; +} + +void FloodingMesh::updateMessageQueue(const messageQueueElementType messageIterator) +{ + _messageIdOrder.emplace(messageIterator); + + if(_messageIDs.size() > messageLogSize()) + { + _messageIDs.erase(_messageIdOrder.front()); + _messageIdOrder.pop(); + assert(_messageIDs.size() == messageLogSize()); // If this is false we either have too many elements in messageIDs or we deleted too many elements. + assert(_messageIDs.size() == _messageIdOrder.size()); // The containers should always be in sync + } +} + +void FloodingMesh::restoreDefaultRequestHandler() +{ + getEspnowMeshBackend().setRequestHandler([this](const String &request, MeshBackendBase &meshInstance){ return _defaultRequestHandler(request, meshInstance); }); +} + +void FloodingMesh::restoreDefaultResponseHandler() +{ + getEspnowMeshBackend().setResponseHandler([this](const String &response, MeshBackendBase &meshInstance){ return _defaultResponseHandler(response, meshInstance); }); +} + +void FloodingMesh::restoreDefaultNetworkFilter() +{ + getEspnowMeshBackend().setNetworkFilter([this](int numberOfNetworks, MeshBackendBase &meshInstance){ return _defaultNetworkFilter(numberOfNetworks, meshInstance); }); +} + +void FloodingMesh::restoreDefaultBroadcastFilter() +{ + getEspnowMeshBackend().setBroadcastFilter([this](String &firstTransmission, EspnowMeshBackend &meshInstance){ return _defaultBroadcastFilter(firstTransmission, meshInstance); }); +} + +void FloodingMesh::restoreDefaultTransmissionOutcomesUpdateHook() +{ + /* Optional way of doing things. Lambda is supposedly better https://stackoverflow.com/a/36596295 . + + using namespace std::placeholders; + + getEspnowMeshBackend().setTransmissionOutcomesUpdateHook(std::bind(&FloodingMesh::_defaultTransmissionOutcomesUpdateHook, this, _1)); + */ + + getEspnowMeshBackend().setTransmissionOutcomesUpdateHook([this](MeshBackendBase &meshInstance){ return _defaultTransmissionOutcomesUpdateHook(meshInstance); }); +} + +void FloodingMesh::restoreDefaultResponseTransmittedHook() +{ + getEspnowMeshBackend().setResponseTransmittedHook([this](bool transmissionSuccessful, const String &response, const uint8_t *recipientMac, uint32_t responseIndex, EspnowMeshBackend &meshInstance) + { return _defaultResponseTransmittedHook(transmissionSuccessful, response, recipientMac, responseIndex, meshInstance); }); +} + +/** + * Callback for when other nodes send you a request + * + * @param request The request string received from another node in the mesh + * @param meshInstance The MeshBackendBase instance that called the function. + * @return The string to send back to the other node. For ESP-NOW, return an empty string ("") if no response should be sent. + */ +String FloodingMesh::_defaultRequestHandler(const String &request, MeshBackendBase &meshInstance) +{ + (void)meshInstance; // This is useful to remove a "unused parameter" compiler warning. Does nothing else. + + String broadcastTarget; + String remainingRequest = request; + + if(request.charAt(0) == metadataDelimiter()) + { + int32_t broadcastTargetEndIndex = request.indexOf(metadataDelimiter(), 1); + + if(broadcastTargetEndIndex == -1) + return emptyString; // metadataDelimiter not found + + broadcastTarget = request.substring(1, broadcastTargetEndIndex + 1); // Include delimiter + remainingRequest.remove(0, broadcastTargetEndIndex + 1); + } + + int32_t messageIDEndIndex = remainingRequest.indexOf(metadataDelimiter()); + + if(messageIDEndIndex == -1) + return emptyString; // metadataDelimiter not found + + uint64_t messageID = TypeCast::stringToUint64(remainingRequest.substring(0, messageIDEndIndex)); + + if(insertCompletedMessageID(messageID)) + { + uint8_t originMacArray[6] = { 0 }; + setOriginMac(TypeCast::uint64ToMac(messageID >> 16, originMacArray)); // messageID consists of MAC + 16 bit counter + + String message = remainingRequest; + message.remove(0, messageIDEndIndex + 1); // This approach avoids the null value removal of substring() + + if(getMessageHandler()(message, *this)) + { + message = broadcastTarget + remainingRequest.substring(0, messageIDEndIndex + 1) + message; + assert(message.length() <= _espnowBackend.getMaxMessageLength()); + getForwardingBacklog().emplace_back(message, getEspnowMeshBackend().receivedEncryptedTransmission()); + } + } + + return emptyString; +} + +/** + * Callback for when you get a response from other nodes + * + * @param response The response string received from another node in the mesh + * @param meshInstance The MeshBackendBase instance that called the function. + * @return The status code resulting from the response, as an int + */ +TransmissionStatusType FloodingMesh::_defaultResponseHandler(const String &response, MeshBackendBase &meshInstance) +{ + TransmissionStatusType statusCode = TransmissionStatusType::TRANSMISSION_COMPLETE; + + getEspnowMeshBackend().warningPrint(String(F("WARNING! Response to FloodingMesh broadcast received, but none is expected!"))); + + (void)response; // This is useful to remove a "unused parameter" compiler warning. Does nothing else. + (void)meshInstance; // This is useful to remove a "unused parameter" compiler warning. Does nothing else. + + return statusCode; +} + +/** + * Callback used to decide which networks to connect to once a WiFi scan has been completed. + * + * @param numberOfNetworks The number of networks found in the WiFi scan. + * @param meshInstance The MeshBackendBase instance that called the function. + */ +void FloodingMesh::_defaultNetworkFilter(const int numberOfNetworks, MeshBackendBase &meshInstance) +{ + // Note that the network index of a given node may change whenever a new scan is done. + for (int networkIndex = 0; networkIndex < numberOfNetworks; ++networkIndex) + { + String currentSSID = WiFi.SSID(networkIndex); + int meshNameIndex = currentSSID.indexOf(meshInstance.getMeshName()); + + // Connect to any APs which contain meshInstance.getMeshName() + if(meshNameIndex >= 0) + { + if(getMacIgnoreList().indexOf(TypeCast::macToString(WiFi.BSSID(networkIndex))) == -1) // If the BSSID is not in the ignore list + { + if(EspnowMeshBackend *espnowInstance = TypeCast::meshBackendCast(&meshInstance)) + { + espnowInstance->connectionQueue().emplace_back(networkIndex); + } + else + { + Serial.println(String(F("Invalid mesh backend!"))); + } + } + } + } +} + +/** + * Callback used to decide which broadcast messages to accept. Only called for the first transmission in each broadcast. + * If true is returned from this callback, the first broadcast transmission is saved until the entire broadcast message has been received. + * The complete broadcast message will then be sent to the requestHandler. + * If false is returned from this callback, the broadcast message is discarded. + * Note that the BroadcastFilter may be called multiple times for messages that are discarded in this way, but is only called once for accepted messages. + * + * @param firstTransmission The first transmission of the broadcast. + * @param meshInstance The EspnowMeshBackend instance that called the function. + * + * @return True if the broadcast should be accepted. False otherwise. + */ +bool FloodingMesh::_defaultBroadcastFilter(String &firstTransmission, EspnowMeshBackend &meshInstance) +{ + // This broadcastFilter will accept a transmission if it contains the metadataDelimiter + // and as metaData either no targetMeshName or a targetMeshName that matches the MeshName of meshInstance + // and insertPreliminaryMessageID(messageID) returns true. + + // Broadcast firstTransmission String structure: targetMeshName+messageID+message. + + int32_t metadataEndIndex = firstTransmission.indexOf(metadataDelimiter()); + + if(metadataEndIndex == -1) + return false; // metadataDelimiter not found + + String targetMeshName = firstTransmission.substring(0, metadataEndIndex); + + if(!targetMeshName.isEmpty() && meshInstance.getMeshName() != targetMeshName) + { + return false; // Broadcast is for another mesh network + } + + int32_t messageIDEndIndex = firstTransmission.indexOf(metadataDelimiter(), metadataEndIndex + 1); + + if(messageIDEndIndex == -1) + return false; // metadataDelimiter not found + + uint64_t messageID = TypeCast::stringToUint64(firstTransmission.substring(metadataEndIndex + 1, messageIDEndIndex)); + + if(insertPreliminaryMessageID(messageID)) + { + // Add broadcast identifier to stored message and mark as accepted broadcast. + firstTransmission = String(metadataDelimiter()) + firstTransmission; + return true; + } + + return false; // Broadcast has already been received the maximum number of times +} + +/** + * Once passed to the setTransmissionOutcomesUpdateHook method of the ESP-NOW backend, + * this function will be called after each update of the latestTransmissionOutcomes vector during attemptTransmission. + * (which happens after each individual transmission has finished) + * + * @param meshInstance The MeshBackendBase instance that called the function. + * + * @return True if attemptTransmission should continue with the next entry in the connectionQueue. False if attemptTransmission should stop. + */ +bool FloodingMesh::_defaultTransmissionOutcomesUpdateHook(MeshBackendBase &meshInstance) +{ + (void)meshInstance; // This is useful to remove a "unused parameter" compiler warning. Does nothing else. + + return true; +} + +/** + * Once passed to the setResponseTransmittedHook method of the ESP-NOW backend, + * this function will be called after each attempted ESP-NOW response transmission. + * In case of a successful response transmission, this happens just before the response is removed from the waiting list. + * Only the hook of the EspnowMeshBackend instance that is getEspnowRequestManager() will be called. + * + * @param transmissionSuccessful True if the response was transmitted successfully. False otherwise. + * @param response The sent response. + * @param recipientMac The MAC address the response was sent to. + * @param responseIndex The index of the response in the waiting list. + * @param meshInstance The EspnowMeshBackend instance that called the function. + * + * @return True if the response transmission process should continue with the next response in the waiting list. + * False if the response transmission process should stop after processing of the just sent response is complete. + */ +bool FloodingMesh::_defaultResponseTransmittedHook(bool transmissionSuccessful, const String &response, const uint8_t *recipientMac, const uint32_t responseIndex, EspnowMeshBackend &meshInstance) +{ + (void)transmissionSuccessful; // This is useful to remove a "unused parameter" compiler warning. Does nothing else. + (void)response; + (void)recipientMac; + (void)responseIndex; + (void)meshInstance; + + return true; +} diff --git a/libraries/ESP8266WiFiMesh/src/FloodingMesh.h b/libraries/ESP8266WiFiMesh/src/FloodingMesh.h new file mode 100644 index 0000000000..43131ca3fd --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/FloodingMesh.h @@ -0,0 +1,343 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#ifndef __FLOODINGMESH_H__ +#define __FLOODINGMESH_H__ + +#include "EspnowMeshBackend.h" +#include +#include + +/** + * An alternative to standard delay(). Will continuously call performMeshMaintenance() during the waiting time, so that the FloodingMesh node remains responsive. + * Note that if there is a lot of FloodingMesh transmission activity to the node during the floodingMeshDelay, the desired duration may be overshot by several ms. + * Thus, if precise timing is required, use standard delay() instead. + * + * Should not be used inside callbacks since performMeshMaintenance() can alter the ESP-NOW state. + * + * @param durationMs The shortest allowed delay duration, in milliseconds. + */ +void floodingMeshDelay(const uint32_t durationMs); + +class FloodingMesh { + +public: + + using messageHandlerType = std::function; + + /** + * FloodingMesh constructor method. Creates a FloodingMesh node, ready to be initialised. + * + * @param messageHandler The callback handler responsible for dealing with messages received from the mesh. + * @param meshPassword The WiFi password for the mesh network. + * @param espnowEncryptedConnectionKey An uint8_t array containing the secret key used by the EspnowMeshBackend instance for creating encrypted ESP-NOW connections. + * @param espnowHashKey An uint8_t array containing the secret key used by the EspnowMeshBackend instance to generate HMACs for encrypted ESP-NOW connections. + * @param ssidPrefix The prefix (first part) of the node SSID. + * @param ssidSuffix The suffix (last part) of the node SSID. + * @param verboseMode Determines if we should print the events occurring in the library to Serial. Off by default. This setting is shared by all EspnowMeshBackend instances. + * @param meshWiFiChannel The WiFi channel used by the mesh network. Valid values are integers from 1 to 13. Defaults to 1. + * WARNING: The ESP8266 has only one WiFi channel, and the station/client mode is always prioritized for channel selection. + * This can cause problems if several mesh instances exist on the same ESP8266 and use different WiFi channels. + * In such a case, whenever the station of one mesh instance connects to an AP, it will silently force the + * WiFi channel of any active AP on the ESP8266 to match that of the station. This will cause disconnects and possibly + * make it impossible for other stations to detect the APs whose WiFi channels have changed. + * + */ + FloodingMesh(messageHandlerType messageHandler, const String &meshPassword, const uint8_t espnowEncryptedConnectionKey[EspnowProtocolInterpreter::encryptedConnectionKeyLength], + const uint8_t espnowHashKey[EspnowProtocolInterpreter::hashKeyLength], const String &ssidPrefix, + const String &ssidSuffix, const bool verboseMode = false, const uint8 meshWiFiChannel = 1); + + /** + * FloodingMesh constructor method. Creates a FloodingMesh node, ready to be initialised. + * + * @param messageHandler The callback handler responsible for dealing with messages received from the mesh. + * @param meshPassword The WiFi password for the mesh network. + * @param espnowEncryptedConnectionKeySeed A string containing the seed that will generate the secret key used by the EspnowMeshBackend instance for creating encrypted ESP-NOW connections. + * @param espnowHashKeySeed A string containing the seed that will generate the secret key used by the EspnowMeshBackend to generate HMACs for encrypted ESP-NOW connections. + * @param ssidPrefix The prefix (first part) of the node SSID. + * @param ssidSuffix The suffix (last part) of the node SSID. + * @param verboseMode Determines if we should print the events occurring in the library to Serial. Off by default. This setting is shared by all EspnowMeshBackend instances. + * @param meshWiFiChannel The WiFi channel used by the mesh network. Valid values are integers from 1 to 13. Defaults to 1. + * WARNING: The ESP8266 has only one WiFi channel, and the station/client mode is always prioritized for channel selection. + * This can cause problems if several mesh instances exist on the same ESP8266 and use different WiFi channels. + * In such a case, whenever the station of one mesh instance connects to an AP, it will silently force the + * WiFi channel of any active AP on the ESP8266 to match that of the station. This will cause disconnects and possibly + * make it impossible for other stations to detect the APs whose WiFi channels have changed. + * + */ + FloodingMesh(messageHandlerType messageHandler, const String &meshPassword, const String &espnowEncryptedConnectionKeySeed, const String &espnowHashKeySeed, + const String &ssidPrefix, const String &ssidSuffix, const bool verboseMode = false, const uint8 meshWiFiChannel = 1); + + /** + * This constructor should be used in combination with serializeMeshState() when the node has gone to sleep while other nodes stayed awake. + * Otherwise the message ID will be reset after sleep, which means that the nodes that stayed awake may ignore new broadcasts for a while. + * + * @param serializedMeshState A String with a serialized mesh node state that the node should use. + */ + FloodingMesh(const String &serializedMeshState, messageHandlerType messageHandler, const String &meshPassword, + const uint8_t espnowEncryptedConnectionKey[EspnowProtocolInterpreter::encryptedConnectionKeyLength], + const uint8_t espnowHashKey[EspnowProtocolInterpreter::hashKeyLength], const String &ssidPrefix, + const String &ssidSuffix, const bool verboseMode = false, const uint8 meshWiFiChannel = 1); + + /** + * This constructor should be used in combination with serializeMeshState() when the node has gone to sleep while other nodes stayed awake. + * Otherwise the message ID will be reset after sleep, which means that the nodes that stayed awake may ignore new broadcasts for a while. + * + * @param serializedMeshState A String with a serialized mesh node state that the node should use. + */ + FloodingMesh(const String &serializedMeshState, messageHandlerType messageHandler, const String &meshPassword, const String &espnowEncryptedConnectionKeySeed, + const String &espnowHashKeySeed, const String &ssidPrefix, const String &ssidSuffix, const bool verboseMode = false, const uint8 meshWiFiChannel = 1); + + virtual ~FloodingMesh(); + + /** + * The method responsible for initialising this FloodingMesh instance. + */ + void begin(); + + /** + * Activate the WiFi access point of this ESP8266. + * This makes it possible to find the node through scans, and also makes it possible to recover from an encrypted ESP-NOW connection where only the other node is encrypted. + * Required for encryptedBroadcast() usage, but also slows down the start-up of the node. + * + * Note that only one AP can be active at a time in total (there is only one WiFi radio on the ESP8266), and this will always be the one which was last activated. + * Thus the AP is shared by all backends. + * All FloodingMesh instances can still broadcast messages though, even if their AP is not visible. + */ + void activateAP(); + + /** + * Deactivate the WiFi access point of this ESP8266. + * + * Note that only one AP can be active at a time in total (there is only one WiFi radio on the ESP8266), and this will always be the one which was last activated. + * Thus the AP is shared by all backends. + * All FloodingMesh instances can still broadcast messages though, even if their AP is not visible. + */ + static void deactivateAP(); + + /** + * Performs maintenance for all available Flooding Mesh instances + */ + static void performMeshMaintenance(); + + /** + * Performs maintenance for this particular Flooding Mesh instance + */ + void performMeshInstanceMaintenance(); + + /** + * Serialize the current mesh node state. Useful to save a state before the node goes to sleep. + * Note that this saves the current state only, so if a broadcast is made after this, the stored state is invalid. + * + * @return A string with the serialized current mesh node state. + */ + String serializeMeshState() const; + + /** + * Make an unencrypted broadcast to the entire mesh network. + * + * activateAP() must have been called for nodes to be able to receive broadcasts. Nodes can however send broadcasts even if their AP is off. + * + * It is recommended that there is at most one new message transmitted in the mesh every 10, 20, 30 ms for messages up to length maxUnencryptedMessageLength()*n, + * where n is (roughly, depending on mesh name length) 1/4, 3/5 and 1 respectively. If transmissions are more frequent than this, message loss will increase. + * + * @param message The message to broadcast. Maximum message length is given by maxUnencryptedMessageLength(). The longer the message, the longer the transmission time. + */ + void broadcast(const String &message); + + /** + * Set the maximum number of redundant copies that will be received of every broadcast. (from different senders) + * A greater number increases the likelihood that at least one of the copies is received successfully, but will also use more RAM. + * + * @param redundancy The maximum number of extra copies that will be accepted. Defaults to 2. Valid values are 0 to 254. + */ + void setBroadcastReceptionRedundancy(const uint8_t redundancy); + uint8_t getBroadcastReceptionRedundancy() const; + + /** + * Make an encrypted broadcast to the entire mesh network. + * + * activateAP() must have been called for encryptedBroadcast to work. + * + * ########## WARNING! This an experimental feature. API may change at any time. Only use if you like it when things break. ########## + * Will be very slow compared to unencrypted broadcasts. Probably works OK in a small mesh with a maximum of 2-3 new messages transmitted in the mesh every second. + * Because of the throughput difference, mixing encrypted and unencrypted broadcasts is not recommended if there are frequent mesh broadcasts (multiple per second), + * since a lot of unencrypted broadcasts can build up while a single encrypted broadcast is sent. + * + * It is recommended that verboseMode is turned off if using this, to avoid slowdowns due to excessive Serial printing. + * + * @param message The message to broadcast. Maximum message length is given by maxEncryptedMessageLength(). The longer the message, the longer the transmission time. + */ + void encryptedBroadcast(const String &message); + + /** + * Clear the logs used for remembering which messages this node has received from the mesh network. + */ + void clearMessageLogs(); + + /** + * Remove all messages received from the mesh network which are stored waiting to be forwarded by this node. + */ + void clearForwardingBacklog(); + + /** + * Set the callback handler responsible for dealing with messages received from the mesh. + * + * @param messageHandler The message handler callback function to use. + */ + void setMessageHandler(const messageHandlerType messageHandler); + messageHandlerType getMessageHandler() const; + + /** + * Get the origin AP MAC address of the most recently received mesh message. + * Returns a String. + * + * @return A String filled with a hexadecimal representation of the MAC, without delimiters. + */ + String getOriginMac() const; + + /** + * Get the origin AP MAC address of the most recently received mesh message. + * Returns a uint8_t array. + * + * @param macArray The array that should store the MAC address. Must be at least 6 bytes. + * @return macArray filled with the origin MAC. + */ + uint8_t *getOriginMac(uint8_t *macArray) const; + + /** + * The number of received messageID:s that will be stored by the node. Used to remember which messages have been received. + * Setting this too low will cause the same message to be received many times. + * Setting this too high will cause the node to run out of RAM. + * In practice, setting this value to more than 1337 is probably a bad idea since the node will run out of RAM quickly and crash as a result. + * + * Defaults to 100. + * + * @param messageLogSize The size of the message log for this FloodingMesh instance. Valid values are 1 to 65535 (uint16_t_max). + * If a value close to the maximum is chosen, there is a high risk the node will ignore transmissions on messageID rollover if they are sent only by one node + * (especially if some transmissions are missed), since the messageID also uses uint16_t. + */ + void setMessageLogSize(const uint16_t messageLogSize); + uint16_t messageLogSize() const; + + /** + * Hint: Use String.length() to get the ASCII length of a String. + * + * @return The maximum length in bytes an unencrypted ASCII message is allowed to be when broadcasted by this node. + * Note that non-ASCII characters usually require at least two bytes each. + * Also note that for unencrypted messages the maximum size will depend on getEspnowMeshBackend().getMeshName().length() + */ + uint32_t maxUnencryptedMessageLength() const; + + /** + * Hint: Use String.length() to get the ASCII length of a String. + * + * @return The maximum length in bytes an encrypted ASCII message is allowed to be when broadcasted by this node. + * Note that non-ASCII characters usually require at least two bytes each. + */ + uint32_t maxEncryptedMessageLength() const; + + /** + * Set the delimiter character used for metadata by every FloodingMesh instance. + * Using characters found in the mesh name or in HEX numbers is unwise, as is using ','. + * + * @param metadataDelimiter The metadata delimiter character to use. + * Defaults to 23 = End-of-Transmission-Block (ETB) control character in ASCII + */ + static void setMetadataDelimiter(const char metadataDelimiter); + static char metadataDelimiter(); + + /* + * Gives you access to the EspnowMeshBackend used by the mesh node. + * The backend handles all mesh communication, and modifying it allows you to change every aspect of the mesh behaviour. + * Random interactions with the backend have a high chance of breaking the mesh network, + * and so are discouraged for those who prefer it when things just work. + */ + EspnowMeshBackend &getEspnowMeshBackend(); + const EspnowMeshBackend &getEspnowMeshBackendConst() const; + + void restoreDefaultRequestHandler(); + void restoreDefaultResponseHandler(); + void restoreDefaultNetworkFilter(); + void restoreDefaultBroadcastFilter(); + void restoreDefaultTransmissionOutcomesUpdateHook(); + void restoreDefaultResponseTransmittedHook(); + +protected: + + using messageQueueElementType = std::map::iterator; + + static std::set availableFloodingMeshes; + + String generateMessageID(); + + void broadcastKernel(const String &message); + + void encryptedBroadcastKernel(const String &message); + + bool insertPreliminaryMessageID(const uint64_t messageID); + bool insertCompletedMessageID(const uint64_t messageID); + void updateMessageQueue(const messageQueueElementType messageIterator); + + void loadMeshState(const String &serializedMeshState); + + /** + * Set the MAC address considered to be the origin AP MAC address of the most recently received mesh message. + * + * @param macArray An uint8_t array which contains the MAC address to store. The method will store the first 6 bytes of the array. + */ + void setOriginMac(const uint8_t *macArray); + + std::list> & getForwardingBacklog(); + + String & getMacIgnoreList(); // Experimental, may break in the future. + +private: + + EspnowMeshBackend _espnowBackend; + + messageHandlerType _messageHandler; + + std::map _messageIDs = {}; + std::queue _messageIdOrder = {}; + std::list> _forwardingBacklog = {}; + + String _macIgnoreList; + + String _defaultRequestHandler(const String &request, MeshBackendBase &meshInstance); + TransmissionStatusType _defaultResponseHandler(const String &response, MeshBackendBase &meshInstance); + void _defaultNetworkFilter(const int numberOfNetworks, MeshBackendBase &meshInstance); + bool _defaultBroadcastFilter(String &firstTransmission, EspnowMeshBackend &meshInstance); + bool _defaultTransmissionOutcomesUpdateHook(MeshBackendBase &meshInstance); + bool _defaultResponseTransmittedHook(bool transmissionSuccessful, const String &response, const uint8_t *recipientMac, const uint32_t responseIndex, EspnowMeshBackend &meshInstance); + + uint8_t _originMac[6] = {0}; + + uint16_t _messageCount = 0; + uint16_t _messageLogSize = 100; + + uint8_t _broadcastReceptionRedundancy = 2; +}; + +#endif diff --git a/libraries/ESP8266WiFiMesh/src/HeapMonitor.cpp b/libraries/ESP8266WiFiMesh/src/HeapMonitor.cpp new file mode 100644 index 0000000000..8bda148355 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/HeapMonitor.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#include "HeapMonitor.h" + +HeapMonitor::HeapMonitor(const uint32_t criticalHeapLevel, const uint32_t criticalHeapLevelBuffer) : + _criticalHeapLevel(criticalHeapLevel), _criticalHeapLevelBuffer(criticalHeapLevelBuffer) +{ } + +void HeapMonitor::setCriticalHeapLevel(const uint32_t freeHeapInBytes) +{ + _criticalHeapLevel = freeHeapInBytes; +} + +uint32_t HeapMonitor::getCriticalHeapLevel() const +{ + return _criticalHeapLevel; +} + +void HeapMonitor::setCriticalHeapLevelBuffer(const uint32_t bufferInBytes) +{ + _criticalHeapLevelBuffer = bufferInBytes; +} + +uint32_t HeapMonitor::getCriticalHeapLevelBuffer() const +{ + return _criticalHeapLevelBuffer; +} + +HeapMonitor::HeapStatus HeapMonitor::getHeapStatus() const +{ + HeapStatus heapStatus = HeapStatus::NOMINAL; + + uint32_t freeHeap = ESP.getFreeHeap(); + + if(freeHeap <= getCriticalHeapLevel()) + heapStatus = HeapStatus::CRITICAL; + else if(freeHeap <= getCriticalHeapLevel() + getCriticalHeapLevelBuffer()) + heapStatus = HeapStatus::LIMITED; + + return heapStatus; +} diff --git a/libraries/ESP8266WiFiMesh/src/HeapMonitor.h b/libraries/ESP8266WiFiMesh/src/HeapMonitor.h new file mode 100644 index 0000000000..44550e30d7 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/HeapMonitor.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2020 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#ifndef __ESPHEAPMONITOR_H__ +#define __ESPHEAPMONITOR_H__ + +#include + +class HeapMonitor { + +public: + + enum class HeapStatus + { + NOMINAL = 0, + LIMITED = 1, + CRITICAL = 2 + }; + + HeapMonitor(const uint32_t criticalHeapLevel, const uint32_t criticalHeapLevelBuffer); + + virtual ~HeapMonitor() = default; + + /** + * Set the maximum free heap level in bytes within which free heap size is considered critical. + */ + void setCriticalHeapLevel(const uint32_t freeHeapInBytes); + uint32_t getCriticalHeapLevel() const; + + /** + * Set the buffer of the critical heap level, within which free heap size is considered limited. + */ + void setCriticalHeapLevelBuffer(const uint32_t bufferInBytes); + uint32_t getCriticalHeapLevelBuffer() const; + + HeapStatus getHeapStatus() const; + +private: + + uint32_t _criticalHeapLevel; + uint32_t _criticalHeapLevelBuffer; + +}; + +#endif diff --git a/libraries/ESP8266WiFiMesh/src/JsonTranslator.cpp b/libraries/ESP8266WiFiMesh/src/JsonTranslator.cpp new file mode 100644 index 0000000000..fa9cf2b1ae --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/JsonTranslator.cpp @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#include "JsonTranslator.h" +#include "EspnowProtocolInterpreter.h" +#include "TypeConversionFunctions.h" + +namespace +{ + namespace TypeCast = MeshTypeConversionFunctions; + + bool getMac(const String &jsonString, const String &valueIdentifier, uint8_t *resultArray) + { + String jsonValue; + bool decoded = JsonTranslator::decode(jsonString, valueIdentifier, jsonValue); + + if(jsonValue.length() != 12) + decoded = false; // Mac String is always 12 characters long + + if(decoded) + TypeCast::stringToMac(jsonValue, resultArray); + + return decoded; + } +} + +namespace JsonTranslator +{ + int32_t getStartIndex(const String &jsonString, const String &valueIdentifier, const int32_t searchStartIndex) + { + int32_t startIndex = jsonString.indexOf(String('"') + valueIdentifier + F("\":"), searchStartIndex); + if(startIndex < 0) + return startIndex; + + startIndex += valueIdentifier.length() + 3; // Do not include valueIdentifier and associated characters + return startIndex; + } + + int32_t getEndIndex(const String &jsonString, const int32_t searchStartIndex) + { + int32_t endIndex = -1; + + if(jsonString[searchStartIndex] == '"') + { + endIndex = jsonString.indexOf('"', searchStartIndex + 1); + } + else if(jsonString[searchStartIndex] == '{') + { + uint32_t depth = 1; + bool withinString = false; + + for(uint32_t index = searchStartIndex + 1; depth != 0 && index < jsonString.length(); ++index) + { + if(jsonString[index] == '"') + withinString = !withinString; + else if(!withinString) + { + if(jsonString[index] == '{') + ++depth; + else if(jsonString[index] == '}') + --depth; + } + + if(depth == 0) + { + assert(index < 0x80000000); // Must avoid int32_t overflow + endIndex = index; + } + } + } + + return endIndex; + } + + String encode(std::initializer_list identifiersAndValues) + { + assert(identifiersAndValues.size() % 2 == 0); // List must consist of identifer-value pairs. + + String result = String('{'); + + bool isIdentifier = true; + for(String element : identifiersAndValues) + { + bool isObject = !isIdentifier && element[0] == '{'; + if(isObject) + result += element; + else + result += String('"') + element + String('"'); + + if(isIdentifier) + result += ':'; + else + result += ','; + + isIdentifier = !isIdentifier; + } + + result[result.length() - 1] = '}'; + + return result; + } + + String encodeLiterally(std::initializer_list identifiersAndValues) + { + assert(identifiersAndValues.size() % 2 == 0); // List must consist of identifer-value pairs. + + String result = String('{'); + + bool isIdentifier = true; + for(String element : identifiersAndValues) + { + if(isIdentifier) + result += String('"') + element + String('"') + ':'; + else + result += element + ','; + + isIdentifier = !isIdentifier; + } + + result[result.length() - 1] = '}'; + + return result; + } + + bool decode(const String &jsonString, const String &valueIdentifier, String &value) + { + int32_t startIndex = getStartIndex(jsonString, valueIdentifier); + if(startIndex < 0) + return false; + + int32_t endIndex = getEndIndex(jsonString, startIndex); + if(endIndex < 0) + return false; + + if(jsonString[startIndex] == '"') + ++startIndex; // Should not include starting " + else if(jsonString[startIndex] == '{') + ++endIndex; // Should include ending } + else + assert(false && F("Illegal JSON starting character!")); + + value = jsonString.substring(startIndex, endIndex); + return true; + } + + bool decode(const String &jsonString, const String &valueIdentifier, uint32_t &value) + { + String jsonValue; + bool decoded = decode(jsonString, valueIdentifier, jsonValue); + + if(decoded) + value = strtoul(jsonValue.c_str(), nullptr, 0); // strtoul stops reading input when an invalid character is discovered. + + return decoded; + } + + bool decodeRadix(const String &jsonString, const String &valueIdentifier, uint64_t &value, const uint8_t radix) + { + String jsonValue; + bool decoded = decode(jsonString, valueIdentifier, jsonValue); + + if(decoded) + value = TypeCast::stringToUint64(jsonValue, radix); + + return decoded; + } + + bool getConnectionState(const String &jsonString, String &result) + { + return decode(jsonString, FPSTR(jsonConnectionState), result); + } + + bool getPassword(const String &jsonString, String &result) + { + return decode(jsonString, FPSTR(jsonPassword), result); + } + + bool getOwnSessionKey(const String &jsonString, uint64_t &result) + { + return decodeRadix(jsonString, FPSTR(jsonOwnSessionKey), result); + } + + bool getPeerSessionKey(const String &jsonString, uint64_t &result) + { + return decodeRadix(jsonString, FPSTR(jsonPeerSessionKey), result); + } + + bool getPeerStaMac(const String &jsonString, uint8_t *resultArray) + { + return getMac(jsonString, FPSTR(jsonPeerStaMac), resultArray); + } + + bool getPeerApMac(const String &jsonString, uint8_t *resultArray) + { + return getMac(jsonString, FPSTR(jsonPeerApMac), resultArray); + } + + bool getDuration(const String &jsonString, uint32_t &result) + { + return decode(jsonString, FPSTR(jsonDuration), result); + } + + bool getNonce(const String &jsonString, String &result) + { + return decode(jsonString, FPSTR(jsonNonce), result); + } + + bool getHmac(const String &jsonString, String &result) + { + return decode(jsonString, FPSTR(jsonHmac), result); + } + + bool getDesync(const String &jsonString, bool &result) + { + String jsonValue; + bool decoded = decode(jsonString, FPSTR(jsonDesync), jsonValue); + + if(decoded) + result = bool(strtoul(jsonValue.c_str(), nullptr, 0)); // strtoul stops reading input when an invalid character is discovered. + + return decoded; + } + + bool getUnsynchronizedMessageID(const String &jsonString, uint32_t &result) + { + return decode(jsonString, FPSTR(jsonUnsynchronizedMessageID), result); + } + + bool getMeshMessageCount(const String &jsonString, uint16_t &result) + { + uint32_t longResult = 0; + bool decoded = decode(jsonString, FPSTR(jsonMeshMessageCount), longResult); + + if(longResult > 65535) // Must fit within uint16_t + decoded = false; + + if(decoded) + result = longResult; + + return decoded; + } +} diff --git a/libraries/ESP8266WiFiMesh/src/JsonTranslator.h b/libraries/ESP8266WiFiMesh/src/JsonTranslator.h new file mode 100644 index 0000000000..337d6c03df --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/JsonTranslator.h @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#ifndef __ESPNOWJSONTRANSLATOR_H__ +#define __ESPNOWJSONTRANSLATOR_H__ + +#include +#include + +namespace JsonTranslator +{ + constexpr char jsonConnectionState[] PROGMEM = "connectionState"; + constexpr char jsonMeshState[] PROGMEM = "meshState"; + constexpr char jsonPassword[] PROGMEM = "password"; + constexpr char jsonOwnSessionKey[] PROGMEM = "ownSK"; + constexpr char jsonPeerSessionKey[] PROGMEM = "peerSK"; + constexpr char jsonPeerStaMac[] PROGMEM = "peerStaMac"; + constexpr char jsonPeerApMac[] PROGMEM = "peerApMac"; + constexpr char jsonDuration[] PROGMEM = "duration"; + constexpr char jsonNonce[] PROGMEM = "nonce"; + constexpr char jsonHmac[] PROGMEM = "hmac"; + constexpr char jsonDesync[] PROGMEM = "desync"; + constexpr char jsonUnsynchronizedMessageID[] PROGMEM = "unsyncMsgID"; + constexpr char jsonMeshMessageCount[] PROGMEM = "meshMsgCount"; + constexpr char jsonArguments[] PROGMEM = "arguments"; + + + /** + * Provides the index within jsonString where the value of valueIdentifier starts. + * Note that including " within a JSON string value will result in errors. + * + * @param jsonString The String to search within. + * @param valueIdentifier The identifier to search for. + * @param searchStartIndex Optional argument that makes it possible to decide at which index of jsonString the search starts. Search will begin at index 0 if not provided. + * + * @return An int32_t containing the index within jsonString where the value of valueIdentifier starts, or a negative value if valueIdentifier was not found. + */ + int32_t getStartIndex(const String &jsonString, const String &valueIdentifier, const int32_t searchStartIndex = 0); + + /** + * Provides the index within jsonString where the JSON object or JSON string value ends, starting the search from searchStartIndex. + * Note that including " within a JSON string value will result in errors. + * + * The character at searchStartIndex must be either " (for a string) or { (for an object), otherwise the search fails. + * + * @param jsonString The String to search within. + * @param searchStartIndex The index of jsonString where the search will start. The index position should contain either " or {. + * + * @return An int32_t containing the index within jsonString where the JSON string/object ends, or a negative value if no such character was found. + */ + int32_t getEndIndex(const String &jsonString, const int32_t searchStartIndex); + + /* + * Create a JSON String based on the identifiers and values given. + * + * Assumes all values are either strings or JSON objects. A value is interpreted as a JSON object if it starts with { + * Assumes all identifiers are strings. + * + * @param identifiersAndValues Any even number of String arguments. It is assumed that the identifiers and values are given in an alternating manner, as in encode({Identifier1, Value1, Identifier2, Value2, ...}) + */ + String encode(std::initializer_list identifiersAndValues); + + /* + * Create a JSON String based on the identifiers and values given. + * + * Does not make any assumptions regarding value types. " must be added manually around string values. + * Useful for example if your JSON values can contain starting { characters, since the regular encode() will then interpret them as JSON objects. + * Assumes all identifiers are strings. + * + * @param identifiersAndValues Any even number of String arguments. It is assumed that the identifiers and values are given in an alternating manner, as in encodeLiterally({Identifier1, Value1, Identifier2, Value2, ...}) + */ + String encodeLiterally(std::initializer_list identifiersAndValues); + + /* + * Get a value from a JSON String. + * Assumes all values are either JSON strings ( starting with " ) or JSON objects ( starting with { ). + * + * Note that including " within a JSON string value will result in errors. + * Escape characters are not supported at this moment, since we do not want string length modification to occur during ESP-NOW protocol transmissions. + * + * @param jsonString The String to search within. + * @param valueIdentifier The identifier to search for. + * @param value The String variable to put the result in. + * + * @return True if a value was found. False otherwise. The value argument is not modified if false is returned. + */ + bool decode(const String &jsonString, const String &valueIdentifier, String &value); + + /* + * Get a value from a JSON String. + * Assumes all values are stored as strings in standard C-format (i.e. decimal by default). + * + * Note that including " within a JSON string value will result in errors. + * Escape characters are not supported at this moment, since we do not want string length modification to occur during ESP-NOW protocol transmissions. + * + * @param jsonString The String to search within. + * @param valueIdentifier The identifier to search for. + * @param value The uint32_t variable to put the result in. + * + * @return True if a value was found. False otherwise. The value argument is not modified if false is returned. + */ + bool decode(const String &jsonString, const String &valueIdentifier, uint32_t &value); + + /* + * Get a value from a JSON String. + * Assumes all values are stored as strings encoded in the specified radix. Hexadecimal encoding is the default. + * + * Note that including " within a JSON string value will result in errors. + * Escape characters are not supported at this moment, since we do not want string length modification to occur during ESP-NOW protocol transmissions. + * + * @param jsonString The String to search within. + * @param valueIdentifier The identifier to search for. + * @param value The uint64_t variable to put the result in. + * @param radix The base to use when converting the string value to uint64_t. Must be between 2 and 36. + * + * @return True if a value was found. False otherwise. The value argument is not modified if false is returned. + */ + bool decodeRadix(const String &jsonString, const String &valueIdentifier, uint64_t &value, const uint8_t radix = 16); + + bool getConnectionState(const String &jsonString, String &result); + /** + * Stores the value of the password field within jsonString into the result variable. + * No changes to the result variable are made if jsonString does not contain a password. + * + * @param jsonString The String to search within. + * @param result The String where the value should be stored. + * + * @return True if a value was found. False otherwise. + */ + bool getPassword(const String &jsonString, String &result); + bool getOwnSessionKey(const String &jsonString, uint64_t &result); + bool getPeerSessionKey(const String &jsonString, uint64_t &result); + + /** + * Stores the value of the peerStaMac field within jsonString into the resultArray. + * No changes to the resultArray are made if jsonString does not contain a peerStaMac. + * + * @param jsonString The String to search within. + * @param resultArray The uint8_t array where the value should be stored. Must be at least 6 bytes. + * + * @return True if a value was found. False otherwise. + */ + bool getPeerStaMac(const String &jsonString, uint8_t *resultArray); + bool getPeerApMac(const String &jsonString, uint8_t *resultArray); + bool getDuration(const String &jsonString, uint32_t &result); + bool getNonce(const String &jsonString, String &result); + bool getHmac(const String &jsonString, String &result); + bool getDesync(const String &jsonString, bool &result); + bool getUnsynchronizedMessageID(const String &jsonString, uint32_t &result); + bool getMeshMessageCount(const String &jsonString, uint16_t &result); +} + +#endif diff --git a/libraries/ESP8266WiFiMesh/src/MeshBackendBase.cpp b/libraries/ESP8266WiFiMesh/src/MeshBackendBase.cpp new file mode 100644 index 0000000000..046c25fa37 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/MeshBackendBase.cpp @@ -0,0 +1,328 @@ +/* + MeshBackendBase + + Copyright (c) 2015 Julian Fell and 2019 Anders Löfgren. All rights reserved. + + 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 "MeshBackendBase.h" +#include "TypeConversionFunctions.h" +#include "MutexTracker.h" + +#include + +namespace +{ + namespace TypeCast = MeshTypeConversionFunctions; + + MeshBackendBase *apController = nullptr; +} + +std::shared_ptr MeshBackendBase::_scanMutex = std::make_shared(false); + +MeshBackendBase::MeshBackendBase(const requestHandlerType requestHandler, const responseHandlerType responseHandler, const networkFilterType networkFilter, const MeshBackendType classType) +{ + setRequestHandler(requestHandler); + setResponseHandler(responseHandler); + setNetworkFilter(networkFilter); + setClassType(classType); +} + +MeshBackendBase::~MeshBackendBase() +{ + deactivateControlledAP(); +} + +void MeshBackendBase::setClassType(const MeshBackendType classType) +{ + _classType = classType; +} + +MeshBackendType MeshBackendBase::getClassType() const {return _classType;} + +void MeshBackendBase::setVerboseModeState(const bool enabled) { _conditionalPrinter.setVerboseModeState(enabled); } +bool MeshBackendBase::verboseMode() const { return _conditionalPrinter.verboseMode(); } + +void MeshBackendBase::verboseModePrint(const String &stringToPrint, const bool newline) const +{ + _conditionalPrinter.verboseModePrint(stringToPrint, newline); +} + +void MeshBackendBase::setPrintWarnings(const bool printEnabled) { ConditionalPrinter::setPrintWarnings(printEnabled); } +bool MeshBackendBase::printWarnings() {return ConditionalPrinter::printWarnings();} + +void MeshBackendBase::warningPrint(const String &stringToPrint, const bool newline) +{ + ConditionalPrinter::warningPrint(stringToPrint, newline); +} + +void MeshBackendBase::activateAP() +{ + // Deactivate active AP to avoid two servers using the same port, which can lead to crashes. + deactivateAP(); + + activateAPHook(); + + WiFi.mode(WIFI_AP_STA); + + apController = this; +} + +void MeshBackendBase::activateAPHook() +{ + WiFi.softAP( getSSID().c_str(), getMeshPassword().c_str(), getWiFiChannel(), getAPHidden() ); // Note that a maximum of 8 TCP/IP stations can be connected at a time to each AP, max 4 by default. +} + +void MeshBackendBase::deactivateAP() +{ + if(MeshBackendBase *currentAPController = MeshBackendBase::getAPController()) + currentAPController->deactivateControlledAP(); +} + +bool MeshBackendBase::deactivateControlledAP() +{ + if(isAPController()) + { + deactivateAPHook(); + + WiFi.softAPdisconnect(); + WiFi.mode(WIFI_STA); + + // Since there is no active AP controller now, make the apController variable point to nothing. + apController = nullptr; + + return true; + } + + return false; +} + +void MeshBackendBase::deactivateAPHook() +{ +} + +void MeshBackendBase::restartAP() +{ + deactivateAP(); + yield(); + activateAP(); + yield(); +} + +MeshBackendBase *MeshBackendBase::getAPController() +{ + return apController; +} + +bool MeshBackendBase::isAPController() const +{ + return (this == getAPController()); +} + +void MeshBackendBase::setWiFiChannel(const uint8 newWiFiChannel) +{ + wifi_country_t wifiCountry; + wifi_get_country(&wifiCountry); // Note: Should return 0 on success and -1 on failure, but always seems to return 1. Possibly broken API. Channels 1 to 13 are the default limits. + assert(wifiCountry.schan <= newWiFiChannel && newWiFiChannel <= wifiCountry.schan + wifiCountry.nchan - 1); + + _meshWiFiChannel = newWiFiChannel; + + // WiFi.channel() will change if this node connects to an AP with another channel, + // so there is no guarantee we are using _meshWiFiChannel. + // Also, we cannot change the WiFi channel while we are still connected to the other AP. + if(WiFi.channel() != getWiFiChannel() && WiFi.status() != WL_CONNECTED) + { + // Apply changes to active AP. + if(isAPController()) + restartAP(); + } +} + +uint8 MeshBackendBase::getWiFiChannel() const +{ + return _meshWiFiChannel; +} + +void MeshBackendBase::setSSID(const String &newSSIDPrefix, const String &newSSIDRoot, const String &newSSIDSuffix) +{ + if(!newSSIDPrefix.isEmpty()) + _SSIDPrefix = newSSIDPrefix; + if(!newSSIDRoot.isEmpty()) + _SSIDRoot = newSSIDRoot; + if(!newSSIDSuffix.isEmpty()) + _SSIDSuffix = newSSIDSuffix; + + String newSSID = _SSIDPrefix + _SSIDRoot + _SSIDSuffix; + + assert(newSSID.length() <= 31); + + if(getSSID() != newSSID) + { + _SSID = newSSID; + + // Apply SSID changes to active AP. + if(isAPController()) + restartAP(); + } +} + +String MeshBackendBase::getSSID() const {return _SSID;} + +void MeshBackendBase::setSSIDPrefix(const String &newSSIDPrefix) +{ + setSSID(newSSIDPrefix); +} + +String MeshBackendBase::getSSIDPrefix() const {return _SSIDPrefix;} + +void MeshBackendBase::setSSIDRoot(const String &newSSIDRoot) +{ + setSSID(emptyString, newSSIDRoot); +} + +String MeshBackendBase::getSSIDRoot() const {return _SSIDRoot;} + +void MeshBackendBase::setSSIDSuffix(const String &newSSIDSuffix) +{ + setSSID(emptyString, emptyString, newSSIDSuffix); +} + +String MeshBackendBase::getSSIDSuffix() const {return _SSIDSuffix;} + +void MeshBackendBase::setMeshName(const String &newMeshName) +{ + setSSIDPrefix(newMeshName); +} + +String MeshBackendBase::getMeshName() const {return getSSIDPrefix();} + +void MeshBackendBase::setNodeID(const String &newNodeID) +{ + setSSIDSuffix(newNodeID); +} + +String MeshBackendBase::getNodeID() const {return getSSIDSuffix();} + +void MeshBackendBase::setMeshPassword(const String &newMeshPassword) +{ + assert(8 <= newMeshPassword.length() && newMeshPassword.length() <= 64); // Limited by the ESP8266 API. + assert(newMeshPassword.indexOf('"') == -1); // " is not allowed in passwords to allow for easier JSON parsing and predictable password length (no need for extra escape characters). + + _meshPassword = newMeshPassword; + + // Apply changes to active AP. + if(isAPController()) + restartAP(); +} + +String MeshBackendBase::getMeshPassword() const {return _meshPassword;} + +void MeshBackendBase::setMessage(const String &newMessage) {_message = newMessage;} +String MeshBackendBase::getMessage() const {return _message;} + +void MeshBackendBase::setRequestHandler(const MeshBackendBase::requestHandlerType requestHandler) {_requestHandler = requestHandler;} +MeshBackendBase::requestHandlerType MeshBackendBase::getRequestHandler() const {return _requestHandler;} + +void MeshBackendBase::setResponseHandler(const MeshBackendBase::responseHandlerType responseHandler) {_responseHandler = responseHandler;} +MeshBackendBase::responseHandlerType MeshBackendBase::getResponseHandler() const {return _responseHandler;} + +void MeshBackendBase::setNetworkFilter(const MeshBackendBase::networkFilterType networkFilter) {_networkFilter = networkFilter;} +MeshBackendBase::networkFilterType MeshBackendBase::getNetworkFilter() const {return _networkFilter;} + +void MeshBackendBase::setTransmissionOutcomesUpdateHook(const MeshBackendBase::transmissionOutcomesUpdateHookType transmissionOutcomesUpdateHook) {_transmissionOutcomesUpdateHook = transmissionOutcomesUpdateHook;} +MeshBackendBase::transmissionOutcomesUpdateHookType MeshBackendBase::getTransmissionOutcomesUpdateHook() const {return _transmissionOutcomesUpdateHook;} + +void MeshBackendBase::setScanHidden(const bool scanHidden) +{ + _scanHidden = scanHidden; +} + +bool MeshBackendBase::getScanHidden() const {return _scanHidden;} + +void MeshBackendBase::setAPHidden(const bool apHidden) +{ + if(getAPHidden() != apHidden) + { + _apHidden = apHidden; + + // Apply changes to active AP. + if(isAPController()) + restartAP(); + } +} + +bool MeshBackendBase::getAPHidden() const {return _apHidden;} + +ConditionalPrinter *MeshBackendBase::getConditionalPrinter() {return &_conditionalPrinter;} +const ConditionalPrinter *MeshBackendBase::getConditionalPrinterConst() const {return &_conditionalPrinter;} + +bool MeshBackendBase::latestTransmissionSuccessfulBase(const std::vector &latestTransmissionOutcomes) +{ + if(latestTransmissionOutcomes.empty()) + return false; + + for(const TransmissionOutcome &transmissionOutcome : latestTransmissionOutcomes) + if(transmissionOutcome.transmissionStatus() != TransmissionStatusType::TRANSMISSION_COMPLETE) + return false; + + return true; +} + +void MeshBackendBase::scanForNetworks(const bool scanAllWiFiChannels) +{ + MutexTracker mutexTracker(_scanMutex); + if(!mutexTracker.mutexCaptured()) + { + assert(false && String(F("ERROR! Scan already in progress. Don't call scanForNetworks from callbacks as this may corrupt program state! Aborting."))); + return; + } + + verboseModePrint(F("Scanning... "), false); + + /* Scan for APs */ + + // If scanAllWiFiChannels is true, scanning will cause the WiFi radio to cycle through all WiFi channels. + // This means existing WiFi connections are likely to break or work poorly if done frequently. + int n = 0; + if(scanAllWiFiChannels) + { + n = WiFi.scanNetworks(false, getScanHidden()); + } + else + { + // Scan function argument overview: scanNetworks(bool async = false, bool show_hidden = false, uint8 channel = 0, uint8* ssid = NULL) + n = WiFi.scanNetworks(false, getScanHidden(), getWiFiChannel()); + } + + getNetworkFilter()(n, *this); // Update the connectionQueue. +} + +void MeshBackendBase::printAPInfo(const NetworkInfoBase &apNetworkInfo) +{ + String mainNetworkIdentifier = apNetworkInfo.SSID(); + if(mainNetworkIdentifier == NetworkInfoBase::defaultSSID) // If SSID not provided, use BSSID instead + { + mainNetworkIdentifier = TypeCast::macToString(apNetworkInfo.BSSID()); + } + + verboseModePrint(String(F("AP acquired: ")) + mainNetworkIdentifier + String(F(", Ch:")) + String(apNetworkInfo.wifiChannel()) + ' ', false); + + if(apNetworkInfo.RSSI() != NetworkInfoBase::defaultRSSI) + { + verboseModePrint(String('(') + String(apNetworkInfo.RSSI()) + String(F("dBm) ")) + + (apNetworkInfo.encryptionType() == ENC_TYPE_NONE ? String(F("open")) : emptyString), false); + } + + verboseModePrint(F("... "), false); +} diff --git a/libraries/ESP8266WiFiMesh/src/MeshBackendBase.h b/libraries/ESP8266WiFiMesh/src/MeshBackendBase.h new file mode 100644 index 0000000000..483deaf319 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/MeshBackendBase.h @@ -0,0 +1,343 @@ +/* + MeshBackendBase + + Copyright (c) 2015 Julian Fell and 2019 Anders Löfgren. All rights reserved. + + 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 __MESHBACKENDBASE_H__ +#define __MESHBACKENDBASE_H__ + +#include +#include "TransmissionOutcome.h" +#include "NetworkInfoBase.h" +#include "ConditionalPrinter.h" + +enum class MeshBackendType +{ + TCP_IP = 0, + ESP_NOW = 1 +}; + +class MeshBackendBase { + +public: + + using requestHandlerType = std::function ; + using responseHandlerType = std::function; + using networkFilterType = std::function; + using transmissionOutcomesUpdateHookType = std::function; + + MeshBackendBase(const requestHandlerType requestHandler, const responseHandlerType responseHandler, const networkFilterType networkFilter, const MeshBackendType classType); + + virtual ~MeshBackendBase(); + + /** + * Initialises the node. + */ + virtual void begin() = 0; + + /** + * Activate the WiFi access point of this ESP8266. + * + * For TCP/IP, each AP requires a separate server port. If two AP:s are using the same server port, they will not be able to have both server instances active at the same time. + * This is managed automatically by the activateAP method. + * + * Note that only one AP can be active at a time in total (there is only one WiFi radio on the ESP8266), and this will always be the one which was last activated. + * Thus the AP is shared by all backends. + */ + void activateAP(); + + /** + * Deactivate the WiFi access point of this ESP8266, regardless of which MeshBackendBase instance is in control of the AP. + * + * Note that only one AP can be active at a time in total (there is only one WiFi radio on the ESP8266), and this will always be the one which was last activated. + * Thus the AP is shared by all backends. + */ + static void deactivateAP(); + + /** + * Deactivate the WiFi access point of this ESP8266, provided that this MeshBackendBase instance is in control of the AP (which normally is the case for the MeshBackendBase instance that did the last activateAP() call). + * + * Note that only one AP can be active at a time in total (there is only one WiFi radio on the ESP8266), and this will always be the one which was last activated. + * Thus the AP is shared by all backends. + * + * @return True if the AP was deactivated. False otherwise. + */ + bool deactivateControlledAP(); + + /** + * Restart the WiFi access point of this ESP8266. + * + * Note that only one AP can be active at a time in total (there is only one WiFi radio on the ESP8266), and this will always be the one which was last activated. + * Thus the AP is shared by all backends. + */ + void restartAP(); + + /** + * Get the MeshBackendBase instance currently in control of the ESP8266 AP. + * Note that the result will be nullptr when there is no active AP controller. + * If another instance takes control over the AP after the pointer is created, + * the created pointer will still point to the old AP instance. + * + * @return A pointer to the MeshBackendBase instance currently in control of the ESP8266 AP, + * or nullptr if there is no active AP controller. + */ + static MeshBackendBase *getAPController(); + + /** + * Check if this MeshBackendBase instance is in control of the ESP8266 AP. + * + * @return True if this MeshBackendBase instance is in control of the ESP8266 AP. False otherwise. + */ + bool isAPController() const; + + /** + * Change the WiFi channel used by this MeshBackendBase instance. + * Will also change the WiFi channel for the active AP (via an AP restart) + * if this MeshBackendBase instance is the current AP controller and it is possible to change channel. + * + * WARNING: The ESP8266 has only one WiFi channel, and the station/client mode is always prioritized for channel selection. + * This can cause problems if several MeshBackendBase instances exist on the same ESP8266 and use different WiFi channels. + * In such a case, whenever the station of one MeshBackendBase instance connects to an AP, it will silently force the + * WiFi channel of any active AP on the ESP8266 to match that of the station. This will cause disconnects and possibly + * make it impossible for other stations to detect the APs whose WiFi channels have changed. + * + * @param newWiFiChannel The WiFi channel to change to. Valid values are determined by wifi_get_country, usually integers from 1 to 11 or 1 to 13. + * + */ + virtual void setWiFiChannel(const uint8 newWiFiChannel); + virtual uint8 getWiFiChannel() const; + + /** + * Change the SSID used by this MeshBackendBase instance. + * The SSID can be at most 31 characters long. + * Will also change the SSID for the active AP (via an AP restart) + * if this MeshBackendBase instance is the current AP controller. + * + * @param newSSIDPrefix The first part of the new SSID. + * @param newSSIDRoot The middle part of the new SSID. + * @param newSSIDSuffix The last part of the new SSID. + */ + void setSSID(const String &newSSIDPrefix = emptyString, const String &newSSIDRoot = emptyString, + const String &newSSIDSuffix = emptyString); + String getSSID() const; + + /** + * Change the first part of the SSID used by this MeshBackendBase instance. + * Will also change the first part of the SSID for the active AP (via an AP restart) + * if this MeshBackendBase instance is the current AP controller. + * + * @param newSSIDPrefix The new first part of the SSID. + */ + void setSSIDPrefix(const String &newSSIDPrefix); + String getSSIDPrefix() const; + + /** + * Change the middle part of the SSID used by this MeshBackendBase instance. + * Will also change the middle part of the SSID for the active AP (via an AP restart) + * if this MeshBackendBase instance is the current AP controller. + * + * @param newSSIDPrefix The new middle part of the SSID. + */ + void setSSIDRoot(const String &newSSIDRoot); + String getSSIDRoot() const; + + /** + * Change the last part of the SSID used by this MeshBackendBase instance. + * Will also change the last part of the SSID for the active AP (via an AP restart) + * if this MeshBackendBase instance is the current AP controller. + * + * @param newSSIDSuffix The new last part of the SSID. + */ + void setSSIDSuffix(const String &newSSIDSuffix); + String getSSIDSuffix() const; + + /** + * Change the mesh name used by this MeshBackendBase instance. + * Will also change the mesh name for the active AP (via an AP restart) + * if this MeshBackendBase instance is the current AP controller. + * + * Used as alias for setSSIDPrefix by default. Feel free to override this method in a subclass if your mesh name is not equal to SSIDPrefix. + * + * @param newMeshName The mesh name to change to. + */ + virtual void setMeshName(const String &newMeshName); + virtual String getMeshName() const; + + /** + * Change the node id used by this MeshBackendBase instance. + * Will also change the node id for the active AP (via an AP restart) + * if this MeshBackendBase instance is the current AP controller. + * + * Used as alias for setSSIDSuffix by default. Feel free to override this method in a subclass if your node id is not equal to SSIDSuffix. + * + * @param newNodeID The node id to change to. + */ + virtual void setNodeID(const String &newNodeID); + virtual String getNodeID() const; + + /** + * Set the password used when connecting to other AP:s and when other nodes connect to the AP of this node. + * Will also change the setting for the active AP (via an AP restart) + * if this MeshBackendBase instance is the current AP controller. + * + * @param newMeshPassword The password to use. Must be between 8 and 64 characters long. " is an illegal character because of JSON parsing requirements. + */ + void setMeshPassword(const String &newMeshPassword); + String getMeshPassword() const; + + /** + * Set the message that will be sent to other nodes when calling attemptTransmission. + * + * @param newMessage The message to send. + */ + void setMessage(const String &newMessage); + String getMessage() const; + + virtual void attemptTransmission(const String &message, const bool scan = true, const bool scanAllWiFiChannels = false) = 0; + + void setRequestHandler(const requestHandlerType requestHandler); + requestHandlerType getRequestHandler() const; + + void setResponseHandler(const responseHandlerType responseHandler); + responseHandlerType getResponseHandler() const; + + void setNetworkFilter(const networkFilterType networkFilter); + networkFilterType getNetworkFilter() const; + + /** + * Set a function that should be called after each update of the latestTransmissionOutcomes vector during attemptTransmission. (which happens after each individual transmission has finished) + * The hook should return a bool. If this return value is true, attemptTransmission will continue with the next entry in the connectionQueue. If it is false, attemptTransmission will stop. + * The default transmissionOutcomesUpdateHook always returns true. + * + * Example use cases is modifying getMessage() between transmissions, or aborting attemptTransmission before all nodes in the connectionQueue have been contacted. + */ + void setTransmissionOutcomesUpdateHook(const transmissionOutcomesUpdateHookType transmissionOutcomesUpdateHook); + transmissionOutcomesUpdateHookType getTransmissionOutcomesUpdateHook() const; + + /** + * Set whether scan results from this MeshBackendBase instance will include WiFi networks with hidden SSIDs. + * This is false by default. + * The SSID field of a found hidden network will be blank in the scan results. + * WiFi.isHidden(networkIndex) can be used to verify that a found network is hidden. + * + * @param scanHidden If true, WiFi networks with hidden SSIDs will be included in scan results. + */ + void setScanHidden(const bool scanHidden); + bool getScanHidden() const; + + /** + * Set whether the AP controlled by this MeshBackendBase instance will have a WiFi network with hidden SSID. + * This is false by default. + * Will also change the setting for the active AP (via an AP restart) + * if this MeshBackendBase instance is the current AP controller. + * + * @param apHidden If true, the WiFi network created will have a hidden SSID. + */ + void setAPHidden(const bool apHidden); + bool getAPHidden() const; + + /** + * Set whether the normal events occurring in the library will be printed to Serial or not. Off by default. + * This setting is separate for each mesh instance. + * + * @param enabled If true, library Serial prints are activated. + */ + virtual void setVerboseModeState(const bool enabled); + virtual bool verboseMode() const; + + /** + * Only print stringToPrint if verboseMode() returns true. + * + * @param stringToPrint String to print. + * @param newline If true, will end the print with a newline. True by default. + */ + virtual void verboseModePrint(const String &stringToPrint, const bool newline = true) const; + + /** + * Set whether the warnings occurring in the library will be printed to Serial or not. On by default. + * This setting will affect all mesh instances. + * + * @param printEnabled If true, warning Serial prints from the library are activated. + */ + static void setPrintWarnings(const bool printEnabled); + static bool printWarnings(); + + /** + * Only print stringToPrint if printWarnings() returns true. + * + * @param stringToPrint String to print. + * @param newline If true, will end the print with a newline. True by default. + */ + static void warningPrint(const String &stringToPrint, const bool newline = true); + + MeshBackendType getClassType() const; + + virtual void printAPInfo(const NetworkInfoBase &apNetworkInfo); + +protected: + + ConditionalPrinter *getConditionalPrinter(); + const ConditionalPrinter *getConditionalPrinterConst() const; + + /** + * @param latestTransmissionOutcomes The transmission outcomes vector to check. + * + * @return True if latest transmission was successful (i.e. latestTransmissionOutcomes is not empty and all entries have transmissionStatus TransmissionStatusType::TRANSMISSION_COMPLETE). False otherwise. + */ + static bool latestTransmissionSuccessfulBase(const std::vector &latestTransmissionOutcomes); + + virtual void scanForNetworks(const bool scanAllWiFiChannels); + + /** + * Called just before we activate the AP. + * Put _server.stop() in deactivateAPHook() in case you use _server.begin() here. + */ + virtual void activateAPHook(); + + /** + * Called just before we deactivate the AP. + * Put _server.stop() here in case you use _server.begin() in activateAPHook(). + */ + virtual void deactivateAPHook(); + + void setClassType(const MeshBackendType classType); + + static std::shared_ptr _scanMutex; + +private: + + MeshBackendType _classType; + + ConditionalPrinter _conditionalPrinter; + + String _SSID; + String _SSIDPrefix; + String _SSIDRoot; + String _SSIDSuffix; + String _meshPassword; + String _message; + + requestHandlerType _requestHandler; + responseHandlerType _responseHandler; + networkFilterType _networkFilter; + transmissionOutcomesUpdateHookType _transmissionOutcomesUpdateHook = [](MeshBackendBase &){return true;}; + + uint8 _meshWiFiChannel; + bool _scanHidden = false; + bool _apHidden = false; +}; + +#endif diff --git a/libraries/ESP8266WiFiMesh/src/MeshCryptoInterface.cpp b/libraries/ESP8266WiFiMesh/src/MeshCryptoInterface.cpp new file mode 100644 index 0000000000..68e9b29065 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/MeshCryptoInterface.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#include "MeshCryptoInterface.h" +#include + +namespace MeshCryptoInterface +{ + String createMeshHmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength) + { + return experimental::crypto::SHA256::hmac(message, hashKey, hashKeyLength, hmacLength); + } + + bool verifyMeshHmac(const String &message, const String &messageHmac, const uint8_t *hashKey, const uint8_t hashKeyLength) + { + String generatedHmac = createMeshHmac(message, hashKey, hashKeyLength, messageHmac.length()/2); // We know that each HMAC byte should become 2 String characters due to uint8ArrayToHexString. + if(generatedHmac == messageHmac) + return true; + else + return false; + } + + uint8_t *initializeKey(uint8_t *key, const uint8_t keyLength, const String &keySeed) + { + assert(keyLength <= experimental::crypto::SHA256::NATURAL_LENGTH); + uint8_t hashArray[experimental::crypto::SHA256::NATURAL_LENGTH] {}; + experimental::crypto::SHA256::hash(keySeed.c_str(), keySeed.length(), hashArray); + memcpy(key, hashArray, keyLength); + return key; + } +} diff --git a/libraries/ESP8266WiFiMesh/src/MeshCryptoInterface.h b/libraries/ESP8266WiFiMesh/src/MeshCryptoInterface.h new file mode 100644 index 0000000000..6fa3fe7e6e --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/MeshCryptoInterface.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#ifndef __MESHCRYPTOINTERFACE_H__ +#define __MESHCRYPTOINTERFACE_H__ + +#include +#include + +namespace MeshCryptoInterface +{ + /** + * There is a constant-time HMAC version available. More constant-time info here: https://www.bearssl.org/constanttime.html + * For small messages, it takes substantially longer time to complete than a normal HMAC (5 ms vs 2 ms in a quick benchmark, + * determined by the difference between min and max allowed message length), and it also sets a maximum length that messages can be (1024 bytes by default). + * Making the fixed max length variable would defeat the whole purpose of using constant-time, and not making it variable would create the wrong HMAC if message size exceeds the maximum. + * + * Also, HMAC is already partially constant-time. Quoting the link above: + * "Hash functions implemented by BearSSL (MD5, SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512) consist in bitwise logical operations and additions on 32-bit or 64-bit words, + * naturally yielding constant-time operations. HMAC is naturally as constant-time as the underlying hash function. The size of the MACed data, and the size of the key, + * may leak, though; only the contents are protected." + * + * Thus the non constant-time version is used within the mesh framework instead. + */ + + /** + * Create a SHA256 HMAC from the message, using the provided hashKey. The result will be hmacLength bytes long and returned as a String in HEX format. + * + * @param message The string from which to create the HMAC. + * @param hashKey The hash key to use when creating the HMAC. + * @param hashKeyLength The length of the hash key in bytes. + * @param hmacLength The desired length of the generated HMAC, in bytes. Valid values are 1 to 32. Defaults to experimental::crypto::SHA256::NATURAL_LENGTH. + * + * @return A String with the generated HMAC in HEX format. + */ + String createMeshHmac(const String &message, const void *hashKey, const size_t hashKeyLength, const size_t hmacLength = experimental::crypto::SHA256::NATURAL_LENGTH); + + /** + * Verify a SHA256 HMAC which was created from the message using the provided hashKey. + * + * @param message The string from which the HMAC was created. + * @param messageHmac A string with the generated HMAC in HEX format. Valid messageHmac.length() is 2 to 64. + * @param hashKey The hash key to use when creating the HMAC. + * @param hashKeyLength The length of the hash key in bytes. + * + * @return True if the HMAC is correct. False otherwise. + */ + bool verifyMeshHmac(const String &message, const String &messageHmac, const uint8_t *hashKey, const uint8_t hashKeyLength); + + /** + * Initialize key with a SHA-256 hash of keySeed. + * + * @param key A uint8_t array containing the key to be initialized. + * @param keyLength The length of the key array in bytes. Maximum value is experimental::crypto::SHA256::NATURAL_LENGTH. + * @param keySeed The key seed. + * + * @return A pointer to the initialized key array. + */ + uint8_t *initializeKey(uint8_t *key, const uint8_t keyLength, const String &keySeed); +} + +#endif diff --git a/libraries/ESP8266WiFiMesh/src/MessageData.cpp b/libraries/ESP8266WiFiMesh/src/MessageData.cpp new file mode 100644 index 0000000000..72d087aed6 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/MessageData.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#include "MessageData.h" +#include "EspnowProtocolInterpreter.h" +#include "EspnowMeshBackend.h" +#include + +MessageData::MessageData(const String &message, const uint8_t transmissionsRemaining, const uint32_t creationTimeMs) : + _timeTracker(creationTimeMs) +{ + _transmissionsExpected = transmissionsRemaining + 1; + _totalMessage += message; + ++_transmissionsReceived; +} + +MessageData::MessageData(uint8_t *initialTransmission, const uint8_t transmissionLength, const uint32_t creationTimeMs) : + _timeTracker(creationTimeMs) +{ + _transmissionsExpected = EspnowProtocolInterpreter::getTransmissionsRemaining(initialTransmission) + 1; + addToMessage(initialTransmission, transmissionLength); +} + +bool MessageData::addToMessage(uint8_t *transmission, const uint8_t transmissionLength) +{ + if(EspnowProtocolInterpreter::getTransmissionsRemaining(transmission) == getTransmissionsRemaining() - 1) + { + String message = EspnowProtocolInterpreter::getHashKeyLength(transmission, transmissionLength); + assert(message.length() <= EspnowMeshBackend::getMaxMessageBytesPerTransmission()); // Should catch some cases where transmission is not null terminated. + _totalMessage += message; + ++_transmissionsReceived; + return true; + } + + return false; +} + +uint8_t MessageData::getTransmissionsReceived() const +{ + return _transmissionsReceived; +} + +uint8_t MessageData::getTransmissionsExpected() const +{ + return _transmissionsExpected; +} + +uint8_t MessageData::getTransmissionsRemaining() const +{ + return getTransmissionsExpected() - getTransmissionsReceived(); +} + +String MessageData::getTotalMessage() const +{ + return _totalMessage; +} + +const TimeTracker &MessageData::getTimeTracker() const { return _timeTracker; } diff --git a/libraries/ESP8266WiFiMesh/src/MessageData.h b/libraries/ESP8266WiFiMesh/src/MessageData.h new file mode 100644 index 0000000000..825ec13f57 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/MessageData.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#ifndef __ESPNOWMESSAGEDATA_H__ +#define __ESPNOWMESSAGEDATA_H__ + +#include "TimeTracker.h" +#include + +class MessageData { + +public: + + MessageData(const String &message, const uint8_t transmissionsRemaining, const uint32_t creationTimeMs = millis()); + MessageData(uint8_t *initialTransmission, const uint8_t transmissionLength, const uint32_t creationTimeMs = millis()); + /** + * @transmission A string of characters, including initial protocol bytes. Not const since that would increase heap consumption during processing. + * @transmissionLength Length of transmission. + */ + bool addToMessage(uint8_t *transmission, const uint8_t transmissionLength); + uint8_t getTransmissionsReceived() const; + uint8_t getTransmissionsExpected() const; + uint8_t getTransmissionsRemaining() const; + String getTotalMessage() const; + const TimeTracker &getTimeTracker() const; + +private: + + TimeTracker _timeTracker; + String _totalMessage; + uint8_t _transmissionsReceived = 0; + uint8_t _transmissionsExpected; +}; + +#endif diff --git a/libraries/ESP8266WiFiMesh/src/MutexTracker.cpp b/libraries/ESP8266WiFiMesh/src/MutexTracker.cpp new file mode 100644 index 0000000000..b4fdad89a8 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/MutexTracker.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#include "MutexTracker.h" + +namespace +{ + std::shared_ptr _captureBan = std::make_shared(false); +} + +MutexTracker::MutexTracker(const std::shared_ptr &mutexToCapture) +{ + attemptMutexCapture(mutexToCapture); +} + +MutexTracker::MutexTracker(const std::shared_ptr &mutexToCapture, const std::function destructorHook) : MutexTracker(mutexToCapture) +{ + _destructorHook = destructorHook; +} + +MutexTracker::~MutexTracker() +{ + releaseMutex(); + _destructorHook(); +} + +MutexTracker MutexTracker::captureBan() +{ + // Syntax like this will move the resulting value into its new position (similar to NRVO): https://stackoverflow.com/a/11540204 + return MutexTracker(_captureBan); +} + +MutexTracker MutexTracker::captureBan(const std::function destructorHook) { return MutexTracker(_captureBan, destructorHook); } + +bool MutexTracker::mutexFree(const std::shared_ptr &mutex) +{ + if(mutex != nullptr && !(*mutex)) + return true; + + return false; +} + +bool MutexTracker::mutexCaptured(const std::shared_ptr &mutex) +{ + if(mutex != nullptr && (*mutex)) + return true; + + return false; +} + +bool MutexTracker::mutexCaptured() const +{ + return mutexCaptured(_capturedMutex); +} + +void MutexTracker::releaseMutex() +{ + if(mutexCaptured()) + { + *_capturedMutex = false; + _capturedMutex.reset(); + } +} + +bool MutexTracker::attemptMutexCapture(const std::shared_ptr &mutexToCapture) +{ + if(mutexFree(_captureBan) && mutexFree(mutexToCapture)) + { + _capturedMutex = mutexToCapture; + *_capturedMutex = true; + return true; + } + + return false; +} diff --git a/libraries/ESP8266WiFiMesh/src/MutexTracker.h b/libraries/ESP8266WiFiMesh/src/MutexTracker.h new file mode 100644 index 0000000000..ac911fda81 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/MutexTracker.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#ifndef __MUTEXTRACKER_H__ +#define __MUTEXTRACKER_H__ + +#include +#include + +/** + * A SLIM (Scope LImited Manager)/Scope-Bound Resource Management/RAII class to manage the state of a mutex. + */ +class MutexTracker +{ + public: + + /** + * Attempts to capture the mutex. Use the mutexCaptured() method to check success. + */ + MutexTracker(const std::shared_ptr &mutexToCapture); + + /** + * Attempts to capture the mutex. Use the mutexCaptured() method to check success. + * + * @param destructorHook A function to hook into the MutexTracker destructor. Will be called when the MutexTracker instance is being destroyed, after the mutex has been released. + */ + MutexTracker(const std::shared_ptr &mutexToCapture, const std::function destructorHook); + + ~MutexTracker(); + + /* + * If captureBan is active, trying to capture a mutex will always fail. + * Inactive by default. + * captureBan can be managed by MutexTracker like any other mutex. + */ + static MutexTracker captureBan(); + static MutexTracker captureBan(const std::function destructorHook); + + bool mutexCaptured() const; + + /** + * Set the mutex free to roam the binary plains, giving new MutexTrackers a chance to capture it. + */ + void releaseMutex(); + + private: + + std::shared_ptr _capturedMutex; + std::function _destructorHook = [](){ }; + + static bool mutexFree(const std::shared_ptr &mutex); + static bool mutexCaptured(const std::shared_ptr &mutex); + + /** + * Attempt to capture the mutex. + * + * @return True if mutex was caught (meaning it exists and no other instance is holding the mutex). False otherwise. + */ + bool attemptMutexCapture(const std::shared_ptr &mutexToCapture); +}; + +#endif diff --git a/libraries/ESP8266WiFiMesh/src/NetworkInfo.cpp b/libraries/ESP8266WiFiMesh/src/NetworkInfo.cpp index 10300dd7bf..7e983ce867 100644 --- a/libraries/ESP8266WiFiMesh/src/NetworkInfo.cpp +++ b/libraries/ESP8266WiFiMesh/src/NetworkInfo.cpp @@ -22,6 +22,29 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ + + + + + + + +/******************************************************************************************** +* NOTE! +* +* This class is deprecated and will be removed in core version 3.0.0. +* If you are still using this class, please consider migrating to the new API shown in +* the EspnowNetworkInfo.h or TcpIpNetworkInfo.h source files. +* +* TODO: delete this file. +********************************************************************************************/ + + + + + + + #include "NetworkInfo.h" @@ -74,4 +97,3 @@ NetworkInfo & NetworkInfo::operator=(const NetworkInfo &other) networkIndex = other.networkIndex; return *this; } - diff --git a/libraries/ESP8266WiFiMesh/src/NetworkInfo.h b/libraries/ESP8266WiFiMesh/src/NetworkInfo.h index 82fcd90c04..4462759f94 100644 --- a/libraries/ESP8266WiFiMesh/src/NetworkInfo.h +++ b/libraries/ESP8266WiFiMesh/src/NetworkInfo.h @@ -23,12 +23,34 @@ * THE SOFTWARE. */ + + + + + + +/******************************************************************************************** +* NOTE! +* +* This class is deprecated and will be removed in core version 3.0.0. +* If you are still using this class, please consider migrating to the new API shown in +* the EspnowNetworkInfo.h or TcpIpNetworkInfo.h source files. +* +* TODO: delete this file. +********************************************************************************************/ + + + + + + + + #ifndef __NETWORKINFO_H__ #define __NETWORKINFO_H__ #include - -const int NETWORK_INFO_DEFAULT_INT = -1; +#include "NetworkInfoBase.h" class NetworkInfo { @@ -38,7 +60,7 @@ class NetworkInfo { public: - String SSID = ""; + String SSID; int wifiChannel = NETWORK_INFO_DEFAULT_INT; uint8_t *BSSID = NULL; int networkIndex = NETWORK_INFO_DEFAULT_INT; diff --git a/libraries/ESP8266WiFiMesh/src/NetworkInfoBase.cpp b/libraries/ESP8266WiFiMesh/src/NetworkInfoBase.cpp new file mode 100644 index 0000000000..7549c5ebc0 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/NetworkInfoBase.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#include "NetworkInfoBase.h" + +uint8_t * const NetworkInfoBase::defaultBSSID = nullptr; +const String NetworkInfoBase::defaultSSID; +const int32_t NetworkInfoBase::defaultWifiChannel = NETWORK_INFO_DEFAULT_INT; +const uint8_t NetworkInfoBase::defaultEncryptionType = 0; +const int32_t NetworkInfoBase::defaultRSSI = ~0; +const bool NetworkInfoBase::defaultIsHidden = false; + +void NetworkInfoBase::storeBSSID(const uint8_t newBSSID[6]) +{ + if(newBSSID != nullptr) + { + if(_BSSID == nullptr) + { + _BSSID = _bssidArray; + } + + for(int i = 0; i < 6; ++i) + { + _BSSID[i] = newBSSID[i]; + } + } + else + { + _BSSID = nullptr; + } +} + +NetworkInfoBase::NetworkInfoBase() {}; + +NetworkInfoBase::NetworkInfoBase(const uint8_t networkIndex) +{ + uint8_t *bssidPtr = nullptr; + WiFi.getNetworkInfo(networkIndex, _SSID, _encryptionType, _RSSI, bssidPtr, _wifiChannel, _isHidden); + storeBSSID(bssidPtr); +} + +NetworkInfoBase::NetworkInfoBase(const String &SSID, const int32_t wifiChannel, const uint8_t BSSID[6], const uint8_t encryptionType, const int32_t RSSI, const bool isHidden) : + _SSID(SSID), _wifiChannel(wifiChannel), _RSSI(RSSI), _encryptionType(encryptionType), _isHidden(isHidden) +{ + storeBSSID(BSSID); +} + +NetworkInfoBase::NetworkInfoBase(const NetworkInfoBase &other) : _SSID(other.SSID()), _wifiChannel(other.wifiChannel()), _RSSI(other.RSSI()), + _encryptionType(other.encryptionType()), _isHidden(other.isHidden()) +{ + storeBSSID(other.BSSID()); +} + +NetworkInfoBase & NetworkInfoBase::operator=(const NetworkInfoBase &other) +{ + if(this != &other) + { + storeBSSID(other.BSSID()); + _SSID = other.SSID(); + _wifiChannel = other.wifiChannel(); + _encryptionType = other.encryptionType(); + _RSSI = other.RSSI(); + _isHidden = other.isHidden(); + } + + return *this; +} + +NetworkInfoBase::~NetworkInfoBase() { }; + +void NetworkInfoBase::setBSSID(const uint8_t BSSID[6]) { storeBSSID(BSSID); } +const uint8_t *NetworkInfoBase::BSSID() const { return _BSSID; } +uint8_t *NetworkInfoBase::getBSSID(uint8_t resultArray[6]) const +{ + if(BSSID()) + { + std::copy_n(_bssidArray, 6, resultArray); + return resultArray; + } + else + { + return nullptr; + } +} + +void NetworkInfoBase::setSSID(const String &SSID) { _SSID = SSID; } +String NetworkInfoBase::SSID() const { return _SSID; } + +void NetworkInfoBase::setWifiChannel(const int32_t wifiChannel) { _wifiChannel = wifiChannel; } +int32_t NetworkInfoBase::wifiChannel() const { return _wifiChannel; } + +void NetworkInfoBase::setEncryptionType(const uint8_t encryptionType) { _encryptionType = encryptionType; } +uint8_t NetworkInfoBase::encryptionType() const { return _encryptionType; } + +void NetworkInfoBase::setRSSI(const int32_t RSSI) { _RSSI = RSSI; } +int32_t NetworkInfoBase::RSSI() const { return _RSSI; } + +void NetworkInfoBase::setIsHidden(const bool isHidden) { _isHidden = isHidden; } +bool NetworkInfoBase::isHidden() const { return _isHidden; } diff --git a/libraries/ESP8266WiFiMesh/src/NetworkInfoBase.h b/libraries/ESP8266WiFiMesh/src/NetworkInfoBase.h new file mode 100644 index 0000000000..7122a3f9e4 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/NetworkInfoBase.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#ifndef __NETWORKINFOBASE_H__ +#define __NETWORKINFOBASE_H__ + +#include + +const int NETWORK_INFO_DEFAULT_INT = -1; + +class NetworkInfoBase { + +public: + + /** + * Automatically fill in the rest of the network info using networkIndex and the WiFi scan results. + */ + NetworkInfoBase(const uint8_t networkIndex); + + /** + * Without giving channel and BSSID, connection time is longer. + */ + NetworkInfoBase(const String &SSID, const int32_t wifiChannel, const uint8_t BSSID[6], const uint8_t encryptionType, const int32_t RSSI, const bool isHidden); + + NetworkInfoBase(const NetworkInfoBase &other); + + NetworkInfoBase & operator=(const NetworkInfoBase &other); + + void setBSSID(const uint8_t BSSID[6]); + const uint8_t *BSSID() const; + /** + * @return If BSSID is set, a pointer to resultArray which will contain a copy of BSSID. nullptr otherwise. + */ + uint8_t *getBSSID(uint8_t resultArray[6]) const; + + void setSSID(const String &SSID); + String SSID() const; + + void setWifiChannel(const int32_t wifiChannel); + int32_t wifiChannel() const; + + void setEncryptionType(const uint8_t encryptionType); + uint8_t encryptionType() const; + + void setRSSI(const int32_t RSSI); + int32_t RSSI() const; + + void setIsHidden(const bool isHidden); + bool isHidden() const; + + static uint8_t * const defaultBSSID; + static const uint8_t defaultEncryptionType; + static const bool defaultIsHidden; + static const String defaultSSID; + static const int32_t defaultWifiChannel; + static const int32_t defaultRSSI; + +protected: + + ~NetworkInfoBase(); + + NetworkInfoBase(); + + /** + * Copy newBSSID into _BSSID. + * Prefer this method for changing NetworkInfo BSSID, unless you actually want to change the _BSSID pointer. + */ + void storeBSSID(const uint8_t newBSSID[6]); + +private: + + uint8_t *_BSSID = defaultBSSID; + String _SSID = defaultSSID; + int32_t _wifiChannel = defaultWifiChannel; + int32_t _RSSI = defaultRSSI; + uint8_t _bssidArray[6] {0}; + uint8_t _encryptionType = defaultEncryptionType; // see enum wl_enc_type for values + bool _isHidden = defaultIsHidden; +}; + +#endif diff --git a/libraries/ESP8266WiFiMesh/src/PeerRequestLog.cpp b/libraries/ESP8266WiFiMesh/src/PeerRequestLog.cpp new file mode 100644 index 0000000000..db87fb5d8c --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/PeerRequestLog.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#include "PeerRequestLog.h" +#include "EspnowMeshBackend.h" + +namespace +{ + using EspnowProtocolInterpreter::hashKeyLength; +} + +PeerRequestLog::PeerRequestLog(const uint64_t requestID, const bool requestEncrypted, const String &authenticationPassword, const uint8_t encryptedConnectionsSoftLimit, + const String &peerRequestNonce, const uint8_t peerStaMac[6], const uint8_t peerApMac[6], const uint8_t hashKey[hashKeyLength]) + : EncryptedConnectionData(peerStaMac, peerApMac, 0, 0, EspnowMeshBackend::getEncryptionRequestTimeout(), hashKey), + _requestID(requestID), _authenticationPassword(authenticationPassword), _peerRequestNonce(peerRequestNonce) + , _requestEncrypted(requestEncrypted), _encryptedConnectionsSoftLimit(encryptedConnectionsSoftLimit) +{ } + +PeerRequestLog::PeerRequestLog(const uint64_t requestID, const bool requestEncrypted, const String &authenticationPassword, const uint8_t encryptedConnectionsSoftLimit, const String &peerRequestNonce, + const uint8_t peerStaMac[6], const uint8_t peerApMac[6], const uint64_t peerSessionKey, const uint64_t ownSessionKey, const uint8_t hashKey[hashKeyLength]) + : EncryptedConnectionData(peerStaMac, peerApMac, peerSessionKey, ownSessionKey, EspnowMeshBackend::getEncryptionRequestTimeout(), hashKey), + _requestID(requestID), _authenticationPassword(authenticationPassword), _peerRequestNonce(peerRequestNonce) + , _requestEncrypted(requestEncrypted), _encryptedConnectionsSoftLimit(encryptedConnectionsSoftLimit) +{ } + +void PeerRequestLog::setRequestID(const uint64_t requestID) { _requestID = requestID; } +uint64_t PeerRequestLog::getRequestID() const { return _requestID; } + +void PeerRequestLog::setRequestEncrypted(const bool requestEncrypted) { _requestEncrypted = requestEncrypted; } +bool PeerRequestLog::requestEncrypted() const { return _requestEncrypted; } + +void PeerRequestLog::setAuthenticationPassword(const String &password) { _authenticationPassword = password; } +String PeerRequestLog::getAuthenticationPassword() const { return _authenticationPassword; } + +void PeerRequestLog::setEncryptedConnectionsSoftLimit(const uint8_t softLimit) { _encryptedConnectionsSoftLimit = softLimit; } +uint8_t PeerRequestLog::getEncryptedConnectionsSoftLimit() const { return _encryptedConnectionsSoftLimit; } + +void PeerRequestLog::setPeerRequestNonce(const String &nonce) { _peerRequestNonce = nonce; } +String PeerRequestLog::getPeerRequestNonce() const { return _peerRequestNonce; } diff --git a/libraries/ESP8266WiFiMesh/src/PeerRequestLog.h b/libraries/ESP8266WiFiMesh/src/PeerRequestLog.h new file mode 100644 index 0000000000..e541dab2fe --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/PeerRequestLog.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#ifndef __ESPNOWPEERREQUESTLOG_H__ +#define __ESPNOWPEERREQUESTLOG_H__ + +#include "EncryptedConnectionData.h" +#include "EspnowProtocolInterpreter.h" + +class PeerRequestLog : public EncryptedConnectionData { + +public: + + PeerRequestLog(const uint64_t requestID, const bool requestEncrypted, const String &authenticationPassword, const uint8_t encryptedConnectionsSoftLimit, const String &peerRequestNonce, + const uint8_t peerStaMac[6], const uint8_t peerApMac[6], const uint8_t hashKey[EspnowProtocolInterpreter::hashKeyLength]); + PeerRequestLog(const uint64_t requestID, const bool requestEncrypted, const String &authenticationPassword, const uint8_t encryptedConnectionsSoftLimit, const String &peerRequestNonce, + const uint8_t peerStaMac[6], const uint8_t peerApMac[6], const uint64_t peerSessionKey, const uint64_t ownSessionKey, + const uint8_t hashKey[EspnowProtocolInterpreter::hashKeyLength]); + + void setRequestID(const uint64_t requestID); + uint64_t getRequestID() const; + + void setRequestEncrypted(const bool requestEncrypted); + bool requestEncrypted() const; + + void setAuthenticationPassword(const String &password); + String getAuthenticationPassword() const; + + void setEncryptedConnectionsSoftLimit(const uint8_t softLimit); + uint8_t getEncryptedConnectionsSoftLimit() const; + + void setPeerRequestNonce(const String &nonce); + String getPeerRequestNonce() const; + +private: + + uint64_t _requestID; + String _authenticationPassword; + String _peerRequestNonce; + bool _requestEncrypted; + uint8_t _encryptedConnectionsSoftLimit; +}; + +#endif diff --git a/libraries/ESP8266WiFiMesh/src/RequestData.cpp b/libraries/ESP8266WiFiMesh/src/RequestData.cpp new file mode 100644 index 0000000000..a3946215cf --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/RequestData.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#include "RequestData.h" +#include "EspnowMeshBackend.h" + +RequestData::RequestData(EspnowMeshBackend &meshInstance, const uint32_t creationTimeMs) : + _timeTracker(creationTimeMs), _meshInstance(meshInstance) +{ } + +EspnowMeshBackend &RequestData::getMeshInstance() const { return _meshInstance; } +const TimeTracker &RequestData::getTimeTracker() const { return _timeTracker; } diff --git a/libraries/ESP8266WiFiMesh/src/RequestData.h b/libraries/ESP8266WiFiMesh/src/RequestData.h new file mode 100644 index 0000000000..c2f5a55965 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/RequestData.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#ifndef __ESPNOWREQUESTDATA_H__ +#define __ESPNOWREQUESTDATA_H__ + +#include +#include "TimeTracker.h" + +class EspnowMeshBackend; + +class RequestData { + +public: + + RequestData(EspnowMeshBackend &meshInstance, const uint32_t creationTimeMs = millis()); + + EspnowMeshBackend &getMeshInstance() const; + const TimeTracker &getTimeTracker() const; + +private: + + TimeTracker _timeTracker; + EspnowMeshBackend &_meshInstance; +}; + +#endif diff --git a/libraries/ESP8266WiFiMesh/src/ResponseData.cpp b/libraries/ESP8266WiFiMesh/src/ResponseData.cpp new file mode 100644 index 0000000000..5c7cb93eb8 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/ResponseData.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#include "ResponseData.h" + +ResponseData::ResponseData(const String &message, const uint8_t recipientMac[6], const uint64_t requestID, const uint32_t creationTimeMs) : + _timeTracker(creationTimeMs), _message(message), _requestID(requestID) +{ + storeRecipientMac(recipientMac); +} + +ResponseData::ResponseData(const ResponseData &other) + : _timeTracker(other.getTimeTracker()), _message(other.getMessage()), _requestID(other.getRequestID()) +{ + storeRecipientMac(other.getRecipientMac()); +} + +ResponseData & ResponseData::operator=(const ResponseData &other) +{ + if(this != &other) + { + _timeTracker = other.getTimeTracker(); + _message = other.getMessage(); + _requestID = other.getRequestID(); + storeRecipientMac(other.getRecipientMac()); + } + + return *this; +} + +void ResponseData::storeRecipientMac(const uint8_t newRecipientMac[6]) +{ + if(newRecipientMac == nullptr) + { + _recipientMac = nullptr; + return; + } + + if(_recipientMac == nullptr) + { + _recipientMac = _recipientMacArray; + } + + for(int i = 0; i < 6; ++i) + { + _recipientMac[i] = newRecipientMac[i]; + } +} + +void ResponseData::setRecipientMac(const uint8_t recipientMac[6]) { storeRecipientMac(recipientMac); } +const uint8_t *ResponseData::getRecipientMac() const { return _recipientMac; } + +void ResponseData::setMessage(const String &message) { _message = message; } +String ResponseData::getMessage() const { return _message; } + +void ResponseData::setRequestID(const uint64_t requestID) { _requestID = requestID; } +uint64_t ResponseData::getRequestID() const { return _requestID; } + +const TimeTracker &ResponseData::getTimeTracker() const { return _timeTracker; } diff --git a/libraries/ESP8266WiFiMesh/src/ResponseData.h b/libraries/ESP8266WiFiMesh/src/ResponseData.h new file mode 100644 index 0000000000..32273d497f --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/ResponseData.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#ifndef __ESPNOWRESPONSEDATA_H__ +#define __ESPNOWRESPONSEDATA_H__ + +#include "TimeTracker.h" +#include + +class ResponseData { + +public: + + ResponseData(const String &message, const uint8_t recipientMac[6], const uint64_t requestID, const uint32_t creationTimeMs = millis()); + ResponseData(const ResponseData &other); + ResponseData & operator=(const ResponseData &other); + // No need for explicit destructor with current class design + + void setRecipientMac(const uint8_t recipientMac[6]); + const uint8_t *getRecipientMac() const; + + void setMessage(const String &message); + String getMessage() const; + + void setRequestID(const uint64_t requestID); + uint64_t getRequestID() const; + + const TimeTracker &getTimeTracker() const; + +private: + + void storeRecipientMac(const uint8_t newRecipientMac[6]); + + TimeTracker _timeTracker; + + uint8_t *_recipientMac = nullptr; + String _message; + uint64_t _requestID = 0; + uint8_t _recipientMacArray[6] {0}; +}; + +#endif diff --git a/libraries/ESP8266WiFiMesh/src/Serializer.cpp b/libraries/ESP8266WiFiMesh/src/Serializer.cpp new file mode 100644 index 0000000000..2b4454fb03 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/Serializer.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2020 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#include "Serializer.h" +#include "JsonTranslator.h" +#include "TypeConversionFunctions.h" +#include "MeshCryptoInterface.h" +#include "EspnowProtocolInterpreter.h" +#include + +namespace +{ + namespace TypeCast = MeshTypeConversionFunctions; + + String createJsonEndPair(const String &valueIdentifier, const String &value) + { + const String q = String('"'); + return q + valueIdentifier + q + ':' + q + value + F("\"}}"); + } +} + +namespace Serializer +{ + /* + * NOTE: The internal states may be changed in future updates, so the function signatures here are not guaranteed to be stable. + */ + + String serializeMeshState(const String &unsyncMsgID, const String &meshMsgCount) + { + using namespace JsonTranslator; + + // Returns: {"meshState":{"connectionState":{"unsyncMsgID":"123"},"meshMsgCount":"123"}} + return encode({FPSTR(jsonMeshState), encode({FPSTR(jsonConnectionState), encode({FPSTR(jsonUnsynchronizedMessageID), unsyncMsgID}), FPSTR(jsonMeshMessageCount), meshMsgCount})}); + } + + String serializeUnencryptedConnection(const String &unsyncMsgID) + { + using namespace JsonTranslator; + + // Returns: {"connectionState":{"unsyncMsgID":"123"}} + return encode({FPSTR(jsonConnectionState), encode({FPSTR(jsonUnsynchronizedMessageID), unsyncMsgID})}); + } + + String serializeEncryptedConnection(const String &duration, const String &desync, const String &ownSK, const String &peerSK, const String &peerStaMac, const String &peerApMac) + { + using namespace JsonTranslator; + + if(duration.isEmpty()) + { + // Returns: {"connectionState":{"desync":"0","ownSK":"1A2","peerSK":"3B4","peerStaMac":"F2","peerApMac":"E3"}} + return encode({FPSTR(jsonConnectionState), encode({FPSTR(jsonDesync), desync, FPSTR(jsonOwnSessionKey), ownSK, FPSTR(jsonPeerSessionKey), peerSK, + FPSTR(jsonPeerStaMac), peerStaMac, FPSTR(jsonPeerApMac), peerApMac})}); + } + + // Returns: {"connectionState":{"duration":"123","desync":"0","ownSK":"1A2","peerSK":"3B4","peerStaMac":"F2","peerApMac":"E3"}} + return encode({FPSTR(jsonConnectionState), encode({FPSTR(jsonDuration), duration, FPSTR(jsonDesync), desync, FPSTR(jsonOwnSessionKey), ownSK, FPSTR(jsonPeerSessionKey), peerSK, + FPSTR(jsonPeerStaMac), peerStaMac, FPSTR(jsonPeerApMac), peerApMac})}); + } + + String createEncryptedConnectionInfo(const String &infoHeader, const String &requestNonce, const String &authenticationPassword, const uint64_t ownSessionKey, const uint64_t peerSessionKey) + { + using namespace JsonTranslator; + + const String q = String('"'); + + // Returns: infoHeader{"arguments":{"nonce":"1F2","password":"abc","ownSK":"3B4","peerSK":"1A2"}} + return + infoHeader + + encode({FPSTR(jsonArguments), + encodeLiterally({FPSTR(jsonNonce), q + requestNonce + q, + FPSTR(jsonPassword), q + authenticationPassword + q, + FPSTR(jsonOwnSessionKey), q + TypeCast::uint64ToString(peerSessionKey) + q, // Exchanges session keys since it should be valid for the receiver. + FPSTR(jsonPeerSessionKey), q + TypeCast::uint64ToString(ownSessionKey) + q})}); + } + + String createEncryptionRequestHmacMessage(const String &requestHeader, const String &requestNonce, const uint8_t *hashKey, const uint8_t hashKeyLength, const uint32_t duration) + { + using namespace JsonTranslator; + + String mainMessage = requestHeader; + + if(requestHeader == FPSTR(EspnowProtocolInterpreter::temporaryEncryptionRequestHeader)) + { + mainMessage += encode({FPSTR(jsonArguments), encode({FPSTR(jsonDuration), String(duration), FPSTR(jsonNonce), requestNonce})}); + } + else + { + mainMessage += encode({FPSTR(jsonArguments), encode({FPSTR(jsonNonce), requestNonce})}); + } + + // We need to have an open JSON object so we can add the HMAC later. + mainMessage.remove(mainMessage.length() - 2); + mainMessage += ','; + + uint8_t staMac[6] {0}; + uint8_t apMac[6] {0}; + String requesterStaApMac = TypeCast::macToString(WiFi.macAddress(staMac)) + TypeCast::macToString(WiFi.softAPmacAddress(apMac)); + String hmac = MeshCryptoInterface::createMeshHmac(requesterStaApMac + mainMessage, hashKey, hashKeyLength); + + // Returns: requestHeader{"arguments":{"duration":"123","nonce":"1F2","hmac":"3B4"}} + return mainMessage + createJsonEndPair(FPSTR(jsonHmac), hmac); + } +} diff --git a/libraries/ESP8266WiFiMesh/src/Serializer.h b/libraries/ESP8266WiFiMesh/src/Serializer.h new file mode 100644 index 0000000000..d5d32dbaa2 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/Serializer.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2020 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#ifndef __ESP8266MESHSERIALIZER_H__ +#define __ESP8266MESHSERIALIZER_H__ + +#include + +namespace Serializer +{ + /* + * NOTE: The internal states may be changed in future updates, so the function signatures here are not guaranteed to be stable. + */ + + String serializeMeshState(const String &unsyncMsgID, const String &meshMsgCount); + String serializeUnencryptedConnection(const String &unsyncMsgID); + String serializeEncryptedConnection(const String &duration, const String &desync, const String &ownSK, const String &peerSK, const String &peerStaMac, const String &peerApMac); + + String createEncryptedConnectionInfo(const String &infoHeader, const String &requestNonce, const String &authenticationPassword, const uint64_t ownSessionKey, const uint64_t peerSessionKey); + String createEncryptionRequestHmacMessage(const String &requestHeader, const String &requestNonce, const uint8_t *hashKey, const uint8_t hashKeyLength, const uint32_t duration = 0); +} + +#endif diff --git a/libraries/ESP8266WiFiMesh/src/TcpIpMeshBackend.cpp b/libraries/ESP8266WiFiMesh/src/TcpIpMeshBackend.cpp new file mode 100644 index 0000000000..1e3c285c17 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/TcpIpMeshBackend.cpp @@ -0,0 +1,561 @@ +/* + TcpIpMeshBackend + + Copyright (c) 2015 Julian Fell and 2019 Anders Löfgren. All rights reserved. + + 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 +#include + +#include "TcpIpMeshBackend.h" +#include "TypeConversionFunctions.h" +#include "MutexTracker.h" +#include "ExpiringTimeTracker.h" + +namespace +{ + constexpr char SERVER_IP_ADDR[] PROGMEM = "192.168.4.1"; + + String _temporaryMessage; + String lastSSID; + bool staticIPActivated = false; + + // IP needs to be at the same subnet as server gateway (192.168.4 in this case). Station gateway ip must match ip for server. + IPAddress staticIP; + IPAddress gateway(192,168,4,1); + IPAddress subnetMask(255,255,255,0); +} + +const IPAddress TcpIpMeshBackend::emptyIP; + +std::shared_ptr TcpIpMeshBackend::_tcpIpTransmissionMutex = std::make_shared(false); +std::shared_ptr TcpIpMeshBackend::_tcpIpConnectionQueueMutex = std::make_shared(false); + +std::vector TcpIpMeshBackend::_connectionQueue = {}; +std::vector TcpIpMeshBackend::_latestTransmissionOutcomes = {}; + +TcpIpMeshBackend::TcpIpMeshBackend(const requestHandlerType requestHandler, const responseHandlerType responseHandler, + const networkFilterType networkFilter, const String &meshPassword, const String &ssidPrefix, + const String &ssidSuffix, const bool verboseMode, const uint8 meshWiFiChannel, const uint16_t serverPort) + : MeshBackendBase(requestHandler, responseHandler, networkFilter, MeshBackendType::TCP_IP), _server(serverPort) +{ + setSSID(ssidPrefix, emptyString, ssidSuffix); + setMeshPassword(meshPassword); + setVerboseModeState(verboseMode); + setWiFiChannel(meshWiFiChannel); + setServerPort(serverPort); +} + +std::vector & TcpIpMeshBackend::connectionQueue() +{ + MutexTracker connectionQueueMutexTracker(_tcpIpConnectionQueueMutex); + if(!connectionQueueMutexTracker.mutexCaptured()) + { + assert(false && String(F("ERROR! connectionQueue locked. Don't call connectionQueue() from callbacks other than NetworkFilter as this may corrupt program state!"))); + } + + return _connectionQueue; +} + +const std::vector & TcpIpMeshBackend::constConnectionQueue() +{ + return _connectionQueue; +} + +std::vector & TcpIpMeshBackend::latestTransmissionOutcomes() +{ + return _latestTransmissionOutcomes; +} + +bool TcpIpMeshBackend::latestTransmissionSuccessful() +{ + return latestTransmissionSuccessfulBase(latestTransmissionOutcomes()); +} + +void TcpIpMeshBackend::begin() +{ + if(!TcpIpMeshBackend::getAPController()) // If there is no active AP controller + WiFi.mode(WIFI_STA); // WIFI_AP_STA mode automatically sets up an AP, so we can't use that as default. + + #if LWIP_VERSION_MAJOR >= 2 + verboseModePrint(F("lwIP version is at least 2. Static ip optimizations enabled.\n")); + #else + verboseModePrint(F("lwIP version is less than 2. Static ip optimizations DISABLED.\n")); + #endif +} + +void TcpIpMeshBackend::activateAPHook() +{ + WiFi.softAP( getSSID().c_str(), getMeshPassword().c_str(), getWiFiChannel(), getAPHidden(), _maxAPStations ); // Note that a maximum of 8 TCP/IP stations can be connected at a time to each AP, max 4 by default. + + _server = WiFiServer(getServerPort()); // Fixes an occasional crash bug that occurs when using the copy constructor to duplicate the AP controller. + _server.begin(); // Actually calls _server.stop()/_server.close() first. +} + +void TcpIpMeshBackend::deactivateAPHook() +{ + _server.stop(); +} + +bool TcpIpMeshBackend::transmissionInProgress(){return *_tcpIpTransmissionMutex;} + +void TcpIpMeshBackend::setTemporaryMessage(const String &newTemporaryMessage) {_temporaryMessage = newTemporaryMessage;} +String TcpIpMeshBackend::getTemporaryMessage() const {return _temporaryMessage;} +void TcpIpMeshBackend::clearTemporaryMessage() {_temporaryMessage.clear();} + +String TcpIpMeshBackend::getCurrentMessage() const +{ + String message = getTemporaryMessage(); + + if(message.isEmpty()) // If no temporary message stored + message = getMessage(); + + return message; +} + +void TcpIpMeshBackend::setStaticIP(const IPAddress &newIP) +{ + // Comment out the line below to remove static IP and use DHCP instead. + // DHCP makes WiFi connection happen slower, but there is no need to care about manually giving different IPs to the nodes and less need to worry about used IPs giving "Server unavailable" issues. + // Static IP has faster connection times (50 % of DHCP) and will make sending of data to a node that is already transmitting data happen more reliably. + // Note that after WiFi.config(staticIP, gateway, subnetMask) is used, static IP will always be active, even for new connections, unless WiFi.config(0u,0u,0u); is called. + WiFi.config(newIP, gateway, subnetMask); + staticIPActivated = true; + staticIP = newIP; +} + +IPAddress TcpIpMeshBackend::getStaticIP() const +{ + if(staticIPActivated) + return staticIP; + + return emptyIP; +} + +void TcpIpMeshBackend::disableStaticIP() +{ + WiFi.config(0u,0u,0u); + yield(); + staticIPActivated = false; +} + +void TcpIpMeshBackend::setServerPort(const uint16_t serverPort) +{ + _serverPort = serverPort; + + // Apply changes to active AP. + if(isAPController()) + restartAP(); +} + +uint16_t TcpIpMeshBackend::getServerPort() const {return _serverPort;} + +void TcpIpMeshBackend::setMaxAPStations(const uint8_t maxAPStations) +{ + assert(maxAPStations <= 8); // Valid values are 0 to 8, but uint8_t is always at least 0. + + if(_maxAPStations != maxAPStations) + { + _maxAPStations = maxAPStations; + + // Apply changes to active AP. + if(isAPController()) + restartAP(); + } +} + +bool TcpIpMeshBackend::getMaxAPStations() const {return _maxAPStations;} + +void TcpIpMeshBackend::setConnectionAttemptTimeout(const uint32_t connectionAttemptTimeoutMs) +{ + _connectionAttemptTimeoutMs = connectionAttemptTimeoutMs; +} + +uint32_t TcpIpMeshBackend::getConnectionAttemptTimeout() const {return _connectionAttemptTimeoutMs;} + +void TcpIpMeshBackend::setStationModeTimeout(const int stationModeTimeoutMs) +{ + _stationModeTimeoutMs = stationModeTimeoutMs; +} + +int TcpIpMeshBackend::getStationModeTimeout() const {return _stationModeTimeoutMs;} + +void TcpIpMeshBackend::setAPModeTimeout(const uint32_t apModeTimeoutMs) +{ + _apModeTimeoutMs = apModeTimeoutMs; +} + +uint32_t TcpIpMeshBackend::getAPModeTimeout() const {return _apModeTimeoutMs;} + +/** + * Disconnect completely from a network. + */ +void TcpIpMeshBackend::fullStop(WiFiClient &currClient) +{ + currClient.stop(); + yield(); + WiFi.disconnect(); + yield(); +} + +/** + * Wait for a WiFiClient to transmit + * + * @return True if the client is ready, false otherwise. + * + */ +bool TcpIpMeshBackend::waitForClientTransmission(WiFiClient &currClient, const uint32_t maxWait) +{ + ExpiringTimeTracker timeout(maxWait); + + while(currClient.connected() && !currClient.available() && !timeout) + { + delay(1); + } + + /* Return false if the client isn't ready to communicate */ + if (WiFi.status() == WL_DISCONNECTED && !currClient.available()) + { + verboseModePrint(F("Disconnected!")); + return false; + } + + return true; +} + +/** + * Send the mesh instance's current message then read back the other node's response + * and pass that to the user-supplied responseHandler. + * + * @param currClient The client to which the message should be transmitted. + * @return A status code based on the outcome of the exchange. + * + */ +TransmissionStatusType TcpIpMeshBackend::exchangeInfo(WiFiClient &currClient) +{ + verboseModePrint(String(F("Transmitting"))); + + currClient.print(getCurrentMessage() + '\r'); + yield(); + + if (!waitForClientTransmission(currClient, _stationModeTimeoutMs)) + { + fullStop(currClient); + return TransmissionStatusType::CONNECTION_FAILED; + } + + if (!currClient.available()) + { + verboseModePrint(F("No response!")); + return TransmissionStatusType::TRANSMISSION_FAILED; // WiFi.status() != WL_DISCONNECTED so we do not want to use fullStop(currClient) here since that would force the node to scan for WiFi networks. + } + + String response = currClient.readStringUntil('\r'); + yield(); + currClient.flush(); + + /* Pass data to user callback */ + return getResponseHandler()(response, *this); +} + +/** + * Handle data transfer process with a connected AP. + * + * @return A status code based on the outcome of the data transfer attempt. + */ +TransmissionStatusType TcpIpMeshBackend::attemptDataTransfer() +{ + // Unlike WiFi.mode(WIFI_AP);, WiFi.mode(WIFI_AP_STA); allows us to stay connected to the AP we connected to in STA mode, at the same time as we can receive connections from other stations. + // We cannot send data to the AP in AP_STA mode though, that requires STA mode. + // Switching to STA mode will disconnect all stations connected to the node AP (though they can request a reconnect even while we are in STA mode). + WiFiMode_t storedWiFiMode = WiFi.getMode(); + WiFi.mode(WIFI_STA); + delay(1); + TransmissionStatusType transmissionOutcome = attemptDataTransferKernel(); + WiFi.mode(storedWiFiMode); + delay(1); + + return transmissionOutcome; +} + +/** + * Helper function that contains the core functionality for the data transfer process with a connected AP. + * + * @return A status code based on the outcome of the data transfer attempt. + */ +TransmissionStatusType TcpIpMeshBackend::attemptDataTransferKernel() +{ + WiFiClient currClient; + currClient.setTimeout(_stationModeTimeoutMs); + + /* Connect to the node's server */ + if (!currClient.connect(FPSTR(SERVER_IP_ADDR), getServerPort())) + { + fullStop(currClient); + verboseModePrint(F("Server unavailable")); + return TransmissionStatusType::CONNECTION_FAILED; + } + + TransmissionStatusType transmissionOutcome = exchangeInfo(currClient); + if (static_cast(transmissionOutcome) <= 0) + { + verboseModePrint(F("Transmission failed during exchangeInfo.")); + return transmissionOutcome; + } + + currClient.stop(); + yield(); + + return transmissionOutcome; +} + +void TcpIpMeshBackend::initiateConnectionToAP(const String &targetSSID, const int targetChannel, const uint8_t *targetBSSID) +{ + if(targetChannel == NETWORK_INFO_DEFAULT_INT) + WiFi.begin( targetSSID.c_str(), getMeshPassword().c_str() ); // Without giving channel and BSSID, connection time is longer. + else if(targetBSSID == NULL) + WiFi.begin( targetSSID.c_str(), getMeshPassword().c_str(), targetChannel ); // Without giving channel and BSSID, connection time is longer. + else + WiFi.begin( targetSSID.c_str(), getMeshPassword().c_str(), targetChannel, targetBSSID ); +} + +/** + * Connect to the AP at SSID and transmit the mesh instance's current message. + * + * @param targetSSID The name of the AP the other node has set up. + * @param targetChannel The WiFI channel of the AP the other node has set up. + * @param targetBSSID The MAC address of the AP the other node has set up. + * @return A status code based on the outcome of the connection and data transfer process. + * + */ +TransmissionStatusType TcpIpMeshBackend::connectToNode(const String &targetSSID, const int targetChannel, const uint8_t *targetBSSID) +{ + if(staticIPActivated && !lastSSID.isEmpty() && lastSSID != targetSSID) // So we only do this once per connection, in case there is a performance impact. + { + #if LWIP_VERSION_MAJOR >= 2 + // Can be used with Arduino core for ESP8266 version 2.4.2 or higher with lwIP2 enabled to keep static IP on even during network switches. + WiFiMode_t storedWiFiMode = WiFi.getMode(); + WiFi.mode(WIFI_OFF); + WiFi.mode(storedWiFiMode); + yield(); + + #else + // Disable static IP so that we can connect to other servers via DHCP (DHCP is slower but required for connecting to more than one server, it seems (possible bug?)). + disableStaticIP(); + verboseModePrint(F("\nConnecting to a different network. Static IP deactivated to make this possible.")); + + #endif + } + lastSSID = targetSSID; + + verboseModePrint(F("Connecting... "), false); + initiateConnectionToAP(targetSSID, targetChannel, targetBSSID); + + int attemptNumber = 1; + ExpiringTimeTracker connectionAttemptTimeout([this](){ return _connectionAttemptTimeoutMs; }); + + while((WiFi.status() == WL_DISCONNECTED) && !connectionAttemptTimeout) + { + if(connectionAttemptTimeout.elapsedTime() > attemptNumber * _connectionAttemptTimeoutMs) // _connectionAttemptTimeoutMs can be replaced (lowered) if you want to limit the time allowed for each connection attempt. + { + verboseModePrint(F("... "), false); + WiFi.disconnect(); + yield(); + initiateConnectionToAP(targetSSID, targetChannel, targetBSSID); + ++attemptNumber; + } + + delay(1); + } + + verboseModePrint(String(connectionAttemptTimeout.elapsedTime())); + + /* If the connection timed out */ + if (WiFi.status() != WL_CONNECTED) + { + verboseModePrint(F("Timeout")); + return TransmissionStatusType::CONNECTION_FAILED; + } + + return attemptDataTransfer(); +} + +TransmissionStatusType TcpIpMeshBackend::initiateTransmission(const TcpIpNetworkInfo &recipientInfo) +{ + WiFi.disconnect(); + yield(); + + assert(!recipientInfo.SSID().isEmpty()); // We need at least SSID to connect + String targetSSID = recipientInfo.SSID(); + int32_t targetWiFiChannel = recipientInfo.wifiChannel(); + uint8_t targetBSSID[6] {0}; + recipientInfo.getBSSID(targetBSSID); + + if(verboseMode()) // Avoid string generation if not required + { + printAPInfo(recipientInfo); + } + + return connectToNode(targetSSID, targetWiFiChannel, targetBSSID); +} + +void TcpIpMeshBackend::enterPostTransmissionState(const bool concludingDisconnect) +{ + if(WiFi.status() == WL_CONNECTED && staticIP != emptyIP && !staticIPActivated) + { + verboseModePrint(F("Reactivating static IP to allow for faster re-connects.")); + setStaticIP(staticIP); + } + + // If we do not want to be connected at end of transmission, disconnect here so we can re-enable static IP first (above). + if(concludingDisconnect) + { + WiFi.disconnect(); + yield(); + } +} + +void TcpIpMeshBackend::attemptTransmission(const String &message, const bool scan, const bool scanAllWiFiChannels, const bool concludingDisconnect, const bool initialDisconnect) +{ + MutexTracker mutexTracker(_tcpIpTransmissionMutex); + if(!mutexTracker.mutexCaptured()) + { + assert(false && String(F("ERROR! TCP/IP transmission in progress. Don't call attemptTransmission from callbacks as this may corrupt program state! Aborting."))); + return; + } + + if(initialDisconnect) + { + WiFi.disconnect(); + yield(); + } + + setMessage(message); + + latestTransmissionOutcomes().clear(); + + if(WiFi.status() == WL_CONNECTED) + { + TransmissionStatusType transmissionResult = attemptDataTransfer(); + latestTransmissionOutcomes().push_back(TransmissionOutcome(constConnectionQueue().back(), transmissionResult)); + + getTransmissionOutcomesUpdateHook()(*this); + } + else + { + if(scan) + { + connectionQueue().clear(); + scanForNetworks(scanAllWiFiChannels); + } + + MutexTracker connectionQueueMutexTracker(_tcpIpConnectionQueueMutex); + if(!connectionQueueMutexTracker.mutexCaptured()) + { + assert(false && String(F("ERROR! connectionQueue locked. Don't call attemptTransmission from callbacks as this may corrupt program state! Aborting."))); + } + else + { + for(const TcpIpNetworkInfo ¤tNetwork : constConnectionQueue()) + { + TransmissionStatusType transmissionResult = initiateTransmission(currentNetwork); + + latestTransmissionOutcomes().push_back(TransmissionOutcome{.origin = currentNetwork, .transmissionStatus = transmissionResult}); + + if(!getTransmissionOutcomesUpdateHook()(*this)) + break; + } + } + } + + enterPostTransmissionState(concludingDisconnect); +} + +void TcpIpMeshBackend::attemptTransmission(const String &message, const bool scan, const bool scanAllWiFiChannels) +{ + attemptTransmission(message, scan, scanAllWiFiChannels, true, false); +} + +TransmissionStatusType TcpIpMeshBackend::attemptTransmission(const String &message, const TcpIpNetworkInfo &recipientInfo, const bool concludingDisconnect, const bool initialDisconnect) +{ + MutexTracker mutexTracker(_tcpIpTransmissionMutex); + if(!mutexTracker.mutexCaptured()) + { + assert(false && String(F("ERROR! TCP/IP transmission in progress. Don't call attemptTransmission from callbacks as this may corrupt program state! Aborting."))); + return TransmissionStatusType::CONNECTION_FAILED; + } + + TransmissionStatusType transmissionResult = TransmissionStatusType::CONNECTION_FAILED; + setTemporaryMessage(message); + + if(initialDisconnect) + { + WiFi.disconnect(); + yield(); + } + + if(WiFi.status() == WL_CONNECTED && WiFi.SSID() == recipientInfo.SSID()) + { + transmissionResult = attemptDataTransfer(); + } + else + { + transmissionResult = initiateTransmission(recipientInfo); + } + + enterPostTransmissionState(concludingDisconnect); + clearTemporaryMessage(); + + return transmissionResult; +} + +void TcpIpMeshBackend::acceptRequests() +{ + MutexTracker mutexTracker(_tcpIpTransmissionMutex); + if(!mutexTracker.mutexCaptured()) + { + assert(false && String(F("ERROR! TCP/IP transmission in progress. Don't call acceptRequests from callbacks as this may corrupt program state! Aborting."))); + return; + } + + while (true) { + WiFiClient _client = _server.accept(); + + if (!_client) + break; + + if (!waitForClientTransmission(_client, _apModeTimeoutMs) || !_client.available()) { + continue; + } + + /* Read in request and pass it to the supplied requestHandler */ + String request = _client.readStringUntil('\r'); + yield(); + _client.flush(); + + String response = getRequestHandler()(request, *this); + + /* Send the response back to the client */ + if (_client.connected()) + { + verboseModePrint(String(F("Responding"))); + _client.print(response + '\r'); + _client.flush(); + yield(); + } + } +} diff --git a/libraries/ESP8266WiFiMesh/src/TcpIpMeshBackend.h b/libraries/ESP8266WiFiMesh/src/TcpIpMeshBackend.h new file mode 100644 index 0000000000..8aca27db0c --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/TcpIpMeshBackend.h @@ -0,0 +1,278 @@ +/* + TcpIpMeshBackend + + Copyright (c) 2015 Julian Fell and 2019 Anders Löfgren. All rights reserved. + + 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 +*/ + +// ESP-NOW is faster for small data payloads (up to a few kB, split over multiple messages). Transfer of up to 234 bytes takes 4 ms. +// In general ESP-NOW transfer time can be approximated with the following function: transferTime = ceil(bytesToTransfer / 234.0)*3 ms. +// If you only transfer 234 bytes at a time, this adds up to around 56kB/s. Finally a chance to relive the glory of the olden days +// when people were restricted to V90 dial-up modems for internet access! +// TCP-IP takes longer to connect (around 1000 ms), and an AP has to disconnect all connected stations in order to transfer data to another AP, +// but this backend has a much higher data transfer speed than ESP-NOW once connected (100x faster or so). + +#ifndef __TCPIPMESHBACKEND_H__ +#define __TCPIPMESHBACKEND_H__ + +#include +#include +#include +#include +#include "MeshBackendBase.h" +#include "TcpIpNetworkInfo.h" + +class TcpIpMeshBackend : public MeshBackendBase { + +public: + + /** + * TCP/IP constructor method. Creates a TCP/IP node, ready to be initialised. + * + * @param requestHandler The callback handler for dealing with received requests. Takes a string as an argument which + * is the request string received from another node and returns the string to send back. + * @param responseHandler The callback handler for dealing with received responses. Takes a string as an argument which + * is the response string received from another node. Returns a transmission status code as a TransmissionStatusType. + * @param networkFilter The callback handler for deciding which WiFi networks to connect to. + * @param meshPassword The WiFi password for the mesh network. + * @param ssidPrefix The prefix (first part) of the node SSID. + * @param ssidSuffix The suffix (last part) of the node SSID. + * @param verboseMode Determines if we should print the events occurring in the library to Serial. Off by default. This setting is separate for each TcpIpMeshBackend instance. + * @param meshWiFiChannel The WiFi channel used by the mesh network. Valid values are integers from 1 to 13. Defaults to 1. + * WARNING: The ESP8266 has only one WiFi channel, and the station/client mode is always prioritized for channel selection. + * This can cause problems if several mesh instances exist on the same ESP8266 and use different WiFi channels. + * In such a case, whenever the station of one mesh instance connects to an AP, it will silently force the + * WiFi channel of any active AP on the ESP8266 to match that of the station. This will cause disconnects and possibly + * make it impossible for other stations to detect the APs whose WiFi channels have changed. + * @param serverPort The server port used both by the AP of the TcpIpMeshBackend instance and when the instance connects to other APs. + * If multiple APs exist on a single ESP8266, each requires a separate server port. + * If two AP:s on the same ESP8266 are using the same server port, they will not be able to have both server instances active at the same time. + * This is managed automatically by the activateAP method. + * + */ + TcpIpMeshBackend(const requestHandlerType requestHandler, const responseHandlerType responseHandler, const networkFilterType networkFilter, + const String &meshPassword, const String &ssidPrefix, const String &ssidSuffix, const bool verboseMode = false, + const uint8 meshWiFiChannel = 1, const uint16_t serverPort = 4011); + + /** + * Returns a vector that contains the NetworkInfo for each WiFi network to connect to. + * This vector is unique for each mesh backend, but NetworkInfo elements can be directly transferred between the vectors as long as both SSID and BSSID are present. + * The connectionQueue vector is cleared before each new scan and filled via the networkFilter callback function once the scan completes. + * WiFi connections will start with connectionQueue[0] and then incrementally proceed to higher vector positions. + * Note that old network indices often are invalidated whenever a new WiFi network scan occurs. + * + * Since the connectionQueue() is iterated over during transmissions, always use constConnectionQueue() from callbacks other than NetworkFilter. + */ + static std::vector & connectionQueue(); + + /** + * Same as connectionQueue(), but can be called from all callbacks since the returned reference is const. + */ + static const std::vector & constConnectionQueue(); + + /** + * Returns a vector with the TransmissionOutcome for each AP to which a transmission was attempted during the latest attemptTransmission call. + * This vector is unique for each mesh backend. + * The latestTransmissionOutcomes vector is cleared before each new transmission attempt. + * Connection attempts are indexed in the same order they were attempted. + * Note that old network indices often are invalidated whenever a new WiFi network scan occurs. + */ + static std::vector & latestTransmissionOutcomes(); + + /** + * @return True if latest transmission was successful (i.e. latestTransmissionOutcomes is not empty and all entries have transmissionStatus TransmissionStatusType::TRANSMISSION_COMPLETE). False otherwise. + * The result is unique for each mesh backend. + */ + static bool latestTransmissionSuccessful(); + + /** + * Initialises the node. + */ + void begin() override; + + /** + * If AP connection already exists, and the initialDisconnect argument is set to false, send message only to the already connected AP. + * Otherwise, scan for other networks, send the scan result to networkFilter and then transmit the message to the networks found in connectionQueue. + * + * @param message The message to send to other nodes. It will be stored in the class instance until replaced via attemptTransmission or setMessage. + * @param concludingDisconnect Disconnect from AP once transmission is complete. Defaults to true. + * @param initialDisconnect Disconnect from any currently connected AP before attempting transmission. Defaults to false. + * @param scan Scan for new networks and call the networkFilter function with the scan results. When set to false, only the data already in connectionQueue will be used for the transmission. + * @param scanAllWiFiChannels Scan all WiFi channels during a WiFi scan, instead of just the channel the MeshBackendBase instance is using. + * Scanning all WiFi channels takes about 2100 ms, compared to just 60 ms if only channel 1 (standard) is scanned. + * Note that if the ESP8266 has an active AP, that AP will switch WiFi channel to match that of any other AP the ESP8266 connects to. + * This can make it impossible for other nodes to detect the AP if they are scanning the wrong WiFi channel. + */ + void attemptTransmission(const String &message, const bool scan, const bool scanAllWiFiChannels, const bool concludingDisconnect, const bool initialDisconnect = false); + + void attemptTransmission(const String &message, const bool scan = true, const bool scanAllWiFiChannels = false) override; + + /** + * Transmit message to a single recipient without changing the local transmission state (apart from connecting to the recipient if required). + * Will not change connectionQueue, latestTransmissionOutcomes or stored message. + * + * Note that if wifiChannel and BSSID are missing from recipientInfo, connection time will be longer. + */ + TransmissionStatusType attemptTransmission(const String &message, const TcpIpNetworkInfo &recipientInfo, const bool concludingDisconnect = true, const bool initialDisconnect = false); + + /** + * If any clients are connected, accept their requests and call the requestHandler function for each one. + */ + void acceptRequests(); + + /** + * Get the TCP/IP message that is currently scheduled for transmission. + * Unlike the getMessage() method, this will be correct even when the single recipient attemptTransmission method is used. + */ + String getCurrentMessage() const; + + /** + * Set a static IP address for the ESP8266 and activate use of static IP. + * The static IP needs to be at the same subnet as the server's gateway. + */ + void setStaticIP(const IPAddress &newIP); + IPAddress getStaticIP() const; + void disableStaticIP(); + + /** + * An empty IPAddress. Used as default when no IP is set. + */ + static const IPAddress emptyIP; + + /** + * Set the server port used both by the AP of the TcpIpMeshBackend instance and when the instance connects to other APs. + * If multiple APs exist on a single ESP8266, each requires a separate server port. + * If two AP:s on the same ESP8266 are using the same server port, they will not be able to have both server instances active at the same time. + * This is managed automatically by the activateAP method. + * Will also change the setting for the active AP (via an AP restart) + * if this TcpIpMeshBackend instance is the current AP controller. + * + * @param serverPort The server port to use. + * + */ + void setServerPort(const uint16_t serverPort); + uint16_t getServerPort() const; + + /** + * Set the maximum number of stations that can simultaneously be connected to the AP controlled by this TcpIpMeshBackend instance. + * This number is 4 by default. + * Once the max number has been reached, any other station that wants to connect will be forced to wait until an already connected station disconnects. + * The more stations that are connected, the more memory is required. + * Will also change the setting for the active AP (via an AP restart) + * if this TcpIpMeshBackend instance is the current AP controller. + * + * @param maxAPStations The maximum number of simultaneous station connections allowed. Valid values are 0 to 8. + */ + void setMaxAPStations(const uint8_t maxAPStations); + bool getMaxAPStations() const; + + /** + * Set the timeout for each attempt to connect to another AP that occurs through the attemptTransmission method by this TcpIpMeshBackend instance. + * The timeout is 10 000 ms by default. + * + * @param connectionAttemptTimeoutMs The timeout for each connection attempt, in milliseconds. + */ + void setConnectionAttemptTimeout(const uint32_t connectionAttemptTimeoutMs); + uint32_t getConnectionAttemptTimeout() const; + + /** + * Set the timeout to use for transmissions when this TcpIpMeshBackend instance acts as a station (i.e. when connected to another AP). + * This will affect the timeout of the attemptTransmission method once a connection to an AP has been established. + * The timeout is 5 000 ms by default. + * + * @param stationModeTimeoutMs The timeout to use, in milliseconds. + */ + void setStationModeTimeout(const int stationModeTimeoutMs); + int getStationModeTimeout() const; + + /** + * Set the timeout to use for transmissions when this TcpIpMeshBackend instance acts as an AP (i.e. when receiving connections from other stations). + * This will affect the timeout of the acceptRequests method. + * The timeout is 4 500 ms by default. + * Will also change the setting for the active AP (without an AP restart) + * if this TcpIpMeshBackend instance is the current AP controller. + * + * @param apModeTimeoutMs The timeout to use, in milliseconds. + */ + void setAPModeTimeout(const uint32_t apModeTimeoutMs); + uint32_t getAPModeTimeout() const; + +protected: + + static std::vector _connectionQueue; + static std::vector _latestTransmissionOutcomes; + + /** + * Called just before we activate the AP. + * Put _server.stop() in deactivateAPHook() in case you use _server.begin() here. + */ + void activateAPHook() override; + + /** + * Called just before we deactivate the AP. + * Put _server.stop() here in case you use _server.begin() in activateAPHook(). + */ + void deactivateAPHook() override; + + /** + * Will be true if a transmission initiated by a public method is in progress. + */ + static std::shared_ptr _tcpIpTransmissionMutex; + + /** + * Will be true when the connectionQueue should not be modified. + */ + static std::shared_ptr _tcpIpConnectionQueueMutex; + + /** + * Check if there is an ongoing TCP/IP transmission in the library. Used to avoid interrupting transmissions. + * + * @return True if a transmission initiated by a public method is in progress. + */ + static bool transmissionInProgress(); + + /** + * Set a message that will be sent to other nodes when calling attemptTransmission, instead of the regular getMessage(). + * This message is used until clearTemporaryMessage() is called. + * + * @param newMessage The message to send. + */ + void setTemporaryMessage(const String &newMessage); + String getTemporaryMessage() const; + void clearTemporaryMessage(); + +private: + + void fullStop(WiFiClient &currClient); + void initiateConnectionToAP(const String &targetSSID, const int targetChannel = NETWORK_INFO_DEFAULT_INT, const uint8_t *targetBSSID = NULL); + TransmissionStatusType connectToNode(const String &targetSSID, const int targetChannel = NETWORK_INFO_DEFAULT_INT, const uint8_t *targetBSSID = NULL); + TransmissionStatusType exchangeInfo(WiFiClient &currClient); + bool waitForClientTransmission(WiFiClient &currClient, const uint32_t maxWait); + TransmissionStatusType attemptDataTransfer(); + TransmissionStatusType attemptDataTransferKernel(); + TransmissionStatusType initiateTransmission(const TcpIpNetworkInfo &recipientInfo); + void enterPostTransmissionState(const bool concludingDisconnect); + + uint32_t _connectionAttemptTimeoutMs = 10000; + int _stationModeTimeoutMs = 5000; // int is the type used in the Arduino core for this particular API, not uint32_t, which is why we use int here. + uint32_t _apModeTimeoutMs = 4500; + + WiFiServer _server; + uint16_t _serverPort; + uint8_t _maxAPStations = 4; // Only affects TCP/IP connections, not ESP-NOW connections + + bool useStaticIP; +}; + +#endif diff --git a/libraries/ESP8266WiFiMesh/src/TcpIpNetworkInfo.cpp b/libraries/ESP8266WiFiMesh/src/TcpIpNetworkInfo.cpp new file mode 100644 index 0000000000..874b750211 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/TcpIpNetworkInfo.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#include "TcpIpNetworkInfo.h" +#include + +TcpIpNetworkInfo::TcpIpNetworkInfo(const int networkIndex) : NetworkInfoBase(networkIndex) { }; + + +TcpIpNetworkInfo::TcpIpNetworkInfo(const NetworkInfoBase &originalNetworkInfo) : NetworkInfoBase(originalNetworkInfo) +{ + assert(SSID() != defaultSSID); // We need at least SSID to be able to connect. +}; + +TcpIpNetworkInfo::TcpIpNetworkInfo(const String &SSID, const int32_t wifiChannel, const uint8_t BSSID[6], const uint8_t encryptionType, const int32_t RSSI , const bool isHidden) + : NetworkInfoBase(SSID, wifiChannel, BSSID, encryptionType, RSSI, isHidden) +{ } diff --git a/libraries/ESP8266WiFiMesh/src/TcpIpNetworkInfo.h b/libraries/ESP8266WiFiMesh/src/TcpIpNetworkInfo.h new file mode 100644 index 0000000000..c3eb63d03a --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/TcpIpNetworkInfo.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#ifndef __TCPIPNETWORKINFO_H__ +#define __TCPIPNETWORKINFO_H__ + +#include "NetworkInfoBase.h" + +class TcpIpNetworkInfo : public NetworkInfoBase { + +public: + + /** + * Automatically fill in the rest of the network info using networkIndex and the WiFi scan results. + */ + TcpIpNetworkInfo(const int networkIndex); + + + TcpIpNetworkInfo(const NetworkInfoBase &originalNetworkInfo); + + /** + * Without giving wifiChannel and BSSID, connection time is longer. + */ + TcpIpNetworkInfo(const String &SSID, const int32_t wifiChannel = defaultWifiChannel, const uint8_t BSSID[6] = defaultBSSID, const uint8_t encryptionType = defaultEncryptionType, + const int32_t RSSI = defaultRSSI, const bool isHidden = defaultIsHidden); +}; + +#endif diff --git a/libraries/ESP8266WiFiMesh/src/TimeTracker.cpp b/libraries/ESP8266WiFiMesh/src/TimeTracker.cpp new file mode 100644 index 0000000000..c98e73fe04 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/TimeTracker.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#include "TimeTracker.h" +#include + +TimeTracker::TimeTracker(const uint32_t creationTimeMs) : _creationTimeMs(creationTimeMs) +{ } + +uint32_t TimeTracker::timeSinceCreation() const +{ + return millis() - creationTimeMs(); // Will work even when millis() overflow: http://forum.arduino.cc/index.php/topic,42997.0.html +} + +uint32_t TimeTracker::creationTimeMs() const +{ + return _creationTimeMs; +} diff --git a/libraries/ESP8266WiFiMesh/src/TimeTracker.h b/libraries/ESP8266WiFiMesh/src/TimeTracker.h new file mode 100644 index 0000000000..970566065e --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/TimeTracker.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#ifndef __TIMETRACKER_H__ +#define __TIMETRACKER_H__ + +#include + +// Minimal time tracking class. Used instead of other classes like ExpiringTimeTracker when small memory footprint is important and other functionality not required. +class TimeTracker { + +public: + + virtual ~TimeTracker() = default; + + TimeTracker(const uint32_t creationTimeMs); + uint32_t timeSinceCreation() const; + uint32_t creationTimeMs() const; + +private: + + uint32_t _creationTimeMs; +}; + +#endif diff --git a/libraries/ESP8266WiFiMesh/src/TransmissionOutcome.cpp b/libraries/ESP8266WiFiMesh/src/TransmissionOutcome.cpp new file mode 100644 index 0000000000..7fb6b567b4 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/TransmissionOutcome.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#include "TransmissionOutcome.h" + +TransmissionOutcome::TransmissionOutcome(const NetworkInfoBase &origin, const TransmissionStatusType transmissionStatus) + : NetworkInfoBase(origin), _transmissionStatus(transmissionStatus) +{ } + +TransmissionOutcome::TransmissionOutcome(const String &SSID, const int32_t wifiChannel, const uint8_t BSSID[6], const uint8_t encryptionType, + const int32_t RSSI, const bool isHidden, const TransmissionStatusType transmissionStatus) + : NetworkInfoBase(SSID, wifiChannel, BSSID, encryptionType, RSSI, isHidden), _transmissionStatus(transmissionStatus) +{ } + +void TransmissionOutcome::setTransmissionStatus(const TransmissionStatusType transmissionStatus) { _transmissionStatus = transmissionStatus; } +TransmissionStatusType TransmissionOutcome::transmissionStatus() const { return _transmissionStatus; } diff --git a/libraries/ESP8266WiFiMesh/src/TransmissionOutcome.h b/libraries/ESP8266WiFiMesh/src/TransmissionOutcome.h new file mode 100644 index 0000000000..cdc9437b6f --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/TransmissionOutcome.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#ifndef __TRANSMISSIONOUTCOME_H__ +#define __TRANSMISSIONOUTCOME_H__ + +#include +#include "NetworkInfoBase.h" + +enum class TransmissionStatusType +{ + CONNECTION_FAILED = -1, + TRANSMISSION_FAILED = 0, + TRANSMISSION_COMPLETE = 1 +}; + +class TransmissionOutcome : public NetworkInfoBase { + +public: + + TransmissionOutcome(const NetworkInfoBase &origin, const TransmissionStatusType transmissionStatus); + + TransmissionOutcome(const String &SSID, const int32_t wifiChannel, const uint8_t BSSID[6], const uint8_t encryptionType, + const int32_t RSSI, const bool isHidden, const TransmissionStatusType transmissionStatus); + + void setTransmissionStatus(const TransmissionStatusType transmissionStatus); + TransmissionStatusType transmissionStatus() const; + +private: + + TransmissionStatusType _transmissionStatus; +}; + +#endif diff --git a/libraries/ESP8266WiFiMesh/src/TransmissionResult.cpp b/libraries/ESP8266WiFiMesh/src/TransmissionResult.cpp index 81f9312461..29b37675c6 100644 --- a/libraries/ESP8266WiFiMesh/src/TransmissionResult.cpp +++ b/libraries/ESP8266WiFiMesh/src/TransmissionResult.cpp @@ -23,6 +23,29 @@ * THE SOFTWARE. */ + + + + + + +/******************************************************************************************** +* NOTE! +* +* This class is deprecated and will be removed in core version 3.0.0. +* If you are still using this class, please consider migrating to the new API shown in +* the EspnowNetworkInfo.h or TcpIpNetworkInfo.h source files. +* +* TODO: delete this file. +********************************************************************************************/ + + + + + + + + #include "TransmissionResult.h" TransmissionResult::TransmissionResult(int newNetworkIndex, transmission_status_t newTransmissionStatus, bool autofill) : diff --git a/libraries/ESP8266WiFiMesh/src/TransmissionResult.h b/libraries/ESP8266WiFiMesh/src/TransmissionResult.h index 8cc4cc020b..709e258f97 100644 --- a/libraries/ESP8266WiFiMesh/src/TransmissionResult.h +++ b/libraries/ESP8266WiFiMesh/src/TransmissionResult.h @@ -23,11 +23,36 @@ * THE SOFTWARE. */ + + + + + + +/******************************************************************************************** +* NOTE! +* +* This class is deprecated and will be removed in core version 3.0.0. +* If you are still using this class, please consider migrating to the new API shown in +* the EspnowNetworkInfo.h or TcpIpNetworkInfo.h source files. +* +* TODO: delete this file. +********************************************************************************************/ + + + + + + + + + #ifndef __TRANSMISSIONRESULT_H__ #define __TRANSMISSIONRESULT_H__ #include #include "NetworkInfo.h" +#include "TransmissionOutcome.h" typedef enum { @@ -45,11 +70,11 @@ class TransmissionResult : public NetworkInfo { /** * @param autofill Automatically fill in the rest of the network info using newNetworkIndex and the WiFi scan results. */ - TransmissionResult(int newNetworkIndex, transmission_status_t newTransmissionStatus, bool autofill = true); + TransmissionResult(int newNetworkIndex, transmission_status_t newTransmissionStatus, bool autofill = true) __attribute__((deprecated)); - TransmissionResult(const String &newSSID, int newWiFiChannel, uint8_t newBSSID[6], transmission_status_t newTransmissionStatus); + TransmissionResult(const String &newSSID, int newWiFiChannel, uint8_t newBSSID[6], transmission_status_t newTransmissionStatus) __attribute__((deprecated)); - TransmissionResult(const String &newSSID, int newWiFiChannel, uint8_t newBSSID[6], int newNetworkIndex, transmission_status_t newTransmissionStatus); + TransmissionResult(const String &newSSID, int newWiFiChannel, uint8_t newBSSID[6], int newNetworkIndex, transmission_status_t newTransmissionStatus) __attribute__((deprecated)); TransmissionResult(const NetworkInfo& origin, transmission_status_t newTransmissionStatus); }; diff --git a/libraries/ESP8266WiFiMesh/src/TypeConversionFunctions.cpp b/libraries/ESP8266WiFiMesh/src/TypeConversionFunctions.cpp index be908b556e..7e4b8c3419 100644 --- a/libraries/ESP8266WiFiMesh/src/TypeConversionFunctions.cpp +++ b/libraries/ESP8266WiFiMesh/src/TypeConversionFunctions.cpp @@ -1,6 +1,6 @@ /* * TypeConversionFunctions - * Copyright (C) 2018 Anders Löfgren + * Copyright (C) 2018-2019 Anders Löfgren * * License (MIT license): * @@ -24,35 +24,184 @@ */ #include "TypeConversionFunctions.h" +#include "MeshBackendBase.h" +#include "TcpIpMeshBackend.h" +#include "EspnowMeshBackend.h" +#include "TypeConversion.h" -String uint64ToString(uint64_t number, byte base) +using namespace experimental::TypeConversion; + +namespace MeshTypeConversionFunctions { - assert(2 <= base && base <= 36); + String uint64ToString(uint64_t number, const uint8_t base) + { + assert(2 <= base && base <= 36); + + String result; + + if(base == 16) + { + do { + result += (char)pgm_read_byte(base36Chars + number % base); + number >>= 4; // We could write number /= 16; and the compiler would optimize it to a shift, but the explicit shift notation makes it clearer where the speed-up comes from. + } while ( number ); + } + else + { + do { + result += (char)pgm_read_byte(base36Chars + number % base); + number /= base; + } while ( number ); + } + + std::reverse( result.begin(), result.end() ); + + return result; + } - String result = ""; - - while(number > 0) + uint64_t stringToUint64(const String &string, const uint8_t base) { - result = String((uint32_t)(number % base), base) + result; - number /= base; + assert(2 <= base && base <= 36); + + uint64_t result = 0; + + if(base == 16) + { + for(uint32_t i = 0; i < string.length(); ++i) + { + result <<= 4; // We could write result *= 16; and the compiler would optimize it to a shift, but the explicit shift notation makes it clearer where the speed-up comes from. + result += pgm_read_byte(base36CharValues + string.charAt(i) - '0'); + } + } + else + { + for(uint32_t i = 0; i < string.length(); ++i) + { + result *= base; + result += pgm_read_byte(base36CharValues + string.charAt(i) - '0'); + } + } + + return result; } - return (result == "" ? "0" : result); -} - -uint64_t stringToUint64(const String &string, byte base) -{ - assert(2 <= base && base <= 36); + String uint8ArrayToHexString(const uint8_t *uint8Array, const uint32_t arrayLength) + { + return experimental::TypeConversion::uint8ArrayToHexString(uint8Array, arrayLength); + } - uint64_t result = 0; - - char currentCharacter[1]; - for(uint32_t i = 0; i < string.length(); i++) + uint8_t *hexStringToUint8Array(const String &hexString, uint8_t *uint8Array, const uint32_t arrayLength) + { + return experimental::TypeConversion::hexStringToUint8Array(hexString, uint8Array, arrayLength); + } + + String uint8ArrayToMultiString(uint8_t *uint8Array, const uint32_t arrayLength) + { + String multiString; + if(!multiString.reserve(arrayLength)) + return emptyString; + + // Ensure we have a NULL terminated character array so the String() constructor knows where to stop. + char finalChar = uint8Array[arrayLength - 1]; + uint8Array[arrayLength - 1] = 0; + + multiString += (char *)(uint8Array); + while(multiString.length() < arrayLength - 1) + { + multiString += (char)0; // String construction only stops for null values, so we need to add those manually. + multiString += (char *)(uint8Array + multiString.length()); + } + + multiString += finalChar; + uint8Array[arrayLength - 1] = finalChar; + + return multiString; + } + + String bufferedUint8ArrayToMultiString(const uint8_t *uint8Array, const uint32_t arrayLength) + { + String multiString; + if(!multiString.reserve(arrayLength)) + return emptyString; + + // Ensure we have a NULL terminated character array so the String() constructor knows where to stop. + uint8_t bufferedData[arrayLength + 1]; + std::copy_n(uint8Array, arrayLength, bufferedData); + bufferedData[arrayLength] = 0; + + multiString += (char *)(bufferedData); + while(multiString.length() < arrayLength) + { + multiString += (char)0; // String construction only stops for null values, so we need to add those manually. + multiString += (char *)(bufferedData + multiString.length()); + } + + return multiString; + } + + String macToString(const uint8_t *mac) + { + return MeshTypeConversionFunctions::uint8ArrayToHexString(mac, 6); + } + + uint8_t *stringToMac(const String &macString, uint8_t *macArray) + { + return MeshTypeConversionFunctions::hexStringToUint8Array(macString, macArray, 6); + } + + uint64_t macToUint64(const uint8_t *macArray) + { + uint64_t result = (uint64_t)macArray[0] << 40 | (uint64_t)macArray[1] << 32 | (uint64_t)macArray[2] << 24 | (uint64_t)macArray[3] << 16 | (uint64_t)macArray[4] << 8 | (uint64_t)macArray[5]; + return result; + } + + uint8_t *uint64ToMac(const uint64_t macValue, uint8_t *macArray) + { + assert(macValue <= 0xFFFFFFFFFFFF); // Overflow will occur if value can't fit within 6 bytes + + macArray[5] = macValue; + macArray[4] = macValue >> 8; + macArray[3] = macValue >> 16; + macArray[2] = macValue >> 24; + macArray[1] = macValue >> 32; + macArray[0] = macValue >> 40; + + return macArray; + } + + uint8_t *uint64ToUint8Array(const uint64_t value, uint8_t *resultArray) + { + return uint64ToUint8ArrayBE(value, resultArray); + } + + uint64_t uint8ArrayToUint64(const uint8_t *inputArray) + { + return uint8ArrayToUint64BE(inputArray); + } + + /** + * Helper function for meshBackendCast. + */ + template + T attemptPointerCast(MeshBackendBase *meshBackendBaseInstance, MeshBackendType resultClassType) + { + if(meshBackendBaseInstance && meshBackendBaseInstance->getClassType() == resultClassType) + { + return static_cast(meshBackendBaseInstance); + } + + return nullptr; + } + + template <> + EspnowMeshBackend *meshBackendCast(MeshBackendBase *meshBackendBaseInstance) { - result *= base; - currentCharacter[0] = string.charAt(i); - result += strtoul(currentCharacter, NULL, base); + return attemptPointerCast(meshBackendBaseInstance, MeshBackendType::ESP_NOW); } - return result; + template <> + TcpIpMeshBackend *meshBackendCast(MeshBackendBase *meshBackendBaseInstance) + { + return attemptPointerCast(meshBackendBaseInstance, MeshBackendType::TCP_IP); + } } diff --git a/libraries/ESP8266WiFiMesh/src/TypeConversionFunctions.h b/libraries/ESP8266WiFiMesh/src/TypeConversionFunctions.h index 5d42e414cc..c8cc72d1ac 100644 --- a/libraries/ESP8266WiFiMesh/src/TypeConversionFunctions.h +++ b/libraries/ESP8266WiFiMesh/src/TypeConversionFunctions.h @@ -1,6 +1,6 @@ /* * TypeConversionFunctions - * Copyright (C) 2018 Anders Löfgren + * Copyright (C) 2018-2019 Anders Löfgren * * License (MIT license): * @@ -29,22 +29,155 @@ #include #include -/** - * Note that using a base higher than 16 increases likelihood of randomly generating SSID strings containing controversial words. - * - * @param number The number to convert to a string with radix "base". - * @param base The radix to convert "number" into. Must be between 2 and 36. - * @returns A string of "number" encoded in radix "base". - */ -String uint64ToString(uint64_t number, byte base = 16); +class MeshBackendBase; +class TcpIpMeshBackend; +class EspnowMeshBackend; -/** - * Note that using a base higher than 16 increases likelihood of randomly generating SSID strings containing controversial words. - * - * @param string The string to convert to uint64_t. String must use radix "base". - * @param base The radix of "string". Must be between 2 and 36. - * @returns A uint64_t of the string, using radix "base" during decoding. - */ -uint64_t stringToUint64(const String &string, byte base = 16); +namespace MeshTypeConversionFunctions +{ + /** + * Note that using base 10 instead of 16 increases conversion time by roughly a factor of 5, due to unfavourable 64-bit arithmetic. + * Note that using a base higher than 16 increases likelihood of randomly generating SSID strings containing controversial words. + * + * @param number The number to convert to a string with radix "base". + * @param base The radix to convert "number" into. Must be between 2 and 36. + * @return A string of "number" encoded in radix "base". + */ + String uint64ToString(uint64_t number, const uint8_t base = 16); + + /** + * Note that using base 10 instead of 16 increases conversion time by roughly a factor of 2, due to unfavourable 64-bit arithmetic. + * Note that using a base higher than 16 increases likelihood of randomly generating SSID strings containing controversial words. + * + * @param string The string to convert to uint64_t. String must use radix "base". + * @param base The radix of "string". Must be between 2 and 36. + * @return A uint64_t of the string, using radix "base" during decoding. + */ + uint64_t stringToUint64(const String &string, const uint8_t base = 16); + + /** + * Convert the contents of a uint8_t array to a String in HEX format. The resulting String starts from index 0 of the array. + * All array elements will be padded with zeroes to ensure they are converted to 2 String characters each. + * + * @param uint8Array The array to make into a HEX String. + * @param arrayLength The size of uint8Array, in bytes. + * @return Normally a String containing the HEX representation of the uint8Array. An empty String if the memory allocation for the String failed. + */ + String uint8ArrayToHexString(const uint8_t *uint8Array, const uint32_t arrayLength); + + /** + * Convert the contents of a String in HEX format to a uint8_t array. Index 0 of the array will represent the start of the String. + * There must be 2 String characters for each array element. Use padding with zeroes where required. + * + * @param hexString The HEX String to convert to a uint8_t array. Must contain at least 2*arrayLength characters. + * @param uint8Array The array to fill with the contents of the hexString. + * @param arrayLength The number of bytes to fill in uint8Array. + * @return A pointer to the uint8Array. + */ + uint8_t *hexStringToUint8Array(const String &hexString, uint8_t *uint8Array, const uint32_t arrayLength); + + /** + * Stores the exact values of uint8Array in a String, even null values. + * Note that Strings containing null values will look like several separate Strings to methods that rely on null values to find the String end, such as String::substring. + * In these cases, it may be helpful to use String::c_str() or String::begin() to access the String data buffer directly instead. + * + * The unbuffered version temporarily edits uint8Array during execution, but restores the array to its original state when returning in a controlled manner. + * + * @param uint8Array The array to make into a multiString. + * @param arrayLength The size of uint8Array, in bytes. + * @return Normally a String containing the same data as the uint8Array. An empty String if the memory allocation for the String failed. + */ + String uint8ArrayToMultiString(uint8_t *uint8Array, const uint32_t arrayLength); + + /** + * Stores the exact values of uint8Array in a String, even null values. + * Note that Strings containing null values will look like several separate Strings to methods that rely on null values to find the String end, such as String::substring. + * In these cases, it may be helpful to use String::c_str() or String::begin() to access the String data buffer directly instead. + * + * The buffered version is slower and uses more memory than the unbuffered version, but can operate on const arrays. + * + * @param uint8Array The array to make into a multiString. + * @param arrayLength The size of uint8Array, in bytes. + * @return Normally a String containing the same data as the uint8Array. An empty String if the memory allocation for the String failed. + */ + String bufferedUint8ArrayToMultiString(const uint8_t *uint8Array, const uint32_t arrayLength); + + /** + * Takes a uint8_t array and converts the first 6 bytes to a hexadecimal string. + * + * @param mac A uint8_t array with the mac address to convert to a string. Should be 6 bytes in total. + * @return A hexadecimal string representation of the mac. + */ + String macToString(const uint8_t *mac); + + /** + * Takes a String and converts the first 12 characters to uint8_t numbers which are stored in the macArray from low to high index. Assumes hexadecimal number encoding. + * + * @param macString A String which begins with the mac address to store in the array as a hexadecimal number. + * @param macArray A uint8_t array that will hold the mac address once the function returns. Should have a size of at least 6 bytes. + * @return The macArray. + */ + uint8_t *stringToMac(const String &macString, uint8_t *macArray); + + /** + * Takes a uint8_t array and converts the first 6 bytes to a uint64_t. Assumes index 0 of the array contains MSB. + * + * @param macArray A uint8_t array with the mac address to convert to a uint64_t. Should be 6 bytes in total. + * @return A uint64_t representation of the mac. + */ + uint64_t macToUint64(const uint8_t *macArray); + + /** + * Takes a uint64_t value and stores the bits of the first 6 bytes (LSB) in a uint8_t array. Assumes index 0 of the array should contain MSB. + * + * @param macValue The uint64_t value to convert to a mac array. Value must fit within 6 bytes. + * @param macArray A uint8_t array that will hold the mac address once the function returns. Should have a size of at least 6 bytes. + * @return The macArray. + */ + uint8_t *uint64ToMac(const uint64_t macValue, uint8_t *macArray); + + /** + * Takes a uint64_t value and stores the bits in a uint8_t array. Assumes index 0 of the array should contain MSB (big endian). + * + * @param value The uint64_t value to convert to a uint8_t array. + * @param resultArray A uint8_t array that will hold the result once the function returns. Should have a size of at least 8 bytes. + * @return The resultArray. + */ + uint8_t *uint64ToUint8Array(const uint64_t value, uint8_t *resultArray); + + /** + * Takes a uint8_t array and converts the first 8 (lowest index) elements to a uint64_t. Assumes index 0 of the array contains MSB (big endian). + * + * @param inputArray A uint8_t array containing the data to convert to a uint64_t. Should have a size of at least 8 bytes. + * @return A uint64_t representation of the first 8 bytes of the array. + */ + uint64_t uint8ArrayToUint64(const uint8_t *inputArray); + + /** + * Conversion function that can be used on MeshBackend classes instead of dynamic_cast since RTTI is disabled. + * + * @param T The MeshBackend class pointer type to cast the meshBackendBaseInstance pointer into. + * @param meshBackendBaseInstance The instance pointer to cast. + * @return A pointer of type T to meshBackendBaseInstance if meshBackendBaseInstance is of type T. nullptr otherwise. + */ + template + T meshBackendCast(MeshBackendBase *meshBackendBaseInstance) + { + // The only valid template arguments are handled by the template specializations below, so ending up here is an error. + static_assert(std::is_same::value || std::is_same::value, + "Error: Invalid MeshBackend class type. Make sure the template argument to meshBackendCast is supported!"); + } + + // These template specializations allow us to put the main template functionality in the .cpp file (which gives better encapsulation). + template <> + EspnowMeshBackend *meshBackendCast(MeshBackendBase *meshBackendBaseInstance); + + template <> + TcpIpMeshBackend *meshBackendCast(MeshBackendBase *meshBackendBaseInstance); +} + +#ifndef ESP8266WIFIMESH_DISABLE_COMPATIBILITY +using namespace MeshTypeConversionFunctions; // Required to retain backwards compatibility. TODO: Remove in core release 3.0.0 +#endif #endif diff --git a/libraries/ESP8266WiFiMesh/src/UtilityFunctions.cpp b/libraries/ESP8266WiFiMesh/src/UtilityFunctions.cpp new file mode 100644 index 0000000000..3ef2f8e6c6 --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/UtilityFunctions.cpp @@ -0,0 +1,62 @@ +/* + * UtilityFunctions + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#include "UtilityFunctions.h" +#include +#include + +namespace MeshUtilityFunctions +{ + bool macEqual(const uint8_t *macOne, const uint8_t *macTwo) + { + for(int i = 0; i <= 5; ++i) + { + if(macOne[i] != macTwo[i]) + { + return false; + } + } + + return true; + } + + uint64_t randomUint64() + { + return (((uint64_t)ESP.random() << 32) | (uint64_t)ESP.random()); + } + + template + T *getMapValue(std::map &mapIn, const uint64_t keyIn) + { + typename std::map::iterator mapIterator = mapIn.find(keyIn); + + if(mapIterator != mapIn.end()) + { + return &mapIterator->second; + } + + return nullptr; + } +} diff --git a/libraries/ESP8266WiFiMesh/src/UtilityFunctions.h b/libraries/ESP8266WiFiMesh/src/UtilityFunctions.h new file mode 100644 index 0000000000..b68947d3af --- /dev/null +++ b/libraries/ESP8266WiFiMesh/src/UtilityFunctions.h @@ -0,0 +1,42 @@ +/* + * UtilityFunctions + * Copyright (C) 2019 Anders Löfgren + * + * License (MIT license): + * + * 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. + * + * 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. + */ + +#ifndef __UTILITYFUNCTIONS_H__ +#define __UTILITYFUNCTIONS_H__ + +#include +#include + +namespace MeshUtilityFunctions +{ + bool macEqual(const uint8_t *macOne, const uint8_t *macTwo); + + uint64_t randomUint64(); + + template + T *getMapValue(std::map &mapIn, const uint64_t keyIn); +} + +#endif diff --git a/libraries/ESP8266WiFiMesh/src/UtilityMethods.cpp b/libraries/ESP8266WiFiMesh/src/UtilityMethods.cpp deleted file mode 100644 index 795eacb638..0000000000 --- a/libraries/ESP8266WiFiMesh/src/UtilityMethods.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * TransmissionResult - * Copyright (C) 2018 Anders Löfgren - * - * License (MIT license): - * - * 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. - * - * 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. - */ - -#include "TypeConversionFunctions.h" -#include "ESP8266WiFiMesh.h" - -void ESP8266WiFiMesh::verboseModePrint(const String &stringToPrint, bool newline) -{ - if(_verboseMode) - { - if(newline) - Serial.println(stringToPrint); - else - Serial.print(stringToPrint); - } -} - -/** - * Calculate the current lwIP version number and store the numbers in the _lwipVersion array. - * lwIP version can be changed in the "Tools" menu of Arduino IDE. - */ -void ESP8266WiFiMesh::storeLwipVersion() -{ - // ESP.getFullVersion() looks something like: - // SDK:2.2.1(cfd48f3)/Core:win-2.5.0-dev/lwIP:2.0.3(STABLE-2_0_3_RELEASE/glue:arduino-2.4.1-10-g0c0d8c2)/BearSSL:94e9704 - String fullVersion = ESP.getFullVersion(); - - int i = fullVersion.indexOf("lwIP:") + 5; - char currentChar = fullVersion.charAt(i); - - for(int versionPart = 0; versionPart < 3; versionPart++) - { - while(!isdigit(currentChar)) - { - currentChar = fullVersion.charAt(++i); - } - while(isdigit(currentChar)) - { - _lwipVersion[versionPart] = 10 * _lwipVersion[versionPart] + (currentChar - '0'); // Left shift and add digit value, in base 10. - currentChar = fullVersion.charAt(++i); - } - } -} - -/** - * Check if the code is running on a version of lwIP that is at least minLwipVersion. - */ -bool ESP8266WiFiMesh::atLeastLwipVersion(const uint32_t minLwipVersion[3]) -{ - for(int versionPart = 0; versionPart < 3; versionPart++) - { - if(_lwipVersion[versionPart] > minLwipVersion[versionPart]) - return true; - else if(_lwipVersion[versionPart] < minLwipVersion[versionPart]) - return false; - } - - return true; -} diff --git a/libraries/ESP8266httpUpdate/examples/httpUpdate/httpUpdate.ino b/libraries/ESP8266httpUpdate/examples/httpUpdate/httpUpdate.ino index 19c555a235..b9da7f8cb4 100644 --- a/libraries/ESP8266httpUpdate/examples/httpUpdate/httpUpdate.ino +++ b/libraries/ESP8266httpUpdate/examples/httpUpdate/httpUpdate.ino @@ -15,7 +15,7 @@ #ifndef APSSID #define APSSID "APSSID" -#define APPSK "APPSK" +#define APPSK "APPSK" #endif ESP8266WiFiMulti WiFiMulti; @@ -23,22 +23,14 @@ 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); - - } void update_started() { @@ -80,21 +72,14 @@ void loop() { t_httpUpdate_return ret = ESPhttpUpdate.update(client, "http://server/file.bin"); // Or: - //t_httpUpdate_return ret = ESPhttpUpdate.update(client, "server", 80, "file.bin"); + // t_httpUpdate_return ret = ESPhttpUpdate.update(client, "server", 80, "file.bin"); switch (ret) { - case HTTP_UPDATE_FAILED: - Serial.printf("HTTP_UPDATE_FAILD Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); - break; + case HTTP_UPDATE_FAILED: Serial.printf("HTTP_UPDATE_FAILD Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); break; - case HTTP_UPDATE_NO_UPDATES: - Serial.println("HTTP_UPDATE_NO_UPDATES"); - break; + case HTTP_UPDATE_NO_UPDATES: Serial.println("HTTP_UPDATE_NO_UPDATES"); break; - case HTTP_UPDATE_OK: - Serial.println("HTTP_UPDATE_OK"); - break; + case HTTP_UPDATE_OK: Serial.println("HTTP_UPDATE_OK"); break; } } } - diff --git a/libraries/ESP8266httpUpdate/examples/httpUpdateLittleFS/httpUpdateLittleFS.ino b/libraries/ESP8266httpUpdate/examples/httpUpdateLittleFS/httpUpdateLittleFS.ino index 6541d047fd..1a4c9ff1b4 100644 --- a/libraries/ESP8266httpUpdate/examples/httpUpdateLittleFS/httpUpdateLittleFS.ino +++ b/libraries/ESP8266httpUpdate/examples/httpUpdateLittleFS/httpUpdateLittleFS.ino @@ -17,7 +17,7 @@ ESP8266WiFiMulti WiFiMulti; #ifndef APSSID #define APSSID "APSSID" -#define APPSK "APPSK" +#define APPSK "APPSK" #endif void setup() { @@ -37,7 +37,6 @@ void setup() { WiFi.mode(WIFI_STA); WiFiMulti.addAP(APSSID, APPSK); - } void loop() { @@ -62,19 +61,12 @@ void loop() { ret = ESPhttpUpdate.update(client, "http://server/file.bin"); switch (ret) { - case HTTP_UPDATE_FAILED: - Serial.printf("HTTP_UPDATE_FAILED Error (%d): %s", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); - break; + case HTTP_UPDATE_FAILED: Serial.printf("HTTP_UPDATE_FAILED Error (%d): %s", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); break; - case HTTP_UPDATE_NO_UPDATES: - Serial.println("HTTP_UPDATE_NO_UPDATES"); - break; + case HTTP_UPDATE_NO_UPDATES: Serial.println("HTTP_UPDATE_NO_UPDATES"); break; - case HTTP_UPDATE_OK: - Serial.println("HTTP_UPDATE_OK"); - break; + case HTTP_UPDATE_OK: Serial.println("HTTP_UPDATE_OK"); break; } } } } - diff --git a/libraries/ESP8266httpUpdate/examples/httpUpdateSecure/httpUpdateSecure.ino b/libraries/ESP8266httpUpdate/examples/httpUpdateSecure/httpUpdateSecure.ino index beb613897c..f7896a22f6 100644 --- a/libraries/ESP8266httpUpdate/examples/httpUpdateSecure/httpUpdateSecure.ino +++ b/libraries/ESP8266httpUpdate/examples/httpUpdateSecure/httpUpdateSecure.ino @@ -18,7 +18,7 @@ #ifndef APSSID #define APSSID "APSSID" -#define APPSK "APPSK" +#define APPSK "APPSK" #endif ESP8266WiFiMulti WiFiMulti; @@ -74,7 +74,7 @@ void setup() { Serial.println(numCerts); if (numCerts == 0) { Serial.println(F("No certs found. Did you run certs-from-mozill.py and upload the LittleFS directory before running?")); - return; // Can't connect to anything w/o certs! + return; // Can't connect to anything w/o certs! } } @@ -87,9 +87,7 @@ void loop() { BearSSL::WiFiClientSecure client; bool mfln = client.probeMaxFragmentLength("server", 443, 1024); // server must be the same as in ESPhttpUpdate.update() Serial.printf("MFLN supported: %s\n", mfln ? "yes" : "no"); - if (mfln) { - client.setBufferSizes(1024, 1024); - } + if (mfln) { client.setBufferSizes(1024, 1024); } client.setCertStore(&certStore); // The line below is optional. It can be used to blink the LED on the board during flashing @@ -102,21 +100,15 @@ void loop() { t_httpUpdate_return ret = ESPhttpUpdate.update(client, "https://server/file.bin"); // Or: - //t_httpUpdate_return ret = ESPhttpUpdate.update(client, "server", 443, "file.bin"); + // t_httpUpdate_return ret = ESPhttpUpdate.update(client, "server", 443, "file.bin"); switch (ret) { - case HTTP_UPDATE_FAILED: - Serial.printf("HTTP_UPDATE_FAILED Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); - break; + case HTTP_UPDATE_FAILED: Serial.printf("HTTP_UPDATE_FAILED Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); break; - case HTTP_UPDATE_NO_UPDATES: - Serial.println("HTTP_UPDATE_NO_UPDATES"); - break; + case HTTP_UPDATE_NO_UPDATES: Serial.println("HTTP_UPDATE_NO_UPDATES"); break; - case HTTP_UPDATE_OK: - Serial.println("HTTP_UPDATE_OK"); - break; + case HTTP_UPDATE_OK: Serial.println("HTTP_UPDATE_OK"); break; } } } diff --git a/libraries/ESP8266httpUpdate/examples/httpUpdateSigned/httpUpdateSigned.ino b/libraries/ESP8266httpUpdate/examples/httpUpdateSigned/httpUpdateSigned.ino index e196ef6419..285d077f05 100644 --- a/libraries/ESP8266httpUpdate/examples/httpUpdateSigned/httpUpdateSigned.ino +++ b/libraries/ESP8266httpUpdate/examples/httpUpdateSigned/httpUpdateSigned.ino @@ -23,7 +23,7 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif ESP8266WiFiMulti WiFiMulti; @@ -73,11 +73,11 @@ void setup() { WiFi.mode(WIFI_STA); WiFiMulti.addAP(STASSID, STAPSK); - #if MANUAL_SIGNING +#if MANUAL_SIGNING signPubKey = new BearSSL::PublicKey(pubkey); hash = new BearSSL::HashSHA256(); sign = new BearSSL::SigningVerifier(signPubKey); - #endif +#endif } @@ -87,10 +87,10 @@ void loop() { WiFiClient client; - #if MANUAL_SIGNING +#if MANUAL_SIGNING // Ensure all updates are signed appropriately. W/o this call, all will be accepted. Update.installSignature(hash, sign); - #endif +#endif // If the key files are present in the build directory, signing will be // enabled using them automatically @@ -99,19 +99,12 @@ void loop() { t_httpUpdate_return ret = ESPhttpUpdate.update(client, "http://192.168.1.8/esp8266.bin"); switch (ret) { - case HTTP_UPDATE_FAILED: - Serial.printf("HTTP_UPDATE_FAILED Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); - break; + case HTTP_UPDATE_FAILED: Serial.printf("HTTP_UPDATE_FAILED Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); break; - case HTTP_UPDATE_NO_UPDATES: - Serial.println("HTTP_UPDATE_NO_UPDATES"); - break; + case HTTP_UPDATE_NO_UPDATES: Serial.println("HTTP_UPDATE_NO_UPDATES"); break; - case HTTP_UPDATE_OK: - Serial.println("HTTP_UPDATE_OK"); - break; + case HTTP_UPDATE_OK: Serial.println("HTTP_UPDATE_OK"); break; } } delay(10000); } - diff --git a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp index 03345054bd..af45852e5f 100755 --- a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp +++ b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.cpp @@ -25,9 +25,7 @@ #include "ESP8266httpUpdate.h" #include - -extern "C" uint32_t _FS_start; -extern "C" uint32_t _FS_end; +#include ESP8266HTTPUpdate::ESP8266HTTPUpdate(void) : _httpClientTimeout(8000) @@ -63,50 +61,6 @@ void ESP8266HTTPUpdate::setAuthorization(const String &auth) _auth = auth; } -#if HTTPUPDATE_1_2_COMPATIBLE -HTTPUpdateResult ESP8266HTTPUpdate::update(const String& url, const String& currentVersion, - const String& httpsFingerprint, bool reboot) -{ - rebootOnUpdate(reboot); -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - return update(url, currentVersion, httpsFingerprint); -#pragma GCC diagnostic pop -} - -HTTPUpdateResult ESP8266HTTPUpdate::update(const String& url, const String& currentVersion) -{ - HTTPClient http; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - http.begin(url); -#pragma GCC diagnostic pop - return handleUpdate(http, currentVersion, false); -} - -HTTPUpdateResult ESP8266HTTPUpdate::update(const String& url, const String& currentVersion, - const String& httpsFingerprint) -{ - HTTPClient http; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - http.begin(url, httpsFingerprint); -#pragma GCC diagnostic pop - return handleUpdate(http, currentVersion, false); -} - -HTTPUpdateResult ESP8266HTTPUpdate::update(const String& url, const String& currentVersion, - const uint8_t httpsFingerprint[20]) -{ - HTTPClient http; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - http.begin(url, httpsFingerprint); -#pragma GCC diagnostic pop - return handleUpdate(http, currentVersion, false); -} -#endif - HTTPUpdateResult ESP8266HTTPUpdate::update(WiFiClient& client, const String& url, const String& currentVersion) { HTTPClient http; @@ -114,38 +68,6 @@ HTTPUpdateResult ESP8266HTTPUpdate::update(WiFiClient& client, const String& url return handleUpdate(http, currentVersion, false); } -#if HTTPUPDATE_1_2_COMPATIBLE -HTTPUpdateResult ESP8266HTTPUpdate::updateSpiffs(const String& url, const String& currentVersion, const String& httpsFingerprint) -{ - HTTPClient http; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - http.begin(url, httpsFingerprint); -#pragma GCC diagnostic pop - return handleUpdate(http, currentVersion, true); -} - -HTTPUpdateResult ESP8266HTTPUpdate::updateSpiffs(const String& url, const String& currentVersion, const uint8_t httpsFingerprint[20]) -{ - HTTPClient http; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - http.begin(url, httpsFingerprint); -#pragma GCC diagnostic pop - return handleUpdate(http, currentVersion, true); -} - -HTTPUpdateResult ESP8266HTTPUpdate::updateSpiffs(const String& url, const String& currentVersion) -{ - HTTPClient http; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - http.begin(url); -#pragma GCC diagnostic pop - return handleUpdate(http, currentVersion, true); -} -#endif - HTTPUpdateResult ESP8266HTTPUpdate::updateFS(WiFiClient& client, const String& url, const String& currentVersion) { HTTPClient http; @@ -153,62 +75,22 @@ HTTPUpdateResult ESP8266HTTPUpdate::updateFS(WiFiClient& client, const String& u return handleUpdate(http, currentVersion, true); } -#if HTTPUPDATE_1_2_COMPATIBLE -HTTPUpdateResult ESP8266HTTPUpdate::update(const String& host, uint16_t port, const String& uri, const String& currentVersion, - bool https, const String& httpsFingerprint, bool reboot) -{ - (void)https; - rebootOnUpdate(reboot); - if (httpsFingerprint.length() == 0) { -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - return update(host, port, uri, currentVersion); - } else { - return update(host, port, uri, currentVersion, httpsFingerprint); -#pragma GCC diagnostic pop - } -} - -HTTPUpdateResult ESP8266HTTPUpdate::update(const String& host, uint16_t port, const String& uri, +HTTPUpdateResult ESP8266HTTPUpdate::update(WiFiClient& client, const String& host, uint16_t port, const String& uri, const String& currentVersion) { HTTPClient http; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - http.begin(host, port, uri); -#pragma GCC diagnostic pop - return handleUpdate(http, currentVersion, false); -} - -HTTPUpdateResult ESP8266HTTPUpdate::update(const String& host, uint16_t port, const String& url, - const String& currentVersion, const String& httpsFingerprint) -{ - HTTPClient http; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - http.begin(host, port, url, httpsFingerprint); -#pragma GCC diagnostic pop + http.begin(client, host, port, uri); return handleUpdate(http, currentVersion, false); } -HTTPUpdateResult ESP8266HTTPUpdate::update(const String& host, uint16_t port, const String& url, - const String& currentVersion, const uint8_t httpsFingerprint[20]) +HTTPUpdateResult ESP8266HTTPUpdate::update(HTTPClient& httpClient, const String& currentVersion) { - HTTPClient http; -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - http.begin(host, port, url, httpsFingerprint); -#pragma GCC diagnostic pop - return handleUpdate(http, currentVersion, false); + return handleUpdate(httpClient, currentVersion, false); } -#endif -HTTPUpdateResult ESP8266HTTPUpdate::update(WiFiClient& client, const String& host, uint16_t port, const String& uri, - const String& currentVersion) +HTTPUpdateResult ESP8266HTTPUpdate::updateFS(HTTPClient& httpClient, const String& currentVersion) { - HTTPClient http; - http.begin(client, host, port, uri); - return handleUpdate(http, currentVersion, false); + return handleUpdate(httpClient, currentVersion, true); } /** @@ -336,14 +218,24 @@ 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()); + } - if(http.hasHeader("x-MD5")) { - DEBUG_HTTP_UPDATE("[httpUpdate] - MD5: %s\n", http.header("x-MD5").c_str()); + String md5; + if (_md5Sum.length()) { + md5 = _md5Sum; + } else if(http.hasHeader("x-MD5")) { + md5 = http.header("x-MD5"); + } + if(md5.length()) { + DEBUG_HTTP_UPDATE("[httpUpdate] - MD5: %s\n", md5.c_str()); } 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() ); @@ -354,7 +246,7 @@ HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String& if(len > 0) { bool startUpdate = true; if(spiffs) { - size_t spiffsSize = ((size_t) &_FS_end - (size_t) &_FS_start); + size_t spiffsSize = ((size_t)FS_end - (size_t)FS_start); if(len > (int) spiffsSize) { DEBUG_HTTP_UPDATE("[httpUpdate] spiffsSize to low (%d) needed: %d\n", spiffsSize, len); startUpdate = false; @@ -376,6 +268,12 @@ HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String& } WiFiClient * tcp = http.getStreamPtr(); + if (!tcp) { + DEBUG_HTTP_UPDATE("[httpUpdate] WiFiClient connection unexpectedly absent\n"); + _setLastError(HTTPC_ERROR_CONNECTION_LOST); + http.end(); + return HTTP_UPDATE_FAILED; + } if (_closeConnectionsOnUpdate) { WiFiUDP::stopAll(); @@ -412,6 +310,9 @@ HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String& } +// it makes no sense to check flash size in auto flash mode +// (sketch size would have to be set in bin header, instead of flash size) +#if !FLASH_MAP_SUPPORT if (buf[0] == 0xe9) { uint32_t bin_flash_size = ESP.magicFlashChipSize((buf[3] & 0xf0) >> 4); @@ -423,8 +324,9 @@ HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String& return HTTP_UPDATE_FAILED; } } +#endif } - if(runUpdate(*tcp, len, http.header("x-MD5"), command)) { + if(runUpdate(*tcp, len, md5, command)) { ret = HTTP_UPDATE_OK; DEBUG_HTTP_UPDATE("[httpUpdate] Update ok\n"); http.end(); @@ -539,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 88216070d7..28e90bad23 100755 --- a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h +++ b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h @@ -32,10 +32,6 @@ #include #include -#ifndef HTTPUPDATE_1_2_COMPATIBLE -#define HTTPUPDATE_1_2_COMPATIBLE HTTPCLIENT_1_1_COMPATIBLE -#endif - #ifdef DEBUG_ESP_HTTP_UPDATE #ifdef DEBUG_ESP_PORT #define DEBUG_HTTP_UPDATE(fmt, ...) DEBUG_ESP_PORT.printf_P( (PGM_P)PSTR(fmt), ## __VA_ARGS__ ) @@ -47,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, @@ -93,7 +91,7 @@ class ESP8266HTTPUpdate _followRedirects = follow ? HTTPC_STRICT_FOLLOW_REDIRECTS : HTTPC_DISABLE_FOLLOW_REDIRECTS; } /** - * set redirect follow mode. See `followRedirects_t` enum for avaliable modes. + * set redirect follow mode. See `followRedirects_t` enum for available modes. * @param follow */ void setFollowRedirects(followRedirects_t follow) @@ -112,48 +110,22 @@ class ESP8266HTTPUpdate _ledOn = ledOn; } + void setMD5sum(const String &md5Sum) + { + _md5Sum = md5Sum; + } + void setAuthorization(const String& user, const String& password); void setAuthorization(const String& auth); -#if HTTPUPDATE_1_2_COMPATIBLE - // This function is deprecated, use rebootOnUpdate and the next one instead - t_httpUpdate_return update(const String& url, const String& currentVersion, - const String& httpsFingerprint, bool reboot) __attribute__((deprecated)); - t_httpUpdate_return update(const String& url, const String& currentVersion = "") __attribute__((deprecated)); - t_httpUpdate_return update(const String& url, const String& currentVersion, - const String& httpsFingerprint) __attribute__((deprecated)); - t_httpUpdate_return update(const String& url, const String& currentVersion, - const uint8_t httpsFingerprint[20]) __attribute__((deprecated)); // BearSSL -#endif t_httpUpdate_return update(WiFiClient& client, const String& url, const String& currentVersion = ""); - -#if HTTPUPDATE_1_2_COMPATIBLE - // This function is deprecated, use one of the overloads below along with rebootOnUpdate - t_httpUpdate_return update(const String& host, uint16_t port, const String& uri, const String& currentVersion, - bool https, const String& httpsFingerprint, bool reboot) __attribute__((deprecated)); - - t_httpUpdate_return update(const String& host, uint16_t port, const String& uri = "/", - const String& currentVersion = "") __attribute__((deprecated)); - t_httpUpdate_return update(const String& host, uint16_t port, const String& url, - const String& currentVersion, const String& httpsFingerprint) __attribute__((deprecated)); - t_httpUpdate_return update(const String& host, uint16_t port, const String& url, - const String& currentVersion, const uint8_t httpsFingerprint[20]) __attribute__((deprecated)); // BearSSL -#endif t_httpUpdate_return update(WiFiClient& client, const String& host, uint16_t port, const String& uri = "/", const String& currentVersion = ""); - -#if HTTPUPDATE_1_2_COMPATIBLE - // This function is deprecated, use rebootOnUpdate and the next one instead - t_httpUpdate_return updateSpiffs(const String& url, const String& currentVersion, - const String& httpsFingerprint, bool reboot) __attribute__((deprecated)); - t_httpUpdate_return updateSpiffs(const String& url, const String& currentVersion = "") __attribute__((deprecated)); - t_httpUpdate_return updateSpiffs(const String& url, const String& currentVersion, const String& httpsFingerprint) __attribute__((deprecated)); - t_httpUpdate_return updateSpiffs(const String& url, const String& currentVersion, const uint8_t httpsFingerprint[20]) __attribute__((deprecated)); // BearSSL -#endif t_httpUpdate_return updateFS(WiFiClient& client, const String& url, const String& currentVersion = ""); - t_httpUpdate_return updateSpiffs(WiFiClient& client, const String& url, const String& currentVersion = "") __attribute__((deprecated)) { - return updateFS(client, url, 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; } @@ -164,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); @@ -181,11 +156,10 @@ class ESP8266HTTPUpdate String _user; String _password; String _auth; - -private: + String _md5Sum; int _httpClientTimeout; followRedirects_t _followRedirects = HTTPC_DISABLE_FOLLOW_REDIRECTS; - +private: // Callbacks HTTPUpdateStartCB _cbStart; HTTPUpdateEndCB _cbEnd; diff --git a/libraries/ESP8266mDNS/examples/LEAmDNS/mDNS_Clock/mDNS_Clock.ino b/libraries/ESP8266mDNS/examples/LEAmDNS/mDNS_Clock/mDNS_Clock.ino index ec0320fdb2..6234e9f109 100644 --- a/libraries/ESP8266mDNS/examples/LEAmDNS/mDNS_Clock/mDNS_Clock.ino +++ b/libraries/ESP8266mDNS/examples/LEAmDNS/mDNS_Clock/mDNS_Clock.ino @@ -3,7 +3,7 @@ This example demonstrates two features of the LEA MDNSResponder: 1. The host and service domain negotiation process that ensures - the uniqueness of the finally choosen host and service domain name. + 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 @@ -11,7 +11,7 @@ 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 negociated. Keep an + 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 @@ -43,39 +43,38 @@ Global defines and vars */ -#define TIMEZONE_OFFSET 1 // CET -#define DST_OFFSET 1 // CEST -#define UPDATE_CYCLE (1 * 1000) // every second +#define TIMEZONE_OFFSET 1 // CET +#define DST_OFFSET 1 // CEST +#define UPDATE_CYCLE (1 * 1000) // every second -#define SERVICE_PORT 80 // HTTP port +#define SERVICE_PORT 80 // HTTP port #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif -const char* ssid = STASSID; -const char* password = STAPSK; +const char* ssid = STASSID; +const char* password = STAPSK; -char* pcHostDomain = 0; // Negociated 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 +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); +ESP8266WebServer server(SERVICE_PORT); /* getTimeString */ const char* getTimeString(void) { - static char acTimeString[32]; + 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... + size_t stLength; + while (((stLength = strlen(acTimeString))) && ('\n' == acTimeString[stLength - 1])) { + acTimeString[stLength - 1] = 0; // Remove trailing line break... } return acTimeString; } @@ -90,7 +89,7 @@ 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 uninitalized starts with (8 * 3600 = 28800) + 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("."); @@ -186,7 +185,8 @@ void handleHTTPRequest() { Serial.println("HTTP Request"); // Get current time - time_t now = time(nullptr);; + time_t now = time(nullptr); + ; struct tm timeinfo; gmtime_r(&now, &timeinfo); @@ -231,10 +231,9 @@ void setup(void) { // Setup MDNS responder MDNS.setHostProbeResultCallback(hostProbeResult); // Init the (currently empty) host domain string with 'esp8266' - if ((!MDNSResponder::indexDomain(pcHostDomain, 0, "esp8266")) || - (!MDNS.begin(pcHostDomain))) { + if ((!MDNSResponder::indexDomain(pcHostDomain, 0, "esp8266")) || (!MDNS.begin(pcHostDomain))) { Serial.println("Error setting up MDNS responder!"); - while (1) { // STOP + while (1) { // STOP delay(1000); } } diff --git a/libraries/ESP8266mDNS/examples/LEAmDNS/mDNS_ServiceMonitor/mDNS_ServiceMonitor.ino b/libraries/ESP8266mDNS/examples/LEAmDNS/mDNS_ServiceMonitor/mDNS_ServiceMonitor.ino index 167a968c71..1773b590b0 100644 --- a/libraries/ESP8266mDNS/examples/LEAmDNS/mDNS_ServiceMonitor/mDNS_ServiceMonitor.ino +++ b/libraries/ESP8266mDNS/examples/LEAmDNS/mDNS_ServiceMonitor/mDNS_ServiceMonitor.ino @@ -3,7 +3,7 @@ This example demonstrates two features of the LEA MDNSResponder: 1. The host and service domain negotiation process that ensures - the uniqueness of the finally choosen host and service domain name. + the uniqueness of the finally chosen host and service domain name. 2. The dynamic MDNS service lookup/query feature. A list of 'HTTP' services in the local network is created and kept up to date. @@ -11,7 +11,7 @@ and announced as a service. The ESP itself is initially announced to clients as 'esp8266.local', if this host domain - is already used in the local network, another host domain is negociated. Keep an + 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 HTTP service. The service itself is is announced as 'host domain'._http._tcp.local. The HTTP server delivers a short greeting and the current list of other 'HTTP' services (not updated). @@ -39,26 +39,26 @@ Global defines and vars */ -#define SERVICE_PORT 80 // HTTP port +#define SERVICE_PORT 80 // HTTP port #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif -const char* ssid = STASSID; -const char* password = STAPSK; +const char* ssid = STASSID; +const char* password = STAPSK; -char* pcHostDomain = 0; // Negociated host domain -bool bHostDomainConfirmed = false; // Flags the confirmation of the host domain -MDNSResponder::hMDNSService hMDNSService = 0; // The handle of the http service in the MDNS responder -MDNSResponder::hMDNSServiceQuery hMDNSServiceQuery = 0; // The handle of the 'http.tcp' service query in the MDNS responder +char* pcHostDomain = 0; // Negotiated host domain +bool bHostDomainConfirmed = false; // Flags the confirmation of the host domain +MDNSResponder::hMDNSService hMDNSService = 0; // The handle of the http service in the MDNS responder +MDNSResponder::hMDNSServiceQuery hMDNSServiceQuery = 0; // The handle of the 'http.tcp' service query in the MDNS responder -const String cstrNoHTTPServices = "Currently no 'http.tcp' services in the local network!
"; -String strHTTPServices = cstrNoHTTPServices; +const String cstrNoHTTPServices = "Currently no 'http.tcp' services in the local network!
"; +String strHTTPServices = cstrNoHTTPServices; // HTTP server at port 'SERVICE_PORT' will respond to HTTP requests -ESP8266WebServer server(SERVICE_PORT); +ESP8266WebServer server(SERVICE_PORT); /* @@ -80,26 +80,17 @@ bool setStationHostname(const char* p_pcHostname) { void MDNSServiceQueryCallback(MDNSResponder::MDNSServiceInfo serviceInfo, MDNSResponder::AnswerType answerType, bool p_bSetContent) { String answerInfo; switch (answerType) { - case MDNSResponder::AnswerType::ServiceDomain : - answerInfo = "ServiceDomain " + String(serviceInfo.serviceDomain()); - break; - case MDNSResponder::AnswerType::HostDomainAndPort : - answerInfo = "HostDomainAndPort " + String(serviceInfo.hostDomain()) + ":" + String(serviceInfo.hostPort()); - break; - case MDNSResponder::AnswerType::IP4Address : + case MDNSResponder::AnswerType::ServiceDomain: answerInfo = "ServiceDomain " + String(serviceInfo.serviceDomain()); break; + case MDNSResponder::AnswerType::HostDomainAndPort: answerInfo = "HostDomainAndPort " + String(serviceInfo.hostDomain()) + ":" + String(serviceInfo.hostPort()); break; + case MDNSResponder::AnswerType::IP4Address: answerInfo = "IP4Address "; - for (IPAddress ip : serviceInfo.IP4Adresses()) { - answerInfo += "- " + ip.toString(); - }; + for (IPAddress ip : serviceInfo.IP4Adresses()) { answerInfo += "- " + ip.toString(); }; break; - case MDNSResponder::AnswerType::Txt : + case MDNSResponder::AnswerType::Txt: answerInfo = "TXT " + String(serviceInfo.strKeyValue()); - for (auto kv : serviceInfo.keyValues()) { - answerInfo += "\nkv : " + String(kv.first) + " : " + String(kv.second); - } + for (auto kv : serviceInfo.keyValues()) { answerInfo += "\nkv : " + String(kv.first) + " : " + String(kv.second); } break; - default : - answerInfo = "Unknown Answertype"; + default: answerInfo = "Unknown Answertype"; } Serial.printf("Answer %s %s\n", answerInfo.c_str(), p_bSetContent ? "Modified" : "Deleted"); } @@ -109,10 +100,8 @@ void MDNSServiceQueryCallback(MDNSResponder::MDNSServiceInfo serviceInfo, MDNSRe Probe result callback for Services */ -void serviceProbeResult(String p_pcServiceName, - const MDNSResponder::hMDNSService p_hMDNSService, - bool p_bProbeResult) { - (void) p_hMDNSService; +void serviceProbeResult(String p_pcServiceName, const MDNSResponder::hMDNSService p_hMDNSService, bool p_bProbeResult) { + (void)p_hMDNSService; Serial.printf("MDNSServiceProbeResultCallback: Service %s probe %s\n", p_pcServiceName.c_str(), (p_bProbeResult ? "succeeded." : "failed!")); } @@ -186,7 +175,7 @@ void handleHTTPRequest() { s += WiFi.hostname() + ".local at " + WiFi.localIP().toString() + ""; s += "

Local HTTP services are :

"; s += "
    "; - for (auto info : MDNS.answerInfo(hMDNSServiceQuery)) { + for (auto info : MDNS.answerInfo(hMDNSServiceQuery)) { s += "
  1. "; s += info.serviceDomain(); if (info.hostDomainAvailable()) { @@ -196,15 +185,11 @@ void handleHTTPRequest() { } if (info.IP4AddressAvailable()) { s += "
    IP4:"; - for (auto ip : info.IP4Adresses()) { - s += " " + ip.toString(); - } + for (auto ip : info.IP4Adresses()) { s += " " + ip.toString(); } } if (info.txtAvailable()) { s += "
    TXT:
    "; - for (auto kv : info.keyValues()) { - s += "\t" + String(kv.first) + " : " + String(kv.second) + "
    "; - } + for (auto kv : info.keyValues()) { s += "\t" + String(kv.first) + " : " + String(kv.second) + "
    "; } } s += "
  2. "; } @@ -245,10 +230,9 @@ void setup(void) { MDNS.setHostProbeResultCallback(hostProbeResult); // Init the (currently empty) host domain string with 'esp8266' - if ((!MDNSResponder::indexDomain(pcHostDomain, 0, "esp8266")) || - (!MDNS.begin(pcHostDomain))) { + if ((!MDNSResponder::indexDomain(pcHostDomain, 0, "esp8266")) || (!MDNS.begin(pcHostDomain))) { Serial.println(" Error setting up MDNS responder!"); - while (1) { // STOP + while (1) { // STOP delay(1000); } } @@ -265,6 +249,3 @@ void loop(void) { // Allow MDNS processing MDNS.update(); } - - - 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 5e4a5a0810..78d4aa6c30 100644 --- a/libraries/ESP8266mDNS/examples/OTA-mDNS-LittleFS/OTA-mDNS-LittleFS.ino +++ b/libraries/ESP8266mDNS/examples/OTA-mDNS-LittleFS/OTA-mDNS-LittleFS.ino @@ -14,12 +14,12 @@ #ifndef APSSID #define APSSID "your-apssid" -#define APPSK "your-password" +#define APPSK "your-password" #endif #ifndef STASSID #define STASSID "your-sta" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif // includes @@ -35,19 +35,19 @@ @brief mDNS and OTA Constants @{ */ -#define HOSTNAME "ESP8266-OTA-" ///< Hostname. The setup function adds the Chip ID at the end. +#define HOSTNAME "ESP8266-OTA-" ///< Hostname. The setup function adds the Chip ID at the end. /// @} /** @brief Default WiFi connection information. @{ */ -const char* ap_default_ssid = APSSID; ///< Default SSID. -const char* ap_default_psk = APPSK; ///< Default PSK. +const char *ap_default_ssid = APSSID; ///< Default SSID. +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. @@ -55,9 +55,9 @@ const char* ap_default_psk = APPSK; ///< Default PSK. @param pass String pointer for storing PSK. @return True or False. - The config file have to containt the WiFi SSID in the first line + The config file has to contain the WiFi SSID in the first line and the WiFi PSK in the second line. - Line seperator can be \r\n (CR LF) \r or \n. + Line separator can be \r\n (CR LF) \r or \n. */ bool loadConfig(String *ssid, String *pass) { // open file for reading. @@ -74,16 +74,14 @@ bool loadConfig(String *ssid, String *pass) { content.trim(); - // Check if ther is a second line available. + // Check if there is a second line available. int8_t pos = content.indexOf("\r\n"); uint8_t le = 2; // check for linux and mac line ending. if (pos == -1) { le = 1; pos = content.indexOf("\n"); - if (pos == -1) { - pos = content.indexOf("\r"); - } + if (pos == -1) { pos = content.indexOf("\r"); } } // If there is no second line: Some information is missing. @@ -110,7 +108,7 @@ bool loadConfig(String *ssid, String *pass) { #endif return true; -} // loadConfig +} // loadConfig /** @@ -135,7 +133,7 @@ bool saveConfig(String *ssid, String *pass) { configFile.close(); return true; -} // saveConfig +} // saveConfig /** @@ -160,7 +158,7 @@ void setup() { // Print hostname. Serial.println("Hostname: " + hostname); - //Serial.println(WiFi.hostname()); + // Serial.println(WiFi.hostname()); // Initialize file system. @@ -170,7 +168,7 @@ void setup() { } // Load wifi connection information. - if (! loadConfig(&station_ssid, &station_psk)) { + if (!loadConfig(&station_ssid, &station_psk)) { station_ssid = STASSID; station_psk = STAPSK; @@ -196,7 +194,7 @@ void setup() { Serial.println(WiFi.SSID()); // ... Uncomment this for debugging output. - //WiFi.printDiag(Serial); + // WiFi.printDiag(Serial); } else { // ... Begin with sdk config. WiFi.begin(); @@ -208,7 +206,7 @@ void setup() { unsigned long startTime = millis(); while (WiFi.status() != WL_CONNECTED && millis() - startTime < 10000) { Serial.write('.'); - //Serial.print(WiFi.status()); + // Serial.print(WiFi.status()); delay(500); } Serial.println(); @@ -245,4 +243,3 @@ void loop() { // Handle OTA server. ArduinoOTA.handle(); } - diff --git a/libraries/ESP8266mDNS/examples/mDNS-SD_Extended/mDNS-SD_Extended.ino b/libraries/ESP8266mDNS/examples/mDNS-SD_Extended/mDNS-SD_Extended.ino index fda2fe6562..20e1fa2146 100644 --- a/libraries/ESP8266mDNS/examples/mDNS-SD_Extended/mDNS-SD_Extended.ino +++ b/libraries/ESP8266mDNS/examples/mDNS-SD_Extended/mDNS-SD_Extended.ino @@ -14,12 +14,12 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif -const char* ssid = STASSID; +const char* ssid = STASSID; const char* password = STAPSK; -char hostString[16] = {0}; +char hostString[16] = { 0 }; void setup() { Serial.begin(115200); @@ -43,14 +43,12 @@ void setup() { Serial.print("IP address: "); Serial.println(WiFi.localIP()); - if (!MDNS.begin(hostString)) { - Serial.println("Error setting up MDNS responder!"); - } + if (!MDNS.begin(hostString)) { Serial.println("Error setting up MDNS responder!"); } Serial.println("mDNS responder started"); - MDNS.addService("esp", "tcp", 8080); // Announce esp tcp service on port 8080 + MDNS.addService("esp", "tcp", 8080); // Announce esp tcp service on port 8080 Serial.println("Sending mDNS query"); - int n = MDNS.queryService("esp", "tcp"); // Send out query for esp tcp services + int n = MDNS.queryService("esp", "tcp"); // Send out query for esp tcp services Serial.println("mDNS query done"); if (n == 0) { Serial.println("no services found"); diff --git a/libraries/ESP8266mDNS/examples/mDNS_Web_Server/mDNS_Web_Server.ino b/libraries/ESP8266mDNS/examples/mDNS_Web_Server/mDNS_Web_Server.ino index 7560864ce1..fe625e5432 100644 --- a/libraries/ESP8266mDNS/examples/mDNS_Web_Server/mDNS_Web_Server.ino +++ b/libraries/ESP8266mDNS/examples/mDNS_Web_Server/mDNS_Web_Server.ino @@ -22,7 +22,7 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif const char* ssid = STASSID; @@ -57,9 +57,7 @@ void setup(void) { // we send our IP address on the WiFi network if (!MDNS.begin("esp8266")) { Serial.println("Error setting up MDNS responder!"); - while (1) { - delay(1000); - } + while (1) { delay(1000); } } Serial.println("mDNS responder started"); @@ -76,17 +74,13 @@ void loop(void) { MDNS.update(); // Check if a client has connected - WiFiClient client = server.available(); - if (!client) { - return; - } + WiFiClient client = server.accept(); + if (!client) { return; } Serial.println(""); Serial.println("New client"); // Wait for data from client to become available - while (client.connected() && !client.available()) { - delay(1); - } + while (client.connected() && !client.available()) { delay(1); } // Read the first line of HTTP request String req = client.readStringUntil('\r'); @@ -121,4 +115,3 @@ void loop(void) { Serial.println("Done with client"); } - diff --git a/libraries/ESP8266mDNS/library.properties b/libraries/ESP8266mDNS/library.properties index 70b0f2ae68..6eed8345be 100644 --- a/libraries/ESP8266mDNS/library.properties +++ b/libraries/ESP8266mDNS/library.properties @@ -3,7 +3,7 @@ version=1.2 author=multiple, see files maintainer=LaborEtArs sentence=Creates a mDNS responder. -paragraph=Creates a mDNS responder to ensure host domain uniqueness in local networks and to allow for mDNS service discovery and announcment. +paragraph=Creates a mDNS responder to ensure host domain uniqueness in local networks and to allow for mDNS service discovery and announcement. category=Communication url=https://github.com/LaborEtArs/ESP8266mDNS architectures=esp8266 diff --git a/libraries/ESP8266mDNS/src/ESP8266mDNS.cpp b/libraries/ESP8266mDNS/src/ESP8266mDNS.cpp index b5d7aac277..821b7010af 100644 --- a/libraries/ESP8266mDNS/src/ESP8266mDNS.cpp +++ b/libraries/ESP8266mDNS/src/ESP8266mDNS.cpp @@ -30,4 +30,3 @@ #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS) MDNSResponder MDNS; #endif - diff --git a/libraries/ESP8266mDNS/src/ESP8266mDNS.h b/libraries/ESP8266mDNS/src/ESP8266mDNS.h index 7c1a1bd5d7..840577b6d2 100644 --- a/libraries/ESP8266mDNS/src/ESP8266mDNS.h +++ b/libraries/ESP8266mDNS/src/ESP8266mDNS.h @@ -2,28 +2,28 @@ ESP8266mDNS.h - mDNSResponder for ESP8266 family This file is part of the esp8266 core for Arduino environment. - Legacy_ESP8266mDNS: - The well known, thouroughly tested (yet no flawless) default mDNS library for the ESP8266 family - - LEA_ESP8266mDNS: - An (currently) experimental mDNS implementation, that supports a lot more of mDNS features than Legacy_ESP8266mDNS, like: - - Presenting a DNS-SD service to interested observers, eg. a http server by presenting _http._tcp service - - Support for multi-level compressed names in input; in output only a very simple one-leven full-name compression is implemented + mDNS implementation, that supports many mDNS features like: + - Presenting a DNS-SD service to interested observers, eg. a http server by presenting + _http._tcp service + - Support for multi-level compressed names in input; in output only a very simple one-leven + full-name compression is implemented - Probing host and service domains for uniqueness in the local network - - Tiebreaking while probing is supported in a very minimalistic way (the 'higher' IP address wins the tiebreak) + - Tiebreaking while probing is supported in a very minimalistic way (the 'higher' IP address + wins the tiebreak) - Announcing available services after successful probing - Using fixed service TXT items or - Using dynamic service TXT items for presented services (via callback) - Remove services (and un-announcing them to the observers by sending goodbye-messages) - - Static queries for DNS-SD services (creating a fixed answer set after a certain timeout period) + - Static queries for DNS-SD services (creating a fixed answer set after a certain timeout + period) - Dynamic queries for DNS-SD services with cached and updated answers and user notifications - Support for multi-homed client host domains - See 'LEA_ESP8266mDNS/EPS8266mDNS.h' for more implementation details and usage informations. - See 'examples/mDNS_Clock' and 'examples/mDNS_ServiceMonitor' for implementation examples of the advanced features. + See 'src/LEAmDNS.h' for implementation details, configuration and usage information. + See 'examples/LEAmDNS/' for examples of the new features. - LEA_ESP8266mDNS is (mostly) client source code compatible to 'Legacy_ESP8266mDNS', so it could be - use as a 'drop-in' replacement in existing projects. + LEAmDNS is expected to be compatible with the original ESP8266mDNS implementation, and it can be + used as a drop-in replacement in existing projects. This library is free software; you can redistribute it and/or @@ -45,13 +45,13 @@ #ifndef __ESP8266MDNS_H #define __ESP8266MDNS_H -#include "LEAmDNS.h" // LEA +#include "LEAmDNS.h" // LEA #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS) // Maps the implementation to use to the global namespace type -using MDNSResponder = esp8266::MDNSImplementation::MDNSResponder; // LEA +using MDNSResponder = esp8266::MDNSImplementation::MDNSResponder; // LEA extern MDNSResponder MDNS; #endif -#endif // __ESP8266MDNS_H +#endif // __ESP8266MDNS_H diff --git a/libraries/ESP8266mDNS/src/LEAmDNS.cpp b/libraries/ESP8266mDNS/src/LEAmDNS.cpp index 24e5799bb9..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,1344 +45,1341 @@ 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 -*/ - -/** - MDNSResponder::MDNSResponder -*/ -MDNSResponder::MDNSResponder(void) - : m_pServices(0), - m_pUDPContext(0), - m_pcHostname(0), - m_pServiceQueries(0), - m_fnServiceTxtCallback(0) -{ -} - -/* - MDNSResponder::~MDNSResponder -*/ -MDNSResponder::~MDNSResponder(void) -{ - - _resetProbeStatus(false); - _releaseServiceQueries(); - _releaseHostname(); - _releaseUDPContext(); - _releaseServices(); -} + /** + INTERFACE + */ -/* - MDNSResponder::begin - - Set the host domain (for probing) and install WiFi event handlers for - IP assignment and disconnection management. In both cases, the MDNS responder - is restarted (reset and restart probe status) - Finally the responder is (re)started - -*/ -bool MDNSResponder::begin(const char* p_pcHostname, const IPAddress& /*p_IPAddress*/, uint32_t /*p_u32TTL*/) -{ - bool bResult = false; - - if (_setHostname(p_pcHostname)) - { - bResult = _restart(); - } - - LwipIntf::stateUpCB - ( - [this](netif * intf) + /** + MDNSResponder::MDNSResponder + */ + MDNSResponder::MDNSResponder(void) : + m_pServices(0), m_pUDPContext(0), m_pcHostname(0), m_pServiceQueries(0), + m_fnServiceTxtCallback(0), m_bLwipCb(false), m_bRestarting(false) { - (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(); } - ); - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] begin: FAILED for '%s'!\n"), (p_pcHostname ? : "-")); - }); - - return bResult; -} - -/* - MDNSResponder::close - - Ends the MDNS responder. - Announced services are unannounced (by multicasting a goodbye message) - -*/ -bool MDNSResponder::close(void) -{ - bool bResult = false; - if (0 != m_pUDPContext) + /* + MDNSResponder::~MDNSResponder + */ + MDNSResponder::~MDNSResponder(void) { - _announce(false, true); - _resetProbeStatus(false); // Stop probing + _resetProbeStatus(false); _releaseServiceQueries(); - _releaseUDPContext(); _releaseHostname(); - - bResult = true; - } - else - { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] close: Ignoring call to close!\n"));); + _releaseUDPContext(); + _releaseServices(); } - return bResult; -} -/* - MDNSResponder::end + /* + MDNSResponder::begin - Ends the MDNS responder. - for compatibility with esp32 + Set the host domain (for probing) and install WiFi event handlers for + IP assignment and disconnection management. In both cases, the MDNS responder + is restarted (reset and restart probe status) + Finally the responder is (re)started -*/ + */ + bool MDNSResponder::begin(const char* p_pcHostname, const IPAddress& /*p_IPAddress*/, + uint32_t /*p_u32TTL*/) + { + bool bResult = false; -bool MDNSResponder::end(void) -{ - return close(); -} + if (_setHostname(p_pcHostname)) + { + bResult = _restart(); + } -/* - MDNSResponder::setHostname + 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; + } - Replaces the current hostname and restarts probing. - For services without own instance name (when the host name was used a instance - name), the instance names are replaced also (and the probing is restarted). + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] begin: FAILED for '%s'!\n"), + (p_pcHostname ?: "-")); + }); -*/ -bool MDNSResponder::setHostname(const char* p_pcHostname) -{ + return bResult; + } + + /* + MDNSResponder::close - bool bResult = false; + Ends the MDNS responder. + Announced services are unannounced (by multicasting a goodbye message) - if (_setHostname(p_pcHostname)) + */ + bool MDNSResponder::close(void) { - m_HostProbeInformation.m_ProbingStatus = ProbingStatus_ReadyToStart; + bool bResult = false; - // Replace 'auto-set' service names - bResult = true; - for (stcMDNSService* pService = m_pServices; ((bResult) && (pService)); pService = pService->m_pNext) + if (0 != m_pUDPContext) { - if (pService->m_bAutoName) - { - bResult = pService->setName(p_pcHostname); - pService->m_ProbeInformation.m_ProbingStatus = ProbingStatus_ReadyToStart; - } + _announce(false, true); + _resetProbeStatus(false); // Stop probing + _releaseServiceQueries(); + _releaseServices(); + _releaseUDPContext(); + _releaseHostname(); + + bResult = true; } + else + { + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] close: Ignoring call to close!\n"));); + } + return bResult; } - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] setHostname: FAILED for '%s'!\n"), (p_pcHostname ? : "-")); - }); - return bResult; -} - -/* - MDNSResponder::setHostname (LEGACY) -*/ -bool MDNSResponder::setHostname(const String& p_strHostname) -{ - return setHostname(p_strHostname.c_str()); -} + /* + MDNSResponder::end + Ends the MDNS responder. + for compatibility with esp32 -/* - SERVICES -*/ - -/* - MDNSResponder::addService + */ - Add service; using hostname if no name is explicitly provided for the service - The usual '_' underline, which is prepended to service and protocol, eg. _http, - may be given. If not, it is added automatically. + bool MDNSResponder::end(void) + { + return close(); + } -*/ -MDNSResponder::hMDNSService MDNSResponder::addService(const char* p_pcName, - const char* p_pcService, - const char* p_pcProtocol, - uint16_t p_u16Port) -{ + /* + MDNSResponder::setHostname - hMDNSService hResult = 0; + Replaces the current hostname and restarts probing. + For services without own instance name (when the host name was used a instance + name), the instance names are replaced also (and the probing is restarted). - if (((!p_pcName) || // NO name OR - (MDNS_DOMAIN_LABEL_MAXLENGTH >= os_strlen(p_pcName))) && // Fitting name - (p_pcService) && - (MDNS_SERVICE_NAME_LENGTH >= os_strlen(p_pcService)) && - (p_pcProtocol) && - ((MDNS_SERVICE_PROTOCOL_LENGTH - 1) != os_strlen(p_pcProtocol)) && - (p_u16Port)) + */ + bool MDNSResponder::setHostname(const char* p_pcHostname) { + bool bResult = false; - if (!_findService((p_pcName ? : m_pcHostname), p_pcService, p_pcProtocol)) // Not already used + if (_setHostname(p_pcHostname)) { - if (0 != (hResult = (hMDNSService)_allocService(p_pcName, p_pcService, p_pcProtocol, p_u16Port))) - { + m_HostProbeInformation.m_ProbingStatus = ProbingStatus_ReadyToStart; - // Start probing - ((stcMDNSService*)hResult)->m_ProbeInformation.m_ProbingStatus = ProbingStatus_ReadyToStart; + // Replace 'auto-set' service names + bResult = true; + for (stcMDNSService* pService = m_pServices; ((bResult) && (pService)); + pService = pService->m_pNext) + { + if (pService->m_bAutoName) + { + bResult = pService->setName(p_pcHostname); + pService->m_ProbeInformation.m_ProbingStatus = ProbingStatus_ReadyToStart; + } } } - } // else: bad arguments - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] addService: %s to add '%s.%s.%s'!\n"), (hResult ? "Succeeded" : "FAILED"), (p_pcName ? : "-"), p_pcService, p_pcProtocol);); - DEBUG_EX_ERR(if (!hResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] addService: FAILED to add '%s.%s.%s'!\n"), (p_pcName ? : "-"), p_pcService, p_pcProtocol); - }); - return hResult; -} + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] setHostname: FAILED for '%s'!\n"), + (p_pcHostname ?: "-")); + }); + return bResult; + } -/* - MDNSResponder::removeService + /* + MDNSResponder::setHostname (LEGACY) + */ + bool MDNSResponder::setHostname(const String& p_strHostname) + { + return setHostname(p_strHostname.c_str()); + } - Unanounce a service (by sending a goodbye message) and remove it - from the MDNS responder + /* + SERVICES + */ -*/ -bool MDNSResponder::removeService(const MDNSResponder::hMDNSService p_hService) -{ + /* + MDNSResponder::addService - stcMDNSService* pService = 0; - bool bResult = (((pService = _findService(p_hService))) && - (_announceService(*pService, false)) && - (_releaseService(pService))); - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] removeService: FAILED!\n")); - }); - return bResult; -} + Add service; using hostname if no name is explicitly provided for the service + The usual '_' underline, which is prepended to service and protocol, eg. _http, + may be given. If not, it is added automatically. -/* - MDNSResponder::removeService -*/ -bool MDNSResponder::removeService(const char* p_pcName, - const char* p_pcService, - const char* p_pcProtocol) -{ + */ + MDNSResponder::hMDNSService MDNSResponder::addService(const char* p_pcName, + const char* p_pcService, + const char* p_pcProtocol, + uint16_t p_u16Port) + { + hMDNSService hResult = 0; - return removeService((hMDNSService)_findService((p_pcName ? : m_pcHostname), p_pcService, p_pcProtocol)); -} + if (((!p_pcName) || // NO name OR + (MDNS_DOMAIN_LABEL_MAXLENGTH >= os_strlen(p_pcName))) + && // Fitting name + (p_pcService) && (MDNS_SERVICE_NAME_LENGTH >= os_strlen(p_pcService)) && (p_pcProtocol) + && ((MDNS_SERVICE_PROTOCOL_LENGTH - 1) != os_strlen(p_pcProtocol)) && (p_u16Port)) + { + if (!_findService((p_pcName ?: m_pcHostname), p_pcService, + p_pcProtocol)) // Not already used + { + if (0 + != (hResult = (hMDNSService)_allocService(p_pcName, p_pcService, p_pcProtocol, + p_u16Port))) + { + // Start probing + ((stcMDNSService*)hResult)->m_ProbeInformation.m_ProbingStatus + = ProbingStatus_ReadyToStart; + } + } + } // else: bad arguments + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] addService: %s to add '%s.%s.%s'!\n"), + (hResult ? "Succeeded" : "FAILED"), (p_pcName ?: "-"), p_pcService, p_pcProtocol);); + DEBUG_EX_ERR(if (!hResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] addService: FAILED to add '%s.%s.%s'!\n"), + (p_pcName ?: "-"), p_pcService, p_pcProtocol); + }); + return hResult; + } -/* - MDNSResponder::addService (LEGACY) -*/ -bool MDNSResponder::addService(const String& p_strService, - const String& p_strProtocol, - uint16_t p_u16Port) -{ + /* + MDNSResponder::removeService - return (0 != addService(m_pcHostname, p_strService.c_str(), p_strProtocol.c_str(), p_u16Port)); -} + Unanounce a service (by sending a goodbye message) and remove it + from the MDNS responder -/* - MDNSResponder::setServiceName -*/ -bool MDNSResponder::setServiceName(const MDNSResponder::hMDNSService p_hService, - const char* p_pcInstanceName) -{ + */ + bool MDNSResponder::removeService(const MDNSResponder::hMDNSService p_hService) + { + stcMDNSService* pService = 0; + bool bResult = (((pService = _findService(p_hService))) + && (_announceService(*pService, false)) && (_releaseService(pService))); + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] removeService: FAILED!\n")); + }); + return bResult; + } - stcMDNSService* pService = 0; - bool bResult = (((!p_pcInstanceName) || - (MDNS_DOMAIN_LABEL_MAXLENGTH >= os_strlen(p_pcInstanceName))) && - ((pService = _findService(p_hService))) && - (pService->setName(p_pcInstanceName)) && - ((pService->m_ProbeInformation.m_ProbingStatus = ProbingStatus_ReadyToStart))); - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] setServiceName: FAILED for '%s'!\n"), (p_pcInstanceName ? : "-")); - }); - return bResult; -} + /* + MDNSResponder::removeService + */ + bool MDNSResponder::removeService(const char* p_pcName, const char* p_pcService, + const char* p_pcProtocol) + { + return removeService( + (hMDNSService)_findService((p_pcName ?: m_pcHostname), p_pcService, p_pcProtocol)); + } -/* - SERVICE TXT -*/ + /* + MDNSResponder::addService (LEGACY) + */ + bool MDNSResponder::addService(const String& p_strService, const String& p_strProtocol, + uint16_t p_u16Port) + { + return ( + 0 != addService(m_pcHostname, p_strService.c_str(), p_strProtocol.c_str(), p_u16Port)); + } -/* - MDNSResponder::addServiceTxt + /* + MDNSResponder::setServiceName + */ + bool MDNSResponder::setServiceName(const MDNSResponder::hMDNSService p_hService, + const char* p_pcInstanceName) + { + stcMDNSService* pService = 0; + bool bResult + = (((!p_pcInstanceName) || (MDNS_DOMAIN_LABEL_MAXLENGTH >= os_strlen(p_pcInstanceName))) + && ((pService = _findService(p_hService))) && (pService->setName(p_pcInstanceName)) + && ((pService->m_ProbeInformation.m_ProbingStatus = ProbingStatus_ReadyToStart))); + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] setServiceName: FAILED for '%s'!\n"), + (p_pcInstanceName ?: "-")); + }); + return bResult; + } - Add a static service TXT item ('Key'='Value') to a service. + /* + SERVICE TXT + */ -*/ -MDNSResponder::hMDNSTxt MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, - const char* p_pcKey, - const char* p_pcValue) -{ + /* + MDNSResponder::addServiceTxt + + Add a static service TXT item ('Key'='Value') to a service. - hMDNSTxt hTxt = 0; - stcMDNSService* pService = _findService(p_hService); - if (pService) + */ + MDNSResponder::hMDNSTxt + MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, const char* p_pcKey, + const char* p_pcValue) { - hTxt = (hMDNSTxt)_addServiceTxt(pService, p_pcKey, p_pcValue, false); + hMDNSTxt hTxt = 0; + stcMDNSService* pService = _findService(p_hService); + if (pService) + { + hTxt = (hMDNSTxt)_addServiceTxt(pService, p_pcKey, p_pcValue, false); + } + DEBUG_EX_ERR(if (!hTxt) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] addServiceTxt: FAILED for '%s=%s'!\n"), + (p_pcKey ?: "-"), (p_pcValue ?: "-")); + }); + return hTxt; } - DEBUG_EX_ERR(if (!hTxt) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] addServiceTxt: FAILED for '%s=%s'!\n"), (p_pcKey ? : "-"), (p_pcValue ? : "-")); - }); - return hTxt; -} - -/* - MDNSResponder::addServiceTxt (uint32_t) - - Formats: http://www.cplusplus.com/reference/cstdio/printf/ -*/ -MDNSResponder::hMDNSTxt MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, - const char* p_pcKey, - uint32_t p_u32Value) -{ - char acBuffer[32]; *acBuffer = 0; - sprintf(acBuffer, "%u", p_u32Value); - return addServiceTxt(p_hService, p_pcKey, acBuffer); -} + /* + MDNSResponder::addServiceTxt (uint32_t) -/* - MDNSResponder::addServiceTxt (uint16_t) -*/ -MDNSResponder::hMDNSTxt MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, - const char* p_pcKey, - uint16_t p_u16Value) -{ - char acBuffer[16]; *acBuffer = 0; - sprintf(acBuffer, "%hu", p_u16Value); + Formats: http://www.cplusplus.com/reference/cstdio/printf/ + */ + MDNSResponder::hMDNSTxt + MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, const char* p_pcKey, + uint32_t p_u32Value) + { + char acBuffer[32]; + *acBuffer = 0; + sprintf(acBuffer, "%u", p_u32Value); - return addServiceTxt(p_hService, p_pcKey, acBuffer); -} + return addServiceTxt(p_hService, p_pcKey, acBuffer); + } -/* - MDNSResponder::addServiceTxt (uint8_t) -*/ -MDNSResponder::hMDNSTxt MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, - const char* p_pcKey, - uint8_t p_u8Value) -{ - char acBuffer[8]; *acBuffer = 0; - sprintf(acBuffer, "%hhu", p_u8Value); + /* + MDNSResponder::addServiceTxt (uint16_t) + */ + MDNSResponder::hMDNSTxt + MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, const char* p_pcKey, + uint16_t p_u16Value) + { + char acBuffer[16]; + *acBuffer = 0; + sprintf(acBuffer, "%hu", p_u16Value); - return addServiceTxt(p_hService, p_pcKey, acBuffer); -} + return addServiceTxt(p_hService, p_pcKey, acBuffer); + } -/* - MDNSResponder::addServiceTxt (int32_t) -*/ -MDNSResponder::hMDNSTxt MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, - const char* p_pcKey, - int32_t p_i32Value) -{ - char acBuffer[32]; *acBuffer = 0; - sprintf(acBuffer, "%i", p_i32Value); + /* + MDNSResponder::addServiceTxt (uint8_t) + */ + MDNSResponder::hMDNSTxt + MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, const char* p_pcKey, + uint8_t p_u8Value) + { + char acBuffer[8]; + *acBuffer = 0; + sprintf(acBuffer, "%hhu", p_u8Value); - return addServiceTxt(p_hService, p_pcKey, acBuffer); -} + return addServiceTxt(p_hService, p_pcKey, acBuffer); + } -/* - MDNSResponder::addServiceTxt (int16_t) -*/ -MDNSResponder::hMDNSTxt MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, - const char* p_pcKey, - int16_t p_i16Value) -{ - char acBuffer[16]; *acBuffer = 0; - sprintf(acBuffer, "%hi", p_i16Value); + /* + MDNSResponder::addServiceTxt (int32_t) + */ + MDNSResponder::hMDNSTxt + MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, const char* p_pcKey, + int32_t p_i32Value) + { + char acBuffer[32]; + *acBuffer = 0; + sprintf(acBuffer, "%i", p_i32Value); - return addServiceTxt(p_hService, p_pcKey, acBuffer); -} + return addServiceTxt(p_hService, p_pcKey, acBuffer); + } -/* - MDNSResponder::addServiceTxt (int8_t) -*/ -MDNSResponder::hMDNSTxt MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, - const char* p_pcKey, - int8_t p_i8Value) -{ - char acBuffer[8]; *acBuffer = 0; - sprintf(acBuffer, "%hhi", p_i8Value); + /* + MDNSResponder::addServiceTxt (int16_t) + */ + MDNSResponder::hMDNSTxt + MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, const char* p_pcKey, + int16_t p_i16Value) + { + char acBuffer[16]; + *acBuffer = 0; + sprintf(acBuffer, "%hi", p_i16Value); - return addServiceTxt(p_hService, p_pcKey, acBuffer); -} + return addServiceTxt(p_hService, p_pcKey, acBuffer); + } -/* - MDNSResponder::removeServiceTxt + /* + MDNSResponder::addServiceTxt (int8_t) + */ + MDNSResponder::hMDNSTxt + MDNSResponder::addServiceTxt(const MDNSResponder::hMDNSService p_hService, const char* p_pcKey, + int8_t p_i8Value) + { + char acBuffer[8]; + *acBuffer = 0; + sprintf(acBuffer, "%hhi", p_i8Value); - Remove a static service TXT item from a service. -*/ -bool MDNSResponder::removeServiceTxt(const MDNSResponder::hMDNSService p_hService, - const MDNSResponder::hMDNSTxt p_hTxt) -{ + return addServiceTxt(p_hService, p_pcKey, acBuffer); + } - bool bResult = false; + /* + MDNSResponder::removeServiceTxt - stcMDNSService* pService = _findService(p_hService); - if (pService) + Remove a static service TXT item from a service. + */ + bool MDNSResponder::removeServiceTxt(const MDNSResponder::hMDNSService p_hService, + const MDNSResponder::hMDNSTxt p_hTxt) { - stcMDNSServiceTxt* pTxt = _findServiceTxt(pService, p_hTxt); - if (pTxt) + bool bResult = false; + + stcMDNSService* pService = _findService(p_hService); + if (pService) { - bResult = _releaseServiceTxt(pService, pTxt); + stcMDNSServiceTxt* pTxt = _findServiceTxt(pService, p_hTxt); + if (pTxt) + { + bResult = _releaseServiceTxt(pService, pTxt); + } } + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] removeServiceTxt: FAILED!\n")); + }); + return bResult; } - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] removeServiceTxt: FAILED!\n")); - }); - return bResult; -} - -/* - MDNSResponder::removeServiceTxt -*/ -bool MDNSResponder::removeServiceTxt(const MDNSResponder::hMDNSService p_hService, - const char* p_pcKey) -{ - bool bResult = false; - - stcMDNSService* pService = _findService(p_hService); - if (pService) + /* + MDNSResponder::removeServiceTxt + */ + bool MDNSResponder::removeServiceTxt(const MDNSResponder::hMDNSService p_hService, + const char* p_pcKey) { - stcMDNSServiceTxt* pTxt = _findServiceTxt(pService, p_pcKey); - if (pTxt) + bool bResult = false; + + stcMDNSService* pService = _findService(p_hService); + if (pService) { - bResult = _releaseServiceTxt(pService, pTxt); + stcMDNSServiceTxt* pTxt = _findServiceTxt(pService, p_pcKey); + if (pTxt) + { + bResult = _releaseServiceTxt(pService, pTxt); + } } + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] removeServiceTxt: FAILED for '%s'!\n"), + (p_pcKey ?: "-")); + }); + return bResult; } - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] removeServiceTxt: FAILED for '%s'!\n"), (p_pcKey ? : "-")); - }); - return bResult; -} - -/* - MDNSResponder::removeServiceTxt -*/ -bool MDNSResponder::removeServiceTxt(const char* p_pcName, - const char* p_pcService, - const char* p_pcProtocol, - const char* p_pcKey) -{ - - bool bResult = false; - stcMDNSService* pService = _findService((p_pcName ? : m_pcHostname), p_pcService, p_pcProtocol); - if (pService) + /* + MDNSResponder::removeServiceTxt + */ + bool MDNSResponder::removeServiceTxt(const char* p_pcName, const char* p_pcService, + const char* p_pcProtocol, const char* p_pcKey) { - stcMDNSServiceTxt* pTxt = _findServiceTxt(pService, p_pcKey); - if (pTxt) + bool bResult = false; + + stcMDNSService* pService + = _findService((p_pcName ?: m_pcHostname), p_pcService, p_pcProtocol); + if (pService) { - bResult = _releaseServiceTxt(pService, pTxt); + stcMDNSServiceTxt* pTxt = _findServiceTxt(pService, p_pcKey); + if (pTxt) + { + bResult = _releaseServiceTxt(pService, pTxt); + } } + return bResult; } - return bResult; -} -/* - MDNSResponder::addServiceTxt (LEGACY) -*/ -bool MDNSResponder::addServiceTxt(const char* p_pcService, - const char* p_pcProtocol, - const char* p_pcKey, - const char* p_pcValue) -{ + /* + MDNSResponder::addServiceTxt (LEGACY) + */ + bool MDNSResponder::addServiceTxt(const char* p_pcService, const char* p_pcProtocol, + const char* p_pcKey, const char* p_pcValue) + { + return (0 + != _addServiceTxt(_findService(m_pcHostname, p_pcService, p_pcProtocol), p_pcKey, + p_pcValue, false)); + } - return (0 != _addServiceTxt(_findService(m_pcHostname, p_pcService, p_pcProtocol), p_pcKey, p_pcValue, false)); -} + /* + MDNSResponder::addServiceTxt (LEGACY) + */ + bool MDNSResponder::addServiceTxt(const String& p_strService, const String& p_strProtocol, + const String& p_strKey, const String& p_strValue) + { + return (0 + != _addServiceTxt( + _findService(m_pcHostname, p_strService.c_str(), p_strProtocol.c_str()), + p_strKey.c_str(), p_strValue.c_str(), false)); + } -/* - MDNSResponder::addServiceTxt (LEGACY) -*/ -bool MDNSResponder::addServiceTxt(const String& p_strService, - const String& p_strProtocol, - const String& p_strKey, - const String& p_strValue) -{ + /* + MDNSResponder::setDynamicServiceTxtCallback (global) - return (0 != _addServiceTxt(_findService(m_pcHostname, p_strService.c_str(), p_strProtocol.c_str()), p_strKey.c_str(), p_strValue.c_str(), false)); -} + Set a global callback for dynamic service TXT items. The callback is called, whenever + service TXT items are needed. -/* - MDNSResponder::setDynamicServiceTxtCallback (global) + */ + bool MDNSResponder::setDynamicServiceTxtCallback( + MDNSResponder::MDNSDynamicServiceTxtCallbackFunc p_fnCallback) + { + m_fnServiceTxtCallback = p_fnCallback; - Set a global callback for dynamic service TXT items. The callback is called, whenever - service TXT items are needed. + return true; + } -*/ -bool MDNSResponder::setDynamicServiceTxtCallback(MDNSResponder::MDNSDynamicServiceTxtCallbackFunc p_fnCallback) -{ + /* + MDNSResponder::setDynamicServiceTxtCallback (service specific) + + Set a service specific callback for dynamic service TXT items. The callback is called, + whenever service TXT items are needed for the given service. - m_fnServiceTxtCallback = p_fnCallback; + */ + bool MDNSResponder::setDynamicServiceTxtCallback( + MDNSResponder::hMDNSService p_hService, + MDNSResponder::MDNSDynamicServiceTxtCallbackFunc p_fnCallback) + { + bool bResult = false; - return true; -} + stcMDNSService* pService = _findService(p_hService); + if (pService) + { + pService->m_fnTxtCallback = p_fnCallback; -/* - MDNSResponder::setDynamicServiceTxtCallback (service specific) + bResult = true; + } + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] setDynamicServiceTxtCallback: FAILED!\n")); + }); + return bResult; + } - Set a service specific callback for dynamic service TXT items. The callback is called, whenever - service TXT items are needed for the given service. + /* + MDNSResponder::addDynamicServiceTxt + */ + MDNSResponder::hMDNSTxt + MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, const char* p_pcKey, + const char* p_pcValue) + { + // DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] addDynamicServiceTxt + // (%s=%s)\n"), p_pcKey, p_pcValue);); -*/ -bool MDNSResponder::setDynamicServiceTxtCallback(MDNSResponder::hMDNSService p_hService, - MDNSResponder::MDNSDynamicServiceTxtCallbackFunc p_fnCallback) -{ + hMDNSTxt hTxt = 0; - bool bResult = false; + stcMDNSService* pService = _findService(p_hService); + if (pService) + { + hTxt = _addServiceTxt(pService, p_pcKey, p_pcValue, true); + } + DEBUG_EX_ERR(if (!hTxt) { + DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] addDynamicServiceTxt: FAILED for '%s=%s'!\n"), + (p_pcKey ?: "-"), (p_pcValue ?: "-")); + }); + return hTxt; + } - stcMDNSService* pService = _findService(p_hService); - if (pService) + /* + MDNSResponder::addDynamicServiceTxt (uint32_t) + */ + MDNSResponder::hMDNSTxt + MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, const char* p_pcKey, + uint32_t p_u32Value) { - pService->m_fnTxtCallback = p_fnCallback; + char acBuffer[32]; + *acBuffer = 0; + sprintf(acBuffer, "%u", p_u32Value); - bResult = true; + return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); } - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] setDynamicServiceTxtCallback: FAILED!\n")); - }); - return bResult; -} -/* - MDNSResponder::addDynamicServiceTxt -*/ -MDNSResponder::hMDNSTxt MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, - const char* p_pcKey, - const char* p_pcValue) -{ - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] addDynamicServiceTxt (%s=%s)\n"), p_pcKey, p_pcValue);); + /* + MDNSResponder::addDynamicServiceTxt (uint16_t) + */ + MDNSResponder::hMDNSTxt + MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, const char* p_pcKey, + uint16_t p_u16Value) + { + char acBuffer[16]; + *acBuffer = 0; + sprintf(acBuffer, "%hu", p_u16Value); - hMDNSTxt hTxt = 0; + return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); + } - stcMDNSService* pService = _findService(p_hService); - if (pService) + /* + MDNSResponder::addDynamicServiceTxt (uint8_t) + */ + MDNSResponder::hMDNSTxt + MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, const char* p_pcKey, + uint8_t p_u8Value) { - hTxt = _addServiceTxt(pService, p_pcKey, p_pcValue, true); + char acBuffer[8]; + *acBuffer = 0; + sprintf(acBuffer, "%hhu", p_u8Value); + + return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); } - DEBUG_EX_ERR(if (!hTxt) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] addDynamicServiceTxt: FAILED for '%s=%s'!\n"), (p_pcKey ? : "-"), (p_pcValue ? : "-")); - }); - return hTxt; -} -/* - MDNSResponder::addDynamicServiceTxt (uint32_t) -*/ -MDNSResponder::hMDNSTxt MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, - const char* p_pcKey, - uint32_t p_u32Value) -{ + /* + MDNSResponder::addDynamicServiceTxt (int32_t) + */ + MDNSResponder::hMDNSTxt + MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, const char* p_pcKey, + int32_t p_i32Value) + { + char acBuffer[32]; + *acBuffer = 0; + sprintf(acBuffer, "%i", p_i32Value); - char acBuffer[32]; *acBuffer = 0; - sprintf(acBuffer, "%u", p_u32Value); + return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); + } - return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); -} + /* + MDNSResponder::addDynamicServiceTxt (int16_t) + */ + MDNSResponder::hMDNSTxt + MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, const char* p_pcKey, + int16_t p_i16Value) + { + char acBuffer[16]; + *acBuffer = 0; + sprintf(acBuffer, "%hi", p_i16Value); -/* - MDNSResponder::addDynamicServiceTxt (uint16_t) -*/ -MDNSResponder::hMDNSTxt MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, - const char* p_pcKey, - uint16_t p_u16Value) -{ + return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); + } - char acBuffer[16]; *acBuffer = 0; - sprintf(acBuffer, "%hu", p_u16Value); + /* + MDNSResponder::addDynamicServiceTxt (int8_t) + */ + MDNSResponder::hMDNSTxt + MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, const char* p_pcKey, + int8_t p_i8Value) + { + char acBuffer[8]; + *acBuffer = 0; + sprintf(acBuffer, "%hhi", p_i8Value); - return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); -} + return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); + } -/* - MDNSResponder::addDynamicServiceTxt (uint8_t) -*/ -MDNSResponder::hMDNSTxt MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, - const char* p_pcKey, - uint8_t p_u8Value) -{ + /** + STATIC SERVICE QUERY (LEGACY) + */ - char acBuffer[8]; *acBuffer = 0; - sprintf(acBuffer, "%hhu", p_u8Value); + /* + MDNSResponder::queryService - return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); -} + Perform a (blocking) static service query. + The arrived answers can be queried by calling: + - answerHostname (or 'hostname') + - answerIP (or 'IP') + - answerPort (or 'port') -/* - MDNSResponder::addDynamicServiceTxt (int32_t) -*/ -MDNSResponder::hMDNSTxt MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, - const char* p_pcKey, - int32_t p_i32Value) -{ + */ + uint32_t + MDNSResponder::queryService(const char* p_pcService, const char* p_pcProtocol, + const uint16_t p_u16Timeout /*= MDNS_QUERYSERVICES_WAIT_TIME*/) + { + if (0 == m_pUDPContext) + { + // safeguard against misuse + return 0; + } - char acBuffer[32]; *acBuffer = 0; - sprintf(acBuffer, "%i", p_i32Value); + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] queryService '%s.%s'\n"), + p_pcService, p_pcProtocol);); - return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); -} + uint32_t u32Result = 0; -/* - MDNSResponder::addDynamicServiceTxt (int16_t) -*/ -MDNSResponder::hMDNSTxt MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, - const char* p_pcKey, - int16_t p_i16Value) -{ + stcMDNSServiceQuery* pServiceQuery = 0; + if ((p_pcService) && (os_strlen(p_pcService)) && (p_pcProtocol) && (os_strlen(p_pcProtocol)) + && (p_u16Timeout) && (_removeLegacyServiceQuery()) + && ((pServiceQuery = _allocServiceQuery())) + && (_buildDomainForService(p_pcService, p_pcProtocol, + pServiceQuery->m_ServiceTypeDomain))) + { + pServiceQuery->m_bLegacyQuery = true; - char acBuffer[16]; *acBuffer = 0; - sprintf(acBuffer, "%hi", p_i16Value); + if (_sendMDNSServiceQuery(*pServiceQuery)) + { + // Wait for answers to arrive + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] queryService: Waiting %u ms for answers...\n"), + p_u16Timeout);); + delay(p_u16Timeout); + + // All answers should have arrived by now -> stop adding new answers + pServiceQuery->m_bAwaitingAnswers = false; + u32Result = pServiceQuery->answerCount(); + } + else // FAILED to send query + { + _removeServiceQuery(pServiceQuery); + } + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] queryService: INVALID input data!\n"));); + } + return u32Result; + } - return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); -} + /* + MDNSResponder::removeQuery -/* - MDNSResponder::addDynamicServiceTxt (int8_t) -*/ -MDNSResponder::hMDNSTxt MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, - const char* p_pcKey, - int8_t p_i8Value) -{ + Remove the last static service query (and all answers). - char acBuffer[8]; *acBuffer = 0; - sprintf(acBuffer, "%hhi", p_i8Value); + */ + bool MDNSResponder::removeQuery(void) + { + return _removeLegacyServiceQuery(); + } - return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); -} + /* + MDNSResponder::queryService (LEGACY) + */ + uint32_t MDNSResponder::queryService(const String& p_strService, const String& p_strProtocol) + { + return queryService(p_strService.c_str(), p_strProtocol.c_str()); + } + /* + MDNSResponder::answerHostname + */ + const char* MDNSResponder::answerHostname(const uint32_t p_u32AnswerIndex) + { + stcMDNSServiceQuery* pServiceQuery = _findLegacyServiceQuery(); + stcMDNSServiceQuery::stcAnswer* pSQAnswer + = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); -/** - STATIC SERVICE QUERY (LEGACY) -*/ - -/* - MDNSResponder::queryService - - Perform a (blocking) static service query. - The arrived answers can be queried by calling: - - answerHostname (or 'hostname') - - answerIP (or 'IP') - - answerPort (or 'port') - -*/ -uint32_t MDNSResponder::queryService(const char* p_pcService, - const char* p_pcProtocol, - const uint16_t p_u16Timeout /*= MDNS_QUERYSERVICES_WAIT_TIME*/) -{ - if (0 == m_pUDPContext) - { - // safeguard against misuse - return 0; - } - - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] queryService '%s.%s'\n"), p_pcService, p_pcProtocol);); - - uint32_t u32Result = 0; - - stcMDNSServiceQuery* pServiceQuery = 0; - if ((p_pcService) && - (os_strlen(p_pcService)) && - (p_pcProtocol) && - (os_strlen(p_pcProtocol)) && - (p_u16Timeout) && - (_removeLegacyServiceQuery()) && - ((pServiceQuery = _allocServiceQuery())) && - (_buildDomainForService(p_pcService, p_pcProtocol, pServiceQuery->m_ServiceTypeDomain))) - { - - pServiceQuery->m_bLegacyQuery = true; - - if (_sendMDNSServiceQuery(*pServiceQuery)) + if ((pSQAnswer) && (pSQAnswer->m_HostDomain.m_u16NameLength) + && (!pSQAnswer->m_pcHostDomain)) { - // Wait for answers to arrive - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] queryService: Waiting %u ms for answers...\n"), p_u16Timeout);); - delay(p_u16Timeout); - - // All answers should have arrived by now -> stop adding new answers - pServiceQuery->m_bAwaitingAnswers = false; - u32Result = pServiceQuery->answerCount(); - } - else // FAILED to send query - { - _removeServiceQuery(pServiceQuery); + char* pcHostDomain = pSQAnswer->allocHostDomain(pSQAnswer->m_HostDomain.c_strLength()); + if (pcHostDomain) + { + pSQAnswer->m_HostDomain.c_str(pcHostDomain); + } } + return (pSQAnswer ? pSQAnswer->m_pcHostDomain : 0); } - else - { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] queryService: INVALID input data!\n"));); - } - return u32Result; -} - -/* - MDNSResponder::removeQuery - - Remove the last static service query (and all answers). - -*/ -bool MDNSResponder::removeQuery(void) -{ - return _removeLegacyServiceQuery(); -} - -/* - MDNSResponder::queryService (LEGACY) -*/ -uint32_t MDNSResponder::queryService(const String& p_strService, - const String& p_strProtocol) -{ - - return queryService(p_strService.c_str(), p_strProtocol.c_str()); -} - -/* - MDNSResponder::answerHostname -*/ -const char* MDNSResponder::answerHostname(const uint32_t p_u32AnswerIndex) -{ - - stcMDNSServiceQuery* pServiceQuery = _findLegacyServiceQuery(); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - - if ((pSQAnswer) && - (pSQAnswer->m_HostDomain.m_u16NameLength) && - (!pSQAnswer->m_pcHostDomain)) +#ifdef MDNS_IP4_SUPPORT + /* + MDNSResponder::answerIP + */ + IPAddress MDNSResponder::answerIP(const uint32_t p_u32AnswerIndex) { - - char* pcHostDomain = pSQAnswer->allocHostDomain(pSQAnswer->m_HostDomain.c_strLength()); - if (pcHostDomain) - { - pSQAnswer->m_HostDomain.c_str(pcHostDomain); - } + const stcMDNSServiceQuery* pServiceQuery = _findLegacyServiceQuery(); + const stcMDNSServiceQuery::stcAnswer* pSQAnswer + = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + const stcMDNSServiceQuery::stcAnswer::stcIP4Address* pIP4Address + = (((pSQAnswer) && (pSQAnswer->m_pIP4Addresses)) ? pSQAnswer->IP4AddressAtIndex(0) : 0); + return (pIP4Address ? pIP4Address->m_IPAddress : IPAddress()); } - return (pSQAnswer ? pSQAnswer->m_pcHostDomain : 0); -} - -#ifdef MDNS_IP4_SUPPORT -/* - MDNSResponder::answerIP -*/ -IPAddress MDNSResponder::answerIP(const uint32_t p_u32AnswerIndex) -{ - - const stcMDNSServiceQuery* pServiceQuery = _findLegacyServiceQuery(); - const stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - const stcMDNSServiceQuery::stcAnswer::stcIP4Address* pIP4Address = (((pSQAnswer) && (pSQAnswer->m_pIP4Addresses)) ? pSQAnswer->IP4AddressAtIndex(0) : 0); - return (pIP4Address ? pIP4Address->m_IPAddress : IPAddress()); -} #endif #ifdef MDNS_IP6_SUPPORT -/* - MDNSResponder::answerIP6 -*/ -IPAddress MDNSResponder::answerIP6(const uint32_t p_u32AnswerIndex) -{ - - const stcMDNSServiceQuery* pServiceQuery = _findLegacyServiceQuery(); - const stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - const stcMDNSServiceQuery::stcAnswer::stcIP6Address* pIP6Address = (((pSQAnswer) && (pSQAnswer->m_pIP6Addresses)) ? pSQAnswer->IP6AddressAtIndex(0) : 0); - return (pIP6Address ? pIP6Address->m_IPAddress : IP6Address()); -} + /* + MDNSResponder::answerIP6 + */ + IPAddress MDNSResponder::answerIP6(const uint32_t p_u32AnswerIndex) + { + const stcMDNSServiceQuery* pServiceQuery = _findLegacyServiceQuery(); + const stcMDNSServiceQuery::stcAnswer* pSQAnswer + = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + const stcMDNSServiceQuery::stcAnswer::stcIP6Address* pIP6Address + = (((pSQAnswer) && (pSQAnswer->m_pIP6Addresses)) ? pSQAnswer->IP6AddressAtIndex(0) : 0); + return (pIP6Address ? pIP6Address->m_IPAddress : IP6Address()); + } #endif -/* - MDNSResponder::answerPort -*/ -uint16_t MDNSResponder::answerPort(const uint32_t p_u32AnswerIndex) -{ - - const stcMDNSServiceQuery* pServiceQuery = _findLegacyServiceQuery(); - const stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - return (pSQAnswer ? pSQAnswer->m_u16Port : 0); -} - -/* - MDNSResponder::hostname (LEGACY) -*/ -String MDNSResponder::hostname(const uint32_t p_u32AnswerIndex) -{ - - return String(answerHostname(p_u32AnswerIndex)); -} - -/* - MDNSResponder::IP (LEGACY) -*/ -IPAddress MDNSResponder::IP(const uint32_t p_u32AnswerIndex) -{ - - return answerIP(p_u32AnswerIndex); -} - -/* - MDNSResponder::port (LEGACY) -*/ -uint16_t MDNSResponder::port(const uint32_t p_u32AnswerIndex) -{ - - return answerPort(p_u32AnswerIndex); -} - - -/** - DYNAMIC SERVICE QUERY -*/ - -/* - MDNSResponder::installServiceQuery + /* + MDNSResponder::answerPort + */ + uint16_t MDNSResponder::answerPort(const uint32_t p_u32AnswerIndex) + { + const stcMDNSServiceQuery* pServiceQuery = _findLegacyServiceQuery(); + const stcMDNSServiceQuery::stcAnswer* pSQAnswer + = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + return (pSQAnswer ? pSQAnswer->m_u16Port : 0); + } - Add a dynamic service query and a corresponding callback to the MDNS responder. - The callback will be called for every answer update. - The answers can also be queried by calling: - - answerServiceDomain - - answerHostDomain - - answerIP4Address/answerIP6Address - - answerPort - - answerTxts + /* + MDNSResponder::hostname (LEGACY) + */ + String MDNSResponder::hostname(const uint32_t p_u32AnswerIndex) + { + return String(answerHostname(p_u32AnswerIndex)); + } -*/ -MDNSResponder::hMDNSServiceQuery MDNSResponder::installServiceQuery(const char* p_pcService, - const char* p_pcProtocol, - MDNSResponder::MDNSServiceQueryCallbackFunc p_fnCallback) -{ - hMDNSServiceQuery hResult = 0; + /* + MDNSResponder::IP (LEGACY) + */ + IPAddress MDNSResponder::IP(const uint32_t p_u32AnswerIndex) + { + return answerIP(p_u32AnswerIndex); + } - stcMDNSServiceQuery* pServiceQuery = 0; - if ((p_pcService) && - (os_strlen(p_pcService)) && - (p_pcProtocol) && - (os_strlen(p_pcProtocol)) && - (p_fnCallback) && - ((pServiceQuery = _allocServiceQuery())) && - (_buildDomainForService(p_pcService, p_pcProtocol, pServiceQuery->m_ServiceTypeDomain))) + /* + MDNSResponder::port (LEGACY) + */ + uint16_t MDNSResponder::port(const uint32_t p_u32AnswerIndex) { + return answerPort(p_u32AnswerIndex); + } - pServiceQuery->m_fnCallback = p_fnCallback; - pServiceQuery->m_bLegacyQuery = false; + /** + DYNAMIC SERVICE QUERY + */ + + /* + MDNSResponder::installServiceQuery + + Add a dynamic service query and a corresponding callback to the MDNS responder. + The callback will be called for every answer update. + The answers can also be queried by calling: + - answerServiceDomain + - answerHostDomain + - answerIP4Address/answerIP6Address + - answerPort + - answerTxts + + */ + MDNSResponder::hMDNSServiceQuery + MDNSResponder::installServiceQuery(const char* p_pcService, const char* p_pcProtocol, + MDNSResponder::MDNSServiceQueryCallbackFunc p_fnCallback) + { + hMDNSServiceQuery hResult = 0; - if (_sendMDNSServiceQuery(*pServiceQuery)) + stcMDNSServiceQuery* pServiceQuery = 0; + if ((p_pcService) && (os_strlen(p_pcService)) && (p_pcProtocol) && (os_strlen(p_pcProtocol)) + && (p_fnCallback) && ((pServiceQuery = _allocServiceQuery())) + && (_buildDomainForService(p_pcService, p_pcProtocol, + pServiceQuery->m_ServiceTypeDomain))) { - pServiceQuery->m_u8SentCount = 1; - pServiceQuery->m_ResendTimeout.reset(MDNS_DYNAMIC_QUERY_RESEND_DELAY); + pServiceQuery->m_fnCallback = p_fnCallback; + pServiceQuery->m_bLegacyQuery = false; - hResult = (hMDNSServiceQuery)pServiceQuery; - } - else - { - _removeServiceQuery(pServiceQuery); + if (_sendMDNSServiceQuery(*pServiceQuery)) + { + pServiceQuery->m_u8SentCount = 1; + pServiceQuery->m_ResendTimeout.reset(MDNS_DYNAMIC_QUERY_RESEND_DELAY); + + hResult = (hMDNSServiceQuery)pServiceQuery; + } + else + { + _removeServiceQuery(pServiceQuery); + } } + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] installServiceQuery: %s for '%s.%s'!\n\n"), + (hResult ? "Succeeded" : "FAILED"), (p_pcService ?: "-"), (p_pcProtocol ?: "-"));); + DEBUG_EX_ERR(if (!hResult) { + DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] installServiceQuery: FAILED for '%s.%s'!\n\n"), + (p_pcService ?: "-"), (p_pcProtocol ?: "-")); + }); + return hResult; } - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] installServiceQuery: %s for '%s.%s'!\n\n"), (hResult ? "Succeeded" : "FAILED"), (p_pcService ? : "-"), (p_pcProtocol ? : "-"));); - DEBUG_EX_ERR(if (!hResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] installServiceQuery: FAILED for '%s.%s'!\n\n"), (p_pcService ? : "-"), (p_pcProtocol ? : "-")); - }); - return hResult; -} - -/* - MDNSResponder::removeServiceQuery - Remove a dynamic service query (and all collected answers) from the MDNS responder + /* + MDNSResponder::removeServiceQuery -*/ -bool MDNSResponder::removeServiceQuery(MDNSResponder::hMDNSServiceQuery p_hServiceQuery) -{ - - stcMDNSServiceQuery* pServiceQuery = 0; - bool bResult = (((pServiceQuery = _findServiceQuery(p_hServiceQuery))) && - (_removeServiceQuery(pServiceQuery))); - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] removeServiceQuery: FAILED!\n")); - }); - return bResult; -} - -/* - MDNSResponder::answerCount -*/ -uint32_t MDNSResponder::answerCount(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery) -{ + Remove a dynamic service query (and all collected answers) from the MDNS responder - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - return (pServiceQuery ? pServiceQuery->answerCount() : 0); -} + */ + bool MDNSResponder::removeServiceQuery(MDNSResponder::hMDNSServiceQuery p_hServiceQuery) + { + stcMDNSServiceQuery* pServiceQuery = 0; + bool bResult = (((pServiceQuery = _findServiceQuery(p_hServiceQuery))) + && (_removeServiceQuery(pServiceQuery))); + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] removeServiceQuery: FAILED!\n")); + }); + return bResult; + } -std::vector MDNSResponder::answerInfo(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery) -{ - std::vector tempVector; - for (uint32_t i = 0; i < answerCount(p_hServiceQuery); i++) + /* + MDNSResponder::answerCount + */ + uint32_t MDNSResponder::answerCount(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery) { - tempVector.emplace_back(*this, p_hServiceQuery, i); + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + return (pServiceQuery ? pServiceQuery->answerCount() : 0); } - return tempVector; -} -/* - MDNSResponder::answerServiceDomain + std::vector + MDNSResponder::answerInfo(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery) + { + std::vector tempVector; + for (uint32_t i = 0; i < answerCount(p_hServiceQuery); i++) + { + tempVector.emplace_back(*this, p_hServiceQuery, i); + } + return tempVector; + } - Returns the domain for the given service. - If not already existing, the string is allocated, filled and attached to the answer. + /* + MDNSResponder::answerServiceDomain -*/ -const char* MDNSResponder::answerServiceDomain(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex) -{ + Returns the domain for the given service. + If not already existing, the string is allocated, filled and attached to the answer. - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - // Fill m_pcServiceDomain (if not already done) - if ((pSQAnswer) && - (pSQAnswer->m_ServiceDomain.m_u16NameLength) && - (!pSQAnswer->m_pcServiceDomain)) + */ + const char* + MDNSResponder::answerServiceDomain(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex) { - - pSQAnswer->m_pcServiceDomain = pSQAnswer->allocServiceDomain(pSQAnswer->m_ServiceDomain.c_strLength()); - if (pSQAnswer->m_pcServiceDomain) + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + stcMDNSServiceQuery::stcAnswer* pSQAnswer + = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + // Fill m_pcServiceDomain (if not already done) + if ((pSQAnswer) && (pSQAnswer->m_ServiceDomain.m_u16NameLength) + && (!pSQAnswer->m_pcServiceDomain)) { - pSQAnswer->m_ServiceDomain.c_str(pSQAnswer->m_pcServiceDomain); + pSQAnswer->m_pcServiceDomain + = pSQAnswer->allocServiceDomain(pSQAnswer->m_ServiceDomain.c_strLength()); + if (pSQAnswer->m_pcServiceDomain) + { + pSQAnswer->m_ServiceDomain.c_str(pSQAnswer->m_pcServiceDomain); + } } + return (pSQAnswer ? pSQAnswer->m_pcServiceDomain : 0); } - return (pSQAnswer ? pSQAnswer->m_pcServiceDomain : 0); -} - -/* - MDNSResponder::hasAnswerHostDomain -*/ -bool MDNSResponder::hasAnswerHostDomain(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex) -{ - - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - return ((pSQAnswer) && - (pSQAnswer->m_u32ContentFlags & ServiceQueryAnswerType_HostDomainAndPort)); -} -/* - MDNSResponder::answerHostDomain + /* + MDNSResponder::hasAnswerHostDomain + */ + bool MDNSResponder::hasAnswerHostDomain(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex) + { + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + stcMDNSServiceQuery::stcAnswer* pSQAnswer + = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + return ((pSQAnswer) + && (pSQAnswer->m_u32ContentFlags & ServiceQueryAnswerType_HostDomainAndPort)); + } - Returns the host domain for the given service. - If not already existing, the string is allocated, filled and attached to the answer. + /* + MDNSResponder::answerHostDomain -*/ -const char* MDNSResponder::answerHostDomain(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex) -{ + Returns the host domain for the given service. + If not already existing, the string is allocated, filled and attached to the answer. - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - // Fill m_pcHostDomain (if not already done) - if ((pSQAnswer) && - (pSQAnswer->m_HostDomain.m_u16NameLength) && - (!pSQAnswer->m_pcHostDomain)) + */ + const char* + MDNSResponder::answerHostDomain(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex) { - - pSQAnswer->m_pcHostDomain = pSQAnswer->allocHostDomain(pSQAnswer->m_HostDomain.c_strLength()); - if (pSQAnswer->m_pcHostDomain) + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + stcMDNSServiceQuery::stcAnswer* pSQAnswer + = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + // Fill m_pcHostDomain (if not already done) + if ((pSQAnswer) && (pSQAnswer->m_HostDomain.m_u16NameLength) + && (!pSQAnswer->m_pcHostDomain)) { - pSQAnswer->m_HostDomain.c_str(pSQAnswer->m_pcHostDomain); + pSQAnswer->m_pcHostDomain + = pSQAnswer->allocHostDomain(pSQAnswer->m_HostDomain.c_strLength()); + if (pSQAnswer->m_pcHostDomain) + { + pSQAnswer->m_HostDomain.c_str(pSQAnswer->m_pcHostDomain); + } } + return (pSQAnswer ? pSQAnswer->m_pcHostDomain : 0); } - return (pSQAnswer ? pSQAnswer->m_pcHostDomain : 0); -} #ifdef MDNS_IP4_SUPPORT -/* - MDNSResponder::hasAnswerIP4Address -*/ -bool MDNSResponder::hasAnswerIP4Address(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex) -{ - - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - return ((pSQAnswer) && - (pSQAnswer->m_u32ContentFlags & ServiceQueryAnswerType_IP4Address)); -} - -/* - MDNSResponder::answerIP4AddressCount -*/ -uint32_t MDNSResponder::answerIP4AddressCount(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex) -{ - - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - return (pSQAnswer ? pSQAnswer->IP4AddressCount() : 0); -} + /* + MDNSResponder::hasAnswerIP4Address + */ + bool MDNSResponder::hasAnswerIP4Address(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex) + { + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + stcMDNSServiceQuery::stcAnswer* pSQAnswer + = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + return ((pSQAnswer) && (pSQAnswer->m_u32ContentFlags & ServiceQueryAnswerType_IP4Address)); + } -/* - MDNSResponder::answerIP4Address -*/ -IPAddress MDNSResponder::answerIP4Address(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex, - const uint32_t p_u32AddressIndex) -{ + /* + MDNSResponder::answerIP4AddressCount + */ + uint32_t + MDNSResponder::answerIP4AddressCount(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex) + { + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + stcMDNSServiceQuery::stcAnswer* pSQAnswer + = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + return (pSQAnswer ? pSQAnswer->IP4AddressCount() : 0); + } - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - stcMDNSServiceQuery::stcAnswer::stcIP4Address* pIP4Address = (pSQAnswer ? pSQAnswer->IP4AddressAtIndex(p_u32AddressIndex) : 0); - return (pIP4Address ? pIP4Address->m_IPAddress : IPAddress()); -} + /* + MDNSResponder::answerIP4Address + */ + IPAddress + MDNSResponder::answerIP4Address(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex, + const uint32_t p_u32AddressIndex) + { + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + stcMDNSServiceQuery::stcAnswer* pSQAnswer + = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + stcMDNSServiceQuery::stcAnswer::stcIP4Address* pIP4Address + = (pSQAnswer ? pSQAnswer->IP4AddressAtIndex(p_u32AddressIndex) : 0); + return (pIP4Address ? pIP4Address->m_IPAddress : IPAddress()); + } #endif #ifdef MDNS_IP6_SUPPORT -/* - MDNSResponder::hasAnswerIP6Address -*/ -bool MDNSResponder::hasAnswerIP6Address(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex) -{ - - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - return ((pSQAnswer) && - (pSQAnswer->m_u32ContentFlags & ServiceQueryAnswerType_HostIP6Address)); -} - -/* - MDNSResponder::answerIP6AddressCount -*/ -uint32_t MDNSResponder::answerIP6AddressCount(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex) -{ - - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - return (pSQAnswer ? pSQAnswer->IP6AddressCount() : 0); -} + /* + MDNSResponder::hasAnswerIP6Address + */ + bool MDNSResponder::hasAnswerIP6Address(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex) + { + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + stcMDNSServiceQuery::stcAnswer* pSQAnswer + = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + return ((pSQAnswer) + && (pSQAnswer->m_u32ContentFlags & ServiceQueryAnswerType_HostIP6Address)); + } -/* - MDNSResponder::answerIP6Address -*/ -IPAddress MDNSResponder::answerIP6Address(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex, - const uint32_t p_u32AddressIndex) -{ + /* + MDNSResponder::answerIP6AddressCount + */ + uint32_t + MDNSResponder::answerIP6AddressCount(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex) + { + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + stcMDNSServiceQuery::stcAnswer* pSQAnswer + = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + return (pSQAnswer ? pSQAnswer->IP6AddressCount() : 0); + } - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - stcMDNSServiceQuery::stcAnswer::stcIP6Address* pIP6Address = (pSQAnswer ? pSQAnswer->IP6AddressAtIndex(p_u32AddressIndex) : 0); - return (pIP6Address ? pIP6Address->m_IPAddress : IPAddress()); -} + /* + MDNSResponder::answerIP6Address + */ + IPAddress + MDNSResponder::answerIP6Address(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex, + const uint32_t p_u32AddressIndex) + { + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + stcMDNSServiceQuery::stcAnswer* pSQAnswer + = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + stcMDNSServiceQuery::stcAnswer::stcIP6Address* pIP6Address + = (pSQAnswer ? pSQAnswer->IP6AddressAtIndex(p_u32AddressIndex) : 0); + return (pIP6Address ? pIP6Address->m_IPAddress : IPAddress()); + } #endif -/* - MDNSResponder::hasAnswerPort -*/ -bool MDNSResponder::hasAnswerPort(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex) -{ - - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - return ((pSQAnswer) && - (pSQAnswer->m_u32ContentFlags & ServiceQueryAnswerType_HostDomainAndPort)); -} - -/* - MDNSResponder::answerPort -*/ -uint16_t MDNSResponder::answerPort(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex) -{ - - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - return (pSQAnswer ? pSQAnswer->m_u16Port : 0); -} - -/* - MDNSResponder::hasAnswerTxts -*/ -bool MDNSResponder::hasAnswerTxts(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex) -{ + /* + MDNSResponder::hasAnswerPort + */ + bool MDNSResponder::hasAnswerPort(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex) + { + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + stcMDNSServiceQuery::stcAnswer* pSQAnswer + = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + return ((pSQAnswer) + && (pSQAnswer->m_u32ContentFlags & ServiceQueryAnswerType_HostDomainAndPort)); + } - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - return ((pSQAnswer) && - (pSQAnswer->m_u32ContentFlags & ServiceQueryAnswerType_Txts)); -} + /* + MDNSResponder::answerPort + */ + uint16_t MDNSResponder::answerPort(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex) + { + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + stcMDNSServiceQuery::stcAnswer* pSQAnswer + = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + return (pSQAnswer ? pSQAnswer->m_u16Port : 0); + } -/* - MDNSResponder::answerTxts + /* + MDNSResponder::hasAnswerTxts + */ + bool MDNSResponder::hasAnswerTxts(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex) + { + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + stcMDNSServiceQuery::stcAnswer* pSQAnswer + = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + return ((pSQAnswer) && (pSQAnswer->m_u32ContentFlags & ServiceQueryAnswerType_Txts)); + } - Returns all TXT items for the given service as a ';'-separated string. - If not already existing; the string is alloced, filled and attached to the answer. + /* + MDNSResponder::answerTxts -*/ -const char* MDNSResponder::answerTxts(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex) -{ + Returns all TXT items for the given service as a ';'-separated string. + If not already existing; the string is allocated, filled and attached to the answer. - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - // Fill m_pcTxts (if not already done) - if ((pSQAnswer) && - (pSQAnswer->m_Txts.m_pTxts) && - (!pSQAnswer->m_pcTxts)) + */ + const char* MDNSResponder::answerTxts(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex) { - - pSQAnswer->m_pcTxts = pSQAnswer->allocTxts(pSQAnswer->m_Txts.c_strLength()); - if (pSQAnswer->m_pcTxts) + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + stcMDNSServiceQuery::stcAnswer* pSQAnswer + = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + // Fill m_pcTxts (if not already done) + if ((pSQAnswer) && (pSQAnswer->m_Txts.m_pTxts) && (!pSQAnswer->m_pcTxts)) { - pSQAnswer->m_Txts.c_str(pSQAnswer->m_pcTxts); + pSQAnswer->m_pcTxts = pSQAnswer->allocTxts(pSQAnswer->m_Txts.c_strLength()); + if (pSQAnswer->m_pcTxts) + { + pSQAnswer->m_Txts.c_str(pSQAnswer->m_pcTxts); + } } + return (pSQAnswer ? pSQAnswer->m_pcTxts : 0); } - return (pSQAnswer ? pSQAnswer->m_pcTxts : 0); -} - -/* - PROBING -*/ -/* - MDNSResponder::setProbeResultCallback - - Set a global callback for probe results. The callback is called, when probing - for the host domain (or a service domain, without specific probe result callback) - failes or succeedes. - In the case of failure, the domain name should be changed via 'setHostname' or 'setServiceName'. - When succeeded, the host or service domain will be announced by the MDNS responder. - -*/ -bool MDNSResponder::setHostProbeResultCallback(MDNSResponder::MDNSHostProbeFn p_fnCallback) -{ + /* + PROBING + */ - m_HostProbeInformation.m_fnHostProbeResultCallback = p_fnCallback; + /* + MDNSResponder::setProbeResultCallback - return true; -} + Set a global callback for probe results. The callback is called, when probing + for the host domain (or a service domain, without specific probe result callback) + fails or succeeds. + In the case of failure, the domain name should be changed via 'setHostname' or + 'setServiceName'. When succeeded, the host or service domain will be announced by the MDNS + responder. -bool MDNSResponder::setHostProbeResultCallback(MDNSHostProbeFn1 pfn) -{ - using namespace std::placeholders; - return setHostProbeResultCallback([this, pfn](const char* p_pcDomainName, bool p_bProbeResult) + */ + bool MDNSResponder::setHostProbeResultCallback(MDNSResponder::MDNSHostProbeFn p_fnCallback) { - pfn(*this, p_pcDomainName, p_bProbeResult); - }); -} + m_HostProbeInformation.m_fnHostProbeResultCallback = p_fnCallback; -/* - MDNSResponder::setServiceProbeResultCallback + return true; + } - Set a service specific callback for probe results. The callback is called, when probing - for the service domain failes or succeedes. - In the case of failure, the service name should be changed via 'setServiceName'. - When succeeded, the service domain will be announced by the MDNS responder. + bool MDNSResponder::setHostProbeResultCallback(MDNSHostProbeFn1 pfn) + { + using namespace std::placeholders; + return setHostProbeResultCallback( + [this, pfn](const char* p_pcDomainName, bool p_bProbeResult) + { + pfn(*this, p_pcDomainName, p_bProbeResult); + }); + } -*/ -bool MDNSResponder::setServiceProbeResultCallback(const MDNSResponder::hMDNSService p_hService, - MDNSResponder::MDNSServiceProbeFn p_fnCallback) -{ + /* + MDNSResponder::setServiceProbeResultCallback - bool bResult = false; + Set a service specific callback for probe results. The callback is called, when probing + for the service domain fails or succeeds. + In the case of failure, the service name should be changed via 'setServiceName'. + When succeeded, the service domain will be announced by the MDNS responder. - stcMDNSService* pService = _findService(p_hService); - if (pService) + */ + bool + MDNSResponder::setServiceProbeResultCallback(const MDNSResponder::hMDNSService p_hService, + MDNSResponder::MDNSServiceProbeFn p_fnCallback) { - pService->m_ProbeInformation.m_fnServiceProbeResultCallback = p_fnCallback; + bool bResult = false; - bResult = true; + stcMDNSService* pService = _findService(p_hService); + if (pService) + { + pService->m_ProbeInformation.m_fnServiceProbeResultCallback = p_fnCallback; + + bResult = true; + } + return bResult; } - return bResult; -} -bool MDNSResponder::setServiceProbeResultCallback(const MDNSResponder::hMDNSService p_hService, - MDNSResponder::MDNSServiceProbeFn1 p_fnCallback) -{ - using namespace std::placeholders; - return setServiceProbeResultCallback(p_hService, [this, p_fnCallback](const char* p_pcServiceName, const hMDNSService p_hMDNSService, bool p_bProbeResult) + bool + MDNSResponder::setServiceProbeResultCallback(const MDNSResponder::hMDNSService p_hService, + MDNSResponder::MDNSServiceProbeFn1 p_fnCallback) { - p_fnCallback(*this, p_pcServiceName, p_hMDNSService, p_bProbeResult); - }); -} - - -/* - MISC -*/ - -/* - MDNSResponder::notifyAPChange - - Should be called, whenever the AP for the MDNS responder changes. - A bit of this is caught by the event callbacks installed in the constructor. + using namespace std::placeholders; + return setServiceProbeResultCallback( + p_hService, + [this, p_fnCallback](const char* p_pcServiceName, const hMDNSService p_hMDNSService, + bool p_bProbeResult) + { + p_fnCallback(*this, p_pcServiceName, p_hMDNSService, p_bProbeResult); + }); + } -*/ -bool MDNSResponder::notifyAPChange(void) -{ + /* + MISC + */ - return _restart(); -} + /* + MDNSResponder::notifyAPChange -/* - MDNSResponder::update + Should be called, whenever the AP for the MDNS responder changes. + A bit of this is caught by the event callbacks installed in the constructor. - Should be called in every 'loop'. + */ + bool MDNSResponder::notifyAPChange(void) + { + return _restart(); + } -*/ -bool MDNSResponder::update(void) -{ - return _process(true); -} + /* + MDNSResponder::update -/* - MDNSResponder::announce + Should be called in every 'loop'. - Should be called, if the 'configuration' changes. Mainly this will be changes in the TXT items... -*/ -bool MDNSResponder::announce(void) -{ + */ + bool MDNSResponder::update(void) + { + return _process(true); + } - return (_announce(true, true)); -} + /* + MDNSResponder::announce -/* - MDNSResponder::enableArduino + Should be called, if the 'configuration' changes. Mainly this will be changes in the TXT + items... + */ + bool MDNSResponder::announce(void) + { + return (_announce(true, true)); + } - Enable the OTA update service. + /* + MDNSResponder::enableArduino -*/ -MDNSResponder::hMDNSService MDNSResponder::enableArduino(uint16_t p_u16Port, - bool p_bAuthUpload /*= false*/) -{ + Enable the OTA update service. - hMDNSService hService = addService(0, "arduino", "tcp", p_u16Port); - if (hService) + */ + MDNSResponder::hMDNSService MDNSResponder::enableArduino(uint16_t p_u16Port, + bool p_bAuthUpload /*= false*/) { - if ((!addServiceTxt(hService, "tcp_check", "no")) || - (!addServiceTxt(hService, "ssh_upload", "no")) || - (!addServiceTxt(hService, "board", STRINGIZE_VALUE_OF(ARDUINO_BOARD))) || - (!addServiceTxt(hService, "auth_upload", (p_bAuthUpload) ? "yes" : "no"))) + hMDNSService hService = addService(0, "arduino", "tcp", p_u16Port); + if (hService) { - - removeService(hService); - hService = 0; + if ((!addServiceTxt(hService, "tcp_check", "no")) + || (!addServiceTxt(hService, "ssh_upload", "no")) + || (!addServiceTxt(hService, "board", ARDUINO_BOARD_ID)) + || (!addServiceTxt(hService, "auth_upload", (p_bAuthUpload) ? "yes" : "no"))) + { + removeService(hService); + hService = 0; + } } + return hService; } - return hService; -} -/* + /* - MULTICAST GROUPS + MULTICAST GROUPS -*/ + */ -/* - MDNSResponder::_joinMulticastGroups -*/ -bool MDNSResponder::_joinMulticastGroups(void) -{ - bool bResult = false; - - // Join multicast group(s) - for (netif* pNetIf = netif_list; pNetIf; pNetIf = pNetIf->next) + /* + MDNSResponder::_joinMulticastGroups + */ + bool MDNSResponder::_joinMulticastGroups(void) { - if (netif_is_up(pNetIf)) + bool bResult = false; + + // Join multicast group(s) + for (netif* pNetIf = netif_list; pNetIf; pNetIf = pNetIf->next) { -#ifdef MDNS_IP4_SUPPORT - ip_addr_t multicast_addr_V4 = DNS_MQUERY_IPV4_GROUP_INIT; - if (!(pNetIf->flags & NETIF_FLAG_IGMP)) + if (netif_is_up(pNetIf) && IPAddress(pNetIf->ip_addr).isSet()) { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _createHost: Setting flag: flags & NETIF_FLAG_IGMP\n"));); - pNetIf->flags |= NETIF_FLAG_IGMP; - - if (ERR_OK != igmp_start(pNetIf)) +#ifdef MDNS_IP4_SUPPORT + ip_addr_t multicast_addr_V4 = DNS_MQUERY_IPV4_GROUP_INIT; + if (!(pNetIf->flags & NETIF_FLAG_IGMP)) { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _createHost: igmp_start FAILED!\n"));); + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR( + "[MDNSResponder] _createHost: Setting flag: flags & NETIF_FLAG_IGMP\n"));); + pNetIf->flags |= NETIF_FLAG_IGMP; + + if (ERR_OK != igmp_start(pNetIf)) + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _createHost: igmp_start FAILED!\n"));); + } } - } - if ((ERR_OK == igmp_joingroup_netif(pNetIf, ip_2_ip4(&multicast_addr_V4)))) - { - bResult = true; - } - else - { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _createHost: igmp_joingroup_netif(" NETIFID_STR ": %s) FAILED!\n"), - NETIFID_VAL(pNetIf), IPAddress(multicast_addr_V4).toString().c_str());); - } + if ((ERR_OK == igmp_joingroup_netif(pNetIf, ip_2_ip4(&multicast_addr_V4)))) + { + bResult = true; + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _createHost: igmp_joingroup_netif(" NETIFID_STR + ": %s) FAILED!\n"), + NETIFID_VAL(pNetIf), IPAddress(multicast_addr_V4).toString().c_str());); + } #endif #ifdef MDNS_IPV6_SUPPORT - ip_addr_t multicast_addr_V6 = DNS_MQUERY_IPV6_GROUP_INIT; - bResult = ((bResult) && - (ERR_OK == mld6_joingroup_netif(pNetIf, ip_2_ip6(&multicast_addr_V6)))); - DEBUG_EX_ERR_IF(!bResult, DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _createHost: mld6_joingroup_netif (" NETIFID_STR ") FAILED!\n"), - NETIFID_VAL(pNetIf))); + ip_addr_t multicast_addr_V6 = DNS_MQUERY_IPV6_GROUP_INIT; + bResult + = ((bResult) + && (ERR_OK == mld6_joingroup_netif(pNetIf, ip_2_ip6(&multicast_addr_V6)))); + DEBUG_EX_ERR_IF( + !bResult, + DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _createHost: mld6_joingroup_netif (" NETIFID_STR + ") FAILED!\n"), + NETIFID_VAL(pNetIf))); #endif + } } + return bResult; } - return bResult; -} - -/* - clsLEAmDNS2_Host::_leaveMulticastGroups -*/ -bool MDNSResponder::_leaveMulticastGroups() -{ - bool bResult = false; - for (netif* pNetIf = netif_list; pNetIf; pNetIf = pNetIf->next) + /* + clsLEAmDNS2_Host::_leaveMulticastGroups + */ + bool MDNSResponder::_leaveMulticastGroups() { - if (netif_is_up(pNetIf)) + bool bResult = false; + + for (netif* pNetIf = netif_list; pNetIf; pNetIf = pNetIf->next) { - bResult = true; + if (netif_is_up(pNetIf) && IPAddress(pNetIf->ip_addr).isSet()) + { + bResult = true; - // Leave multicast group(s) + // Leave multicast group(s) #ifdef MDNS_IP4_SUPPORT - ip_addr_t multicast_addr_V4 = DNS_MQUERY_IPV4_GROUP_INIT; - if (ERR_OK != igmp_leavegroup_netif(pNetIf, ip_2_ip4(&multicast_addr_V4))) - { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("\n"));); - } + ip_addr_t multicast_addr_V4 = DNS_MQUERY_IPV4_GROUP_INIT; + if (ERR_OK != igmp_leavegroup_netif(pNetIf, ip_2_ip4(&multicast_addr_V4))) + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("\n"));); + } #endif #ifdef MDNS_IPV6_SUPPORT - ip_addr_t multicast_addr_V6 = DNS_MQUERY_IPV6_GROUP_INIT; - if (ERR_OK != mld6_leavegroup_netif(pNetIf, ip_2_ip6(&multicast_addr_V6)/*&(multicast_addr_V6.u_addr.ip6)*/)) - { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("\n"));); - } + ip_addr_t multicast_addr_V6 = DNS_MQUERY_IPV6_GROUP_INIT; + if (ERR_OK + != mld6_leavegroup_netif( + pNetIf, ip_2_ip6(&multicast_addr_V6) /*&(multicast_addr_V6.u_addr.ip6)*/)) + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("\n"));); + } #endif + } } + return bResult; } - return bResult; -} - - - -} //namespace MDNSImplementation - -} //namespace esp8266 +} // namespace MDNSImplementation +} // namespace esp8266 diff --git a/libraries/ESP8266mDNS/src/LEAmDNS.h b/libraries/ESP8266mDNS/src/LEAmDNS.h index bf2f1e9832..3fc4dd98da 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS.h +++ b/libraries/ESP8266mDNS/src/LEAmDNS.h @@ -8,18 +8,22 @@ Essentially, this is an rewrite of the original EPS8266 Multicast DNS code (ESP8266mDNS). The target of this rewrite was to keep the existing interface as stable as possible while adding and extending the supported set of mDNS features. - A lot of the additions were basicly taken from Erik Ekman's lwIP mdns app code. + A lot of the additions were basically taken from Erik Ekman's lwIP mdns app code. Supported mDNS features (in some cases somewhat limited): - - Presenting a DNS-SD service to interested observers, eg. a http server by presenting _http._tcp service - - Support for multi-level compressed names in input; in output only a very simple one-leven full-name compression is implemented + - Presenting a DNS-SD service to interested observers, eg. a http server by presenting + _http._tcp service + - Support for multi-level compressed names in input; in output only a very simple one-leven + full-name compression is implemented - Probing host and service domains for uniqueness in the local network - - Tiebreaking while probing is supported in a very minimalistic way (the 'higher' IP address wins the tiebreak) + - Tiebreaking while probing is supported in a very minimalistic way (the 'higher' IP address + wins the tiebreak) - Announcing available services after successful probing - Using fixed service TXT items or - Using dynamic service TXT items for presented services (via callback) - Remove services (and un-announcing them to the observers by sending goodbye-messages) - - Static queries for DNS-SD services (creating a fixed answer set after a certain timeout period) + - Static queries for DNS-SD services (creating a fixed answer set after a certain timeout + period) - Dynamic queries for DNS-SD services with cached and updated answers and user notifications @@ -30,19 +34,23 @@ For presenting services: In 'setup()': - Install a callback for the probing of host (and service) domains via 'MDNS.setProbeResultCallback(probeResultCallback, &userData);' - Register DNS-SD services with 'MDNSResponder::hMDNSService hService = MDNS.addService("MyESP", "http", "tcp", 5000);' - (Install additional callbacks for the probing of these service domains via 'MDNS.setServiceProbeResultCallback(hService, probeResultCallback, &userData);') - Add service TXT items with 'MDNS.addServiceTxt(hService, "c#", "1");' or by installing a service TXT callback - using 'MDNS.setDynamicServiceTxtCallback(dynamicServiceTxtCallback, &userData);' or service specific - 'MDNS.setDynamicServiceTxtCallback(hService, dynamicServiceTxtCallback, &userData);' + Install a callback for the probing of host (and service) domains via + 'MDNS.setProbeResultCallback(probeResultCallback, &userData);' Register DNS-SD services with + 'MDNSResponder::hMDNSService hService = MDNS.addService("MyESP", "http", "tcp", 5000);' (Install + additional callbacks for the probing of these service domains via + 'MDNS.setServiceProbeResultCallback(hService, probeResultCallback, &userData);') Add service TXT + items with 'MDNS.addServiceTxt(hService, "c#", "1");' or by installing a service TXT callback + using 'MDNS.setDynamicServiceTxtCallback(dynamicServiceTxtCallback, &userData);' or service + specific 'MDNS.setDynamicServiceTxtCallback(hService, dynamicServiceTxtCallback, &userData);' Call MDNS.begin("MyHostname"); - In 'probeResultCallback(MDNSResponder* p_MDNSResponder, const char* p_pcDomain, MDNSResponder:hMDNSService p_hService, bool p_bProbeResult, void* p_pUserdata)': - Check the probe result and update the host or service domain name if the probe failed + In 'probeResultCallback(MDNSResponder* p_MDNSResponder, const char* p_pcDomain, + MDNSResponder:hMDNSService p_hService, bool p_bProbeResult, void* p_pUserdata)': Check the probe + result and update the host or service domain name if the probe failed - In 'dynamicServiceTxtCallback(MDNSResponder* p_MDNSResponder, const hMDNSService p_hService, void* p_pUserdata)': - Add dynamic TXT items by calling 'MDNS.addDynamicServiceTxt(p_hService, "c#", "1");' + In 'dynamicServiceTxtCallback(MDNSResponder* p_MDNSResponder, const hMDNSService p_hService, + void* p_pUserdata)': Add dynamic TXT items by calling 'MDNS.addDynamicServiceTxt(p_hService, + "c#", "1");' In loop(): Call 'MDNS.update();' @@ -51,15 +59,17 @@ For querying services: Static: Call 'uint32_t u32AnswerCount = MDNS.queryService("http", "tcp");' - Iterate answers by: 'for (uint32_t u=0; u // for UdpContext.h +#include // for UdpContext.h #include "WiFiUdp.h" #include "lwip/udp.h" #include "debug.h" @@ -111,10 +121,8 @@ #include #include - #include "ESP8266WiFi.h" - namespace esp8266 { @@ -124,1338 +132,1261 @@ 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 #endif #ifdef MDNS_IP4_SUPPORT -#define MDNS_IP4_SIZE 4 +#define MDNS_IP4_SIZE 4 #endif #ifdef MDNS_IP6_SUPPORT -#define MDNS_IP6_SIZE 16 +#define MDNS_IP6_SIZE 16 #endif /* Maximum length for all service txts for one service */ -#define MDNS_SERVICE_TXT_MAXLENGTH 1300 +#define MDNS_SERVICE_TXT_MAXLENGTH 1300 /* Maximum length for a full domain name eg. MyESP._http._tcp.local */ -#define MDNS_DOMAIN_MAXLENGTH 256 +#define MDNS_DOMAIN_MAXLENGTH 256 /* Maximum length of on label in a domain name (length info fits into 6 bits) */ -#define MDNS_DOMAIN_LABEL_MAXLENGTH 63 +#define MDNS_DOMAIN_LABEL_MAXLENGTH 63 /* Maximum length of a service name eg. http */ -#define MDNS_SERVICE_NAME_LENGTH 15 +#define MDNS_SERVICE_NAME_LENGTH 15 /* Maximum length of a service protocol name eg. tcp */ -#define MDNS_SERVICE_PROTOCOL_LENGTH 3 +#define MDNS_SERVICE_PROTOCOL_LENGTH 3 /* Default timeout for static service queries */ -#define MDNS_QUERYSERVICES_WAIT_TIME 1000 +#define MDNS_QUERYSERVICES_WAIT_TIME 1000 /* Timeout for udpContext->sendtimeout() */ -#define MDNS_UDPCONTEXT_TIMEOUT 50 - -/** - MDNSResponder -*/ -class MDNSResponder -{ -public: - /* INTERFACE */ - - MDNSResponder(void); - virtual ~MDNSResponder(void); - - // Start the MDNS responder by setting the default hostname - // Later call MDNS::update() in every 'loop' to run the process loop - // (probing, announcing, responding, ...) - // if interfaceAddress is not specified, default interface is STA, or AP when STA is not set - bool begin(const char* p_pcHostname, const IPAddress& p_IPAddress = INADDR_ANY, uint32_t p_u32TTL = 120 /*ignored*/); - bool begin(const String& p_strHostname, const IPAddress& p_IPAddress = INADDR_ANY, uint32_t p_u32TTL = 120 /*ignored*/) - { - return begin(p_strHostname.c_str(), p_IPAddress, p_u32TTL); - } - bool _joinMulticastGroups(void); - bool _leaveMulticastGroups(void); - - // Finish MDNS processing - bool close(void); - // for esp32 compatability - bool end(void); - // Change hostname (probing is restarted) - bool setHostname(const char* p_pcHostname); - // for compatibility... - bool setHostname(const String& p_strHostname); - - bool isRunning(void) - { - return (m_pUDPContext != 0); - } +#define MDNS_UDPCONTEXT_TIMEOUT 50 /** - hMDNSService (opaque handle to access the service) + MDNSResponder */ - typedef const void* hMDNSService; - - // Add a new service to the MDNS responder. If no name (instance name) is given (p_pcName = 0) - // the current hostname is used. If the hostname is changed later, the instance names for - // these 'auto-named' services are changed to the new name also (and probing is restarted). - // The usual '_' before p_pcService (eg. http) and protocol (eg. tcp) may be given. - hMDNSService addService(const char* p_pcName, - const char* p_pcService, - const char* p_pcProtocol, - uint16_t p_u16Port); - // Removes a service from the MDNS responder - bool removeService(const hMDNSService p_hService); - bool removeService(const char* p_pcInstanceName, - const char* p_pcServiceName, - const char* p_pcProtocol); - // for compatibility... - bool addService(const String& p_strServiceName, - const String& p_strProtocol, - uint16_t p_u16Port); - - - // Change the services instance name (and restart probing). - bool setServiceName(const hMDNSService p_hService, - const char* p_pcInstanceName); - //for compatibility - //Warning: this has the side effect of changing the hostname. - //TODO: implement instancename different from hostname - void setInstanceName(const char* p_pcHostname) - { - setHostname(p_pcHostname); - } - // for esp32 compatibilty - void setInstanceName(const String& s_pcHostname) + class MDNSResponder { - setInstanceName(s_pcHostname.c_str()); - } + public: + /* INTERFACE */ + + MDNSResponder(void); + virtual ~MDNSResponder(void); + + // Start the MDNS responder by setting the default hostname + // Later call MDNS::update() in every 'loop' to run the process loop + // (probing, announcing, responding, ...) + // if interfaceAddress is not specified, default interface is STA, or AP when STA is not set + bool begin(const char* p_pcHostname, const IPAddress& p_IPAddress = INADDR_ANY, + uint32_t p_u32TTL = 120 /*ignored*/); + bool begin(const String& p_strHostname, const IPAddress& p_IPAddress = INADDR_ANY, + uint32_t p_u32TTL = 120 /*ignored*/) + { + return begin(p_strHostname.c_str(), p_IPAddress, p_u32TTL); + } + bool _joinMulticastGroups(void); + bool _leaveMulticastGroups(void); + + // Finish MDNS processing + bool close(void); + // for esp32 compatibility + bool end(void); + // Change hostname (probing is restarted) + bool setHostname(const char* p_pcHostname); + // for compatibility... + bool setHostname(const String& p_strHostname); + + bool isRunning(void) + { + return (m_pUDPContext != 0); + } - /** - hMDNSTxt (opaque handle to access the TXT items) - */ - typedef void* hMDNSTxt; + /** + hMDNSService (opaque handle to access the service) + */ + typedef const void* hMDNSService; + + // Add a new service to the MDNS responder. If no name (instance name) is given (p_pcName = + // 0) the current hostname is used. If the hostname is changed later, the instance names for + // these 'auto-named' services are changed to the new name also (and probing is restarted). + // The usual '_' before p_pcService (eg. http) and protocol (eg. tcp) may be given. + hMDNSService addService(const char* p_pcName, const char* p_pcService, + const char* p_pcProtocol, uint16_t p_u16Port); + // Removes a service from the MDNS responder + bool removeService(const hMDNSService p_hService); + bool removeService(const char* p_pcInstanceName, const char* p_pcServiceName, + const char* p_pcProtocol); + // for compatibility... + bool addService(const String& p_strServiceName, const String& p_strProtocol, + uint16_t p_u16Port); + + // Change the services instance name (and restart probing). + bool setServiceName(const hMDNSService p_hService, const char* p_pcInstanceName); + // for compatibility + // Warning: this has the side effect of changing the hostname. + // TODO: implement instancename different from hostname + void setInstanceName(const char* p_pcHostname) + { + setHostname(p_pcHostname); + } + // for esp32 compatibility + void setInstanceName(const String& s_pcHostname) + { + setInstanceName(s_pcHostname.c_str()); + } - // Add a (static) MDNS TXT item ('key' = 'value') to the service - hMDNSTxt addServiceTxt(const hMDNSService p_hService, - const char* p_pcKey, + /** + hMDNSTxt (opaque handle to access the TXT items) + */ + typedef void* hMDNSTxt; + + // Add a (static) MDNS TXT item ('key' = 'value') to the service + hMDNSTxt addServiceTxt(const hMDNSService p_hService, const char* p_pcKey, + const char* p_pcValue); + hMDNSTxt addServiceTxt(const hMDNSService p_hService, const char* p_pcKey, + uint32_t p_u32Value); + hMDNSTxt addServiceTxt(const hMDNSService p_hService, const char* p_pcKey, + uint16_t p_u16Value); + hMDNSTxt addServiceTxt(const hMDNSService p_hService, const char* p_pcKey, + uint8_t p_u8Value); + hMDNSTxt addServiceTxt(const hMDNSService p_hService, const char* p_pcKey, + int32_t p_i32Value); + hMDNSTxt addServiceTxt(const hMDNSService p_hService, const char* p_pcKey, + int16_t p_i16Value); + hMDNSTxt addServiceTxt(const hMDNSService p_hService, const char* p_pcKey, + int8_t p_i8Value); + + // Remove an existing (static) MDNS TXT item from the service + bool removeServiceTxt(const hMDNSService p_hService, const hMDNSTxt p_hTxt); + bool removeServiceTxt(const hMDNSService p_hService, const char* p_pcKey); + bool removeServiceTxt(const char* p_pcinstanceName, const char* p_pcServiceName, + const char* p_pcProtocol, const char* p_pcKey); + // for compatibility... + bool addServiceTxt(const char* p_pcService, const char* p_pcProtocol, const char* p_pcKey, const char* p_pcValue); - hMDNSTxt addServiceTxt(const hMDNSService p_hService, - const char* p_pcKey, - uint32_t p_u32Value); - hMDNSTxt addServiceTxt(const hMDNSService p_hService, - const char* p_pcKey, - uint16_t p_u16Value); - hMDNSTxt addServiceTxt(const hMDNSService p_hService, - const char* p_pcKey, - uint8_t p_u8Value); - hMDNSTxt addServiceTxt(const hMDNSService p_hService, - const char* p_pcKey, - int32_t p_i32Value); - hMDNSTxt addServiceTxt(const hMDNSService p_hService, - const char* p_pcKey, - int16_t p_i16Value); - hMDNSTxt addServiceTxt(const hMDNSService p_hService, - const char* p_pcKey, - int8_t p_i8Value); - - // Remove an existing (static) MDNS TXT item from the service - bool removeServiceTxt(const hMDNSService p_hService, - const hMDNSTxt p_hTxt); - bool removeServiceTxt(const hMDNSService p_hService, - const char* p_pcKey); - bool removeServiceTxt(const char* p_pcinstanceName, - const char* p_pcServiceName, - const char* p_pcProtocol, - const char* p_pcKey); - // for compatibility... - bool addServiceTxt(const char* p_pcService, - const char* p_pcProtocol, - const char* p_pcKey, - const char* p_pcValue); - bool addServiceTxt(const String& p_strService, - const String& p_strProtocol, - const String& p_strKey, - const String& p_strValue); + bool addServiceTxt(const String& p_strService, const String& p_strProtocol, + const String& p_strKey, const String& p_strValue); - /** - MDNSDynamicServiceTxtCallbackFn - Callback function for dynamic MDNS TXT items - */ + /** + MDNSDynamicServiceTxtCallbackFn + Callback function for dynamic MDNS TXT items + */ - typedef std::function MDNSDynamicServiceTxtCallbackFunc; - - // Set a global callback for dynamic MDNS TXT items. The callback function is called - // every time, a TXT item is needed for one of the installed services. - bool setDynamicServiceTxtCallback(MDNSDynamicServiceTxtCallbackFunc p_fnCallback); - // Set a service specific callback for dynamic MDNS TXT items. The callback function - // is called every time, a TXT item is needed for the given service. - bool setDynamicServiceTxtCallback(const hMDNSService p_hService, - MDNSDynamicServiceTxtCallbackFunc p_fnCallback); - - // Add a (dynamic) MDNS TXT item ('key' = 'value') to the service - // Dynamic TXT items are removed right after one-time use. So they need to be added - // every time the value s needed (via callback). - hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, - const char* p_pcKey, - const char* p_pcValue); - hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, - const char* p_pcKey, - uint32_t p_u32Value); - hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, - const char* p_pcKey, - uint16_t p_u16Value); - hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, - const char* p_pcKey, - uint8_t p_u8Value); - hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, - const char* p_pcKey, - int32_t p_i32Value); - hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, - const char* p_pcKey, - int16_t p_i16Value); - hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, - const char* p_pcKey, - int8_t p_i8Value); - - // Perform a (static) service query. The function returns after p_u16Timeout milliseconds - // The answers (the number of received answers is returned) can be retrieved by calling - // - answerHostname (or hostname) - // - answerIP (or IP) - // - answerPort (or port) - uint32_t queryService(const char* p_pcService, - const char* p_pcProtocol, - const uint16_t p_u16Timeout = MDNS_QUERYSERVICES_WAIT_TIME); - bool removeQuery(void); - // for compatibility... - uint32_t queryService(const String& p_strService, - const String& p_strProtocol); - - const char* answerHostname(const uint32_t p_u32AnswerIndex); - IPAddress answerIP(const uint32_t p_u32AnswerIndex); - uint16_t answerPort(const uint32_t p_u32AnswerIndex); - // for compatibility... - String hostname(const uint32_t p_u32AnswerIndex); - IPAddress IP(const uint32_t p_u32AnswerIndex); - uint16_t port(const uint32_t p_u32AnswerIndex); + typedef std::function + MDNSDynamicServiceTxtCallbackFunc; + + // Set a global callback for dynamic MDNS TXT items. The callback function is called + // every time, a TXT item is needed for one of the installed services. + bool setDynamicServiceTxtCallback(MDNSDynamicServiceTxtCallbackFunc p_fnCallback); + // Set a service specific callback for dynamic MDNS TXT items. The callback function + // is called every time, a TXT item is needed for the given service. + bool setDynamicServiceTxtCallback(const hMDNSService p_hService, + MDNSDynamicServiceTxtCallbackFunc p_fnCallback); + + // Add a (dynamic) MDNS TXT item ('key' = 'value') to the service + // Dynamic TXT items are removed right after one-time use. So they need to be added + // every time the value s needed (via callback). + hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, const char* p_pcKey, + const char* p_pcValue); + hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, const char* p_pcKey, + uint32_t p_u32Value); + hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, const char* p_pcKey, + uint16_t p_u16Value); + hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, const char* p_pcKey, + uint8_t p_u8Value); + hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, const char* p_pcKey, + int32_t p_i32Value); + hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, const char* p_pcKey, + int16_t p_i16Value); + hMDNSTxt addDynamicServiceTxt(hMDNSService p_hService, const char* p_pcKey, + int8_t p_i8Value); + + // Perform a (static) service query. The function returns after p_u16Timeout milliseconds + // The answers (the number of received answers is returned) can be retrieved by calling + // - answerHostname (or hostname) + // - answerIP (or IP) + // - answerPort (or port) + uint32_t queryService(const char* p_pcService, const char* p_pcProtocol, + const uint16_t p_u16Timeout = MDNS_QUERYSERVICES_WAIT_TIME); + bool removeQuery(void); + // for compatibility... + uint32_t queryService(const String& p_strService, const String& p_strProtocol); + + const char* answerHostname(const uint32_t p_u32AnswerIndex); + IPAddress answerIP(const uint32_t p_u32AnswerIndex); + uint16_t answerPort(const uint32_t p_u32AnswerIndex); + // for compatibility... + String hostname(const uint32_t p_u32AnswerIndex); + IPAddress IP(const uint32_t p_u32AnswerIndex); + uint16_t port(const uint32_t p_u32AnswerIndex); - /** - hMDNSServiceQuery (opaque handle to access dynamic service queries) - */ - typedef const void* hMDNSServiceQuery; + /** + hMDNSServiceQuery (opaque handle to access dynamic service queries) + */ + typedef const void* hMDNSServiceQuery; - /** - enuServiceQueryAnswerType - */ - typedef enum _enuServiceQueryAnswerType - { - ServiceQueryAnswerType_ServiceDomain = (1 << 0), // Service instance name - ServiceQueryAnswerType_HostDomainAndPort = (1 << 1), // Host domain and service port - ServiceQueryAnswerType_Txts = (1 << 2), // TXT items + /** + enuServiceQueryAnswerType + */ + typedef enum _enuServiceQueryAnswerType + { + ServiceQueryAnswerType_ServiceDomain = (1 << 0), // Service instance name + ServiceQueryAnswerType_HostDomainAndPort = (1 << 1), // Host domain and service port + ServiceQueryAnswerType_Txts = (1 << 2), // TXT items #ifdef MDNS_IP4_SUPPORT - ServiceQueryAnswerType_IP4Address = (1 << 3), // IP4 address + ServiceQueryAnswerType_IP4Address = (1 << 3), // IP4 address #endif #ifdef MDNS_IP6_SUPPORT - ServiceQueryAnswerType_IP6Address = (1 << 4), // IP6 address + ServiceQueryAnswerType_IP6Address = (1 << 4), // IP6 address #endif - } enuServiceQueryAnswerType; + } enuServiceQueryAnswerType; - enum class AnswerType : uint32_t - { - Unknown = 0, - ServiceDomain = ServiceQueryAnswerType_ServiceDomain, - HostDomainAndPort = ServiceQueryAnswerType_HostDomainAndPort, - Txt = ServiceQueryAnswerType_Txts, + enum class AnswerType : uint32_t + { + Unknown = 0, + ServiceDomain = ServiceQueryAnswerType_ServiceDomain, + HostDomainAndPort = ServiceQueryAnswerType_HostDomainAndPort, + Txt = ServiceQueryAnswerType_Txts, #ifdef MDNS_IP4_SUPPORT - IP4Address = ServiceQueryAnswerType_IP4Address, + IP4Address = ServiceQueryAnswerType_IP4Address, #endif #ifdef MDNS_IP6_SUPPORT - IP6Address = ServiceQueryAnswerType_IP6Address, + IP6Address = ServiceQueryAnswerType_IP6Address, #endif - }; + }; - /** - MDNSServiceQueryCallbackFn - Callback function for received answers for dynamic service queries - */ - struct MDNSServiceInfo; // forward declaration - typedef std::function MDNSServiceQueryCallbackFunc; - - // Install a dynamic service query. For every received answer (part) the given callback - // function is called. The query will be updated every time, the TTL for an answer - // has timed-out. - // The answers can also be retrieved by calling - // - answerCount - // - answerServiceDomain - // - hasAnswerHostDomain/answerHostDomain - // - hasAnswerIP4Address/answerIP4Address - // - hasAnswerIP6Address/answerIP6Address - // - hasAnswerPort/answerPort - // - hasAnswerTxts/answerTxts - hMDNSServiceQuery installServiceQuery(const char* p_pcService, - const char* p_pcProtocol, - MDNSServiceQueryCallbackFunc p_fnCallback); - // Remove a dynamic service query - bool removeServiceQuery(hMDNSServiceQuery p_hServiceQuery); - - uint32_t answerCount(const hMDNSServiceQuery p_hServiceQuery); - std::vector answerInfo(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery); - - const char* answerServiceDomain(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - bool hasAnswerHostDomain(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - const char* answerHostDomain(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); + /** + MDNSServiceQueryCallbackFn + Callback function for received answers for dynamic service queries + */ + struct MDNSServiceInfo; // forward declaration + typedef std::function + MDNSServiceQueryCallbackFunc; + + // Install a dynamic service query. For every received answer (part) the given callback + // function is called. The query will be updated every time, the TTL for an answer + // has timed-out. + // The answers can also be retrieved by calling + // - answerCount + // - answerServiceDomain + // - hasAnswerHostDomain/answerHostDomain + // - hasAnswerIP4Address/answerIP4Address + // - hasAnswerIP6Address/answerIP6Address + // - hasAnswerPort/answerPort + // - hasAnswerTxts/answerTxts + hMDNSServiceQuery installServiceQuery(const char* p_pcService, const char* p_pcProtocol, + MDNSServiceQueryCallbackFunc p_fnCallback); + // Remove a dynamic service query + bool removeServiceQuery(hMDNSServiceQuery p_hServiceQuery); + + uint32_t answerCount(const hMDNSServiceQuery p_hServiceQuery); + std::vector + answerInfo(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery); + + const char* answerServiceDomain(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + bool hasAnswerHostDomain(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + const char* answerHostDomain(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); #ifdef MDNS_IP4_SUPPORT - bool hasAnswerIP4Address(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - uint32_t answerIP4AddressCount(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - IPAddress answerIP4Address(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex, - const uint32_t p_u32AddressIndex); + bool hasAnswerIP4Address(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + uint32_t answerIP4AddressCount(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + IPAddress answerIP4Address(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex, + const uint32_t p_u32AddressIndex); #endif #ifdef MDNS_IP6_SUPPORT - bool hasAnswerIP6Address(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - uint32_t answerIP6AddressCount(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - IPAddress answerIP6Address(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex, - const uint32_t p_u32AddressIndex); + bool hasAnswerIP6Address(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + uint32_t answerIP6AddressCount(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + IPAddress answerIP6Address(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex, + const uint32_t p_u32AddressIndex); #endif - bool hasAnswerPort(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - uint16_t answerPort(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - bool hasAnswerTxts(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - // Get the TXT items as a ';'-separated string - const char* answerTxts(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); + bool hasAnswerPort(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + uint16_t answerPort(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + bool hasAnswerTxts(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + // Get the TXT items as a ';'-separated string + const char* answerTxts(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); - /** - MDNSProbeResultCallbackFn - Callback function for (host and service domain) probe results - */ - typedef std::function MDNSHostProbeFn; - - typedef std::function MDNSHostProbeFn1; - - typedef std::function MDNSServiceProbeFn; - - typedef std::function MDNSServiceProbeFn1; - - // Set a global callback function for host and service probe results - // The callback function is called, when the probing for the host domain - // (or a service domain, which hasn't got a service specific callback) - // Succeeds or fails. - // In case of failure, the failed domain name should be changed. - bool setHostProbeResultCallback(MDNSHostProbeFn p_fnCallback); - bool setHostProbeResultCallback(MDNSHostProbeFn1 p_fnCallback); - - // Set a service specific probe result callback - bool setServiceProbeResultCallback(const MDNSResponder::hMDNSService p_hService, - MDNSServiceProbeFn p_fnCallback); - bool setServiceProbeResultCallback(const MDNSResponder::hMDNSService p_hService, - MDNSServiceProbeFn1 p_fnCallback); - - // Application should call this whenever AP is configured/disabled - bool notifyAPChange(void); - - // 'update' should be called in every 'loop' to run the MDNS processing - bool update(void); - - // 'announce' can be called every time, the configuration of some service - // changes. Mainly, this would be changed content of TXT items. - bool announce(void); - - // Enable OTA update - hMDNSService enableArduino(uint16_t p_u16Port, - bool p_bAuthUpload = false); - - // Domain name helper - static bool indexDomain(char*& p_rpcDomain, - const char* p_pcDivider = "-", - const char* p_pcDefaultDomain = 0); - - /** STRUCTS **/ - -public: - /** - MDNSServiceInfo, used in application callbacks - */ - struct MDNSServiceInfo - { - MDNSServiceInfo(MDNSResponder& p_pM, MDNSResponder::hMDNSServiceQuery p_hS, uint32_t p_u32A) - : p_pMDNSResponder(p_pM), - p_hServiceQuery(p_hS), - p_u32AnswerIndex(p_u32A) - {}; - struct CompareKey + /** + MDNSProbeResultCallbackFn + Callback function for (host and service domain) probe results + */ + typedef std::function + MDNSHostProbeFn; + + typedef std::function + MDNSHostProbeFn1; + + typedef std::function + MDNSServiceProbeFn; + + typedef std::function + MDNSServiceProbeFn1; + + // Set a global callback function for host and service probe results + // The callback function is called, when the probing for the host domain + // (or a service domain, which hasn't got a service specific callback) + // Succeeds or fails. + // In case of failure, the failed domain name should be changed. + bool setHostProbeResultCallback(MDNSHostProbeFn p_fnCallback); + bool setHostProbeResultCallback(MDNSHostProbeFn1 p_fnCallback); + + // Set a service specific probe result callback + bool setServiceProbeResultCallback(const MDNSResponder::hMDNSService p_hService, + MDNSServiceProbeFn p_fnCallback); + bool setServiceProbeResultCallback(const MDNSResponder::hMDNSService p_hService, + MDNSServiceProbeFn1 p_fnCallback); + + // Application should call this whenever AP is configured/disabled + bool notifyAPChange(void); + + // 'update' should be called in every 'loop' to run the MDNS processing + bool update(void); + + // 'announce' can be called every time, the configuration of some service + // changes. Mainly, this would be changed content of TXT items. + bool announce(void); + + // Enable OTA update + hMDNSService enableArduino(uint16_t p_u16Port, bool p_bAuthUpload = false); + + // Domain name helper + static bool indexDomain(char*& p_rpcDomain, const char* p_pcDivider = "-", + const char* p_pcDefaultDomain = 0); + + /** STRUCTS **/ + + public: + /** + MDNSServiceInfo, used in application callbacks + */ + struct MDNSServiceInfo { - bool operator()(char const *a, char const *b) const + MDNSServiceInfo(MDNSResponder& p_pM, MDNSResponder::hMDNSServiceQuery p_hS, + uint32_t 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 + { + return strcmp(a, b) < 0; + } + }; + using KeyValueMap = std::map; + + protected: + MDNSResponder& p_pMDNSResponder; + MDNSResponder::hMDNSServiceQuery p_hServiceQuery; + uint32_t p_u32AnswerIndex; + KeyValueMap keyValueMap; + + public: + const char* serviceDomain() { - return strcmp(a, b) < 0; + return p_pMDNSResponder.answerServiceDomain(p_hServiceQuery, p_u32AnswerIndex); + }; + bool hostDomainAvailable() + { + return (p_pMDNSResponder.hasAnswerHostDomain(p_hServiceQuery, p_u32AnswerIndex)); } - }; - using KeyValueMap = std::map; - protected: - MDNSResponder& p_pMDNSResponder; - MDNSResponder::hMDNSServiceQuery p_hServiceQuery; - uint32_t p_u32AnswerIndex; - KeyValueMap keyValueMap; - public: - const char* serviceDomain() - { - return p_pMDNSResponder.answerServiceDomain(p_hServiceQuery, p_u32AnswerIndex); - }; - bool hostDomainAvailable() - { - return (p_pMDNSResponder.hasAnswerHostDomain(p_hServiceQuery, p_u32AnswerIndex)); - } - const char* hostDomain() - { - return (hostDomainAvailable()) ? - p_pMDNSResponder.answerHostDomain(p_hServiceQuery, p_u32AnswerIndex) : nullptr; - }; - bool hostPortAvailable() - { - return (p_pMDNSResponder.hasAnswerPort(p_hServiceQuery, p_u32AnswerIndex)); - } - uint16_t hostPort() - { - return (hostPortAvailable()) ? - p_pMDNSResponder.answerPort(p_hServiceQuery, p_u32AnswerIndex) : 0; - }; - bool IP4AddressAvailable() - { - return (p_pMDNSResponder.hasAnswerIP4Address(p_hServiceQuery, p_u32AnswerIndex)); - } - std::vector IP4Adresses() - { - std::vector internalIP; - if (IP4AddressAvailable()) + const char* hostDomain() + { + return (hostDomainAvailable()) + ? p_pMDNSResponder.answerHostDomain(p_hServiceQuery, p_u32AnswerIndex) + : nullptr; + }; + bool hostPortAvailable() + { + return (p_pMDNSResponder.hasAnswerPort(p_hServiceQuery, p_u32AnswerIndex)); + } + uint16_t hostPort() + { + return (hostPortAvailable()) + ? p_pMDNSResponder.answerPort(p_hServiceQuery, p_u32AnswerIndex) + : 0; + }; + bool IP4AddressAvailable() { - uint16_t cntIP4Adress = p_pMDNSResponder.answerIP4AddressCount(p_hServiceQuery, p_u32AnswerIndex); - for (uint32_t u2 = 0; u2 < cntIP4Adress; ++u2) + return (p_pMDNSResponder.hasAnswerIP4Address(p_hServiceQuery, p_u32AnswerIndex)); + } + std::vector IP4Adresses() + { + std::vector internalIP; + if (IP4AddressAvailable()) { - internalIP.emplace_back(p_pMDNSResponder.answerIP4Address(p_hServiceQuery, p_u32AnswerIndex, u2)); + uint16_t cntIP4Adress + = p_pMDNSResponder.answerIP4AddressCount(p_hServiceQuery, p_u32AnswerIndex); + for (uint32_t u2 = 0; u2 < cntIP4Adress; ++u2) + { + internalIP.emplace_back(p_pMDNSResponder.answerIP4Address( + p_hServiceQuery, p_u32AnswerIndex, u2)); + } } + return internalIP; + }; + bool txtAvailable() + { + return (p_pMDNSResponder.hasAnswerTxts(p_hServiceQuery, p_u32AnswerIndex)); } - return internalIP; - }; - bool txtAvailable() - { - return (p_pMDNSResponder.hasAnswerTxts(p_hServiceQuery, p_u32AnswerIndex)); - } - const char* strKeyValue() - { - return (txtAvailable()) ? - p_pMDNSResponder.answerTxts(p_hServiceQuery, p_u32AnswerIndex) : nullptr; - }; - const KeyValueMap& keyValues() - { - if (txtAvailable() && keyValueMap.size() == 0) + const char* strKeyValue() + { + return (txtAvailable()) + ? p_pMDNSResponder.answerTxts(p_hServiceQuery, p_u32AnswerIndex) + : nullptr; + }; + const KeyValueMap& keyValues() { - for (auto kv = p_pMDNSResponder._answerKeyValue(p_hServiceQuery, p_u32AnswerIndex); kv != nullptr; kv = kv->m_pNext) + if (txtAvailable() && keyValueMap.size() == 0) { - keyValueMap.emplace(std::pair(kv->m_pcKey, kv->m_pcValue)); + for (auto kv + = p_pMDNSResponder._answerKeyValue(p_hServiceQuery, p_u32AnswerIndex); + kv != nullptr; kv = kv->m_pNext) + { + keyValueMap.emplace( + std::pair(kv->m_pcKey, kv->m_pcValue)); + } } + return keyValueMap; } - return keyValueMap; - } - const char* value(const char* key) - { - char* result = nullptr; - - for (stcMDNSServiceTxt* pTxt = p_pMDNSResponder._answerKeyValue(p_hServiceQuery, p_u32AnswerIndex); pTxt; pTxt = pTxt->m_pNext) + const char* value(const char* key) { - if ((key) && - (0 == strcmp(pTxt->m_pcKey, key))) + char* result = nullptr; + + for (stcMDNSServiceTxt* pTxt + = p_pMDNSResponder._answerKeyValue(p_hServiceQuery, p_u32AnswerIndex); + pTxt; pTxt = pTxt->m_pNext) { - result = pTxt->m_pcValue; - break; + if ((key) && (0 == strcmp(pTxt->m_pcKey, key))) + { + result = pTxt->m_pcValue; + break; + } } + return result; } - return result; - } - }; -protected: + }; - /** - stcMDNSServiceTxt - */ - struct stcMDNSServiceTxt - { - stcMDNSServiceTxt* m_pNext; - char* m_pcKey; - char* m_pcValue; - bool m_bTemp; - - stcMDNSServiceTxt(const char* p_pcKey = 0, - const char* p_pcValue = 0, - bool p_bTemp = false); - stcMDNSServiceTxt(const stcMDNSServiceTxt& p_Other); - ~stcMDNSServiceTxt(void); - - stcMDNSServiceTxt& operator=(const stcMDNSServiceTxt& p_Other); - bool clear(void); - - char* allocKey(size_t p_stLength); - bool setKey(const char* p_pcKey, - size_t p_stLength); - bool setKey(const char* p_pcKey); - bool releaseKey(void); - - char* allocValue(size_t p_stLength); - bool setValue(const char* p_pcValue, - size_t p_stLength); - bool setValue(const char* p_pcValue); - bool releaseValue(void); - - bool set(const char* p_pcKey, - const char* p_pcValue, - bool p_bTemp = false); - - bool update(const char* p_pcValue); - - size_t length(void) const; - }; + protected: + /** + stcMDNSServiceTxt + */ + struct stcMDNSServiceTxt + { + stcMDNSServiceTxt* m_pNext; + char* m_pcKey; + char* m_pcValue; + bool m_bTemp; - /** - stcMDNSTxts - */ - struct stcMDNSServiceTxts - { - stcMDNSServiceTxt* m_pTxts; + stcMDNSServiceTxt(const char* p_pcKey = 0, const char* p_pcValue = 0, + bool p_bTemp = false); + stcMDNSServiceTxt(const stcMDNSServiceTxt& p_Other); + ~stcMDNSServiceTxt(void); - stcMDNSServiceTxts(void); - stcMDNSServiceTxts(const stcMDNSServiceTxts& p_Other); - ~stcMDNSServiceTxts(void); + stcMDNSServiceTxt& operator=(const stcMDNSServiceTxt& p_Other); + bool clear(void); - stcMDNSServiceTxts& operator=(const stcMDNSServiceTxts& p_Other); + char* allocKey(size_t p_stLength); + bool setKey(const char* p_pcKey, size_t p_stLength); + bool setKey(const char* p_pcKey); + bool releaseKey(void); - bool clear(void); + char* allocValue(size_t p_stLength); + bool setValue(const char* p_pcValue, size_t p_stLength); + bool setValue(const char* p_pcValue); + bool releaseValue(void); - bool add(stcMDNSServiceTxt* p_pTxt); - bool remove(stcMDNSServiceTxt* p_pTxt); + bool set(const char* p_pcKey, const char* p_pcValue, bool p_bTemp = false); - bool removeTempTxts(void); + bool update(const char* p_pcValue); - stcMDNSServiceTxt* find(const char* p_pcKey); - const stcMDNSServiceTxt* find(const char* p_pcKey) const; - stcMDNSServiceTxt* find(const stcMDNSServiceTxt* p_pTxt); + size_t length(void) const; + }; - uint16_t length(void) const; + /** + stcMDNSTxts + */ + struct stcMDNSServiceTxts + { + stcMDNSServiceTxt* m_pTxts; - size_t c_strLength(void) const; - bool c_str(char* p_pcBuffer); + stcMDNSServiceTxts(void); + stcMDNSServiceTxts(const stcMDNSServiceTxts& p_Other); + ~stcMDNSServiceTxts(void); - size_t bufferLength(void) const; - bool buffer(char* p_pcBuffer); + stcMDNSServiceTxts& operator=(const stcMDNSServiceTxts& p_Other); - bool compare(const stcMDNSServiceTxts& p_Other) const; - bool operator==(const stcMDNSServiceTxts& p_Other) const; - bool operator!=(const stcMDNSServiceTxts& p_Other) const; - }; + bool clear(void); - /** - enuContentFlags - */ - typedef enum _enuContentFlags - { - // Host - ContentFlag_A = 0x01, - ContentFlag_PTR_IP4 = 0x02, - ContentFlag_PTR_IP6 = 0x04, - ContentFlag_AAAA = 0x08, - // Service - ContentFlag_PTR_TYPE = 0x10, - ContentFlag_PTR_NAME = 0x20, - ContentFlag_TXT = 0x40, - ContentFlag_SRV = 0x80, - } enuContentFlags; + bool add(stcMDNSServiceTxt* p_pTxt); + bool remove(stcMDNSServiceTxt* p_pTxt); - /** - stcMDNS_MsgHeader - */ - struct stcMDNS_MsgHeader - { - uint16_t m_u16ID; // Identifier - bool m_1bQR : 1; // Query/Response flag - unsigned char m_4bOpcode : 4; // Operation code - bool m_1bAA : 1; // Authoritative Answer flag - bool m_1bTC : 1; // Truncation flag - bool m_1bRD : 1; // Recursion desired - bool m_1bRA : 1; // Recursion available - unsigned char m_3bZ : 3; // Zero - unsigned char m_4bRCode : 4; // Response code - uint16_t m_u16QDCount; // Question count - uint16_t m_u16ANCount; // Answer count - uint16_t m_u16NSCount; // Authority Record count - uint16_t m_u16ARCount; // Additional Record count - - stcMDNS_MsgHeader(uint16_t p_u16ID = 0, - bool p_bQR = false, - unsigned char p_ucOpcode = 0, - bool p_bAA = false, - bool p_bTC = false, - bool p_bRD = false, - 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); - }; + bool removeTempTxts(void); - /** - stcMDNS_RRDomain - */ - struct stcMDNS_RRDomain - { - char m_acName[MDNS_DOMAIN_MAXLENGTH]; // Encoded domain name - uint16_t m_u16NameLength; // Length (incl. '\0') + stcMDNSServiceTxt* find(const char* p_pcKey); + const stcMDNSServiceTxt* find(const char* p_pcKey) const; + stcMDNSServiceTxt* find(const stcMDNSServiceTxt* p_pTxt); - stcMDNS_RRDomain(void); - stcMDNS_RRDomain(const stcMDNS_RRDomain& p_Other); + uint16_t length(void) const; - stcMDNS_RRDomain& operator=(const stcMDNS_RRDomain& p_Other); + size_t c_strLength(void) const; + bool c_str(char* p_pcBuffer); - bool clear(void); + size_t bufferLength(void) const; + bool buffer(char* p_pcBuffer); - bool addLabel(const char* p_pcLabel, - bool p_bPrependUnderline = false); + bool compare(const stcMDNSServiceTxts& p_Other) const; + bool operator==(const stcMDNSServiceTxts& p_Other) const; + bool operator!=(const stcMDNSServiceTxts& p_Other) const; + }; - bool compare(const stcMDNS_RRDomain& p_Other) const; - bool operator==(const stcMDNS_RRDomain& p_Other) const; - bool operator!=(const stcMDNS_RRDomain& p_Other) const; - bool operator>(const stcMDNS_RRDomain& p_Other) const; + /** + enuContentFlags + */ + typedef enum _enuContentFlags + { + // Host + ContentFlag_A = 0x01, + ContentFlag_PTR_IP4 = 0x02, + ContentFlag_PTR_IP6 = 0x04, + ContentFlag_AAAA = 0x08, + // Service + ContentFlag_PTR_TYPE = 0x10, + ContentFlag_PTR_NAME = 0x20, + ContentFlag_TXT = 0x40, + ContentFlag_SRV = 0x80, + } enuContentFlags; - size_t c_strLength(void) const; - bool c_str(char* p_pcBuffer); - }; + /** + stcMDNS_MsgHeader + */ + struct stcMDNS_MsgHeader + { + uint16_t m_u16ID; // Identifier + bool m_1bQR : 1; // Query/Response flag + unsigned char m_4bOpcode : 4; // Operation code + bool m_1bAA : 1; // Authoritative Answer flag + bool m_1bTC : 1; // Truncation flag + bool m_1bRD : 1; // Recursion desired + bool m_1bRA : 1; // Recursion available + unsigned char m_3bZ : 3; // Zero + unsigned char m_4bRCode : 4; // Response code + uint16_t m_u16QDCount; // Question count + uint16_t m_u16ANCount; // Answer count + uint16_t m_u16NSCount; // Authority Record count + uint16_t m_u16ARCount; // Additional Record count + + stcMDNS_MsgHeader(uint16_t p_u16ID = 0, bool p_bQR = false, + unsigned char p_ucOpcode = 0, bool p_bAA = false, bool p_bTC = false, + bool p_bRD = false, 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); + }; - /** - stcMDNS_RRAttributes - */ - struct stcMDNS_RRAttributes - { - uint16_t m_u16Type; // Type - uint16_t m_u16Class; // Class, nearly always 'IN' + /** + stcMDNS_RRDomain + */ + struct stcMDNS_RRDomain + { + char m_acName[MDNS_DOMAIN_MAXLENGTH]; // Encoded domain name + uint16_t m_u16NameLength; // Length (incl. '\0') - stcMDNS_RRAttributes(uint16_t p_u16Type = 0, - uint16_t p_u16Class = 1 /*DNS_RRCLASS_IN Internet*/); - stcMDNS_RRAttributes(const stcMDNS_RRAttributes& p_Other); + stcMDNS_RRDomain(void); + stcMDNS_RRDomain(const stcMDNS_RRDomain& p_Other); - stcMDNS_RRAttributes& operator=(const stcMDNS_RRAttributes& p_Other); - }; + stcMDNS_RRDomain& operator=(const stcMDNS_RRDomain& p_Other); - /** - stcMDNS_RRHeader - */ - struct stcMDNS_RRHeader - { - stcMDNS_RRDomain m_Domain; - stcMDNS_RRAttributes m_Attributes; + bool clear(void); - stcMDNS_RRHeader(void); - stcMDNS_RRHeader(const stcMDNS_RRHeader& p_Other); + bool addLabel(const char* p_pcLabel, bool p_bPrependUnderline = false); - stcMDNS_RRHeader& operator=(const stcMDNS_RRHeader& p_Other); + bool compare(const stcMDNS_RRDomain& p_Other) const; + bool operator==(const stcMDNS_RRDomain& p_Other) const; + bool operator!=(const stcMDNS_RRDomain& p_Other) const; + bool operator>(const stcMDNS_RRDomain& p_Other) const; - bool clear(void); - }; + size_t c_strLength(void) const; + bool c_str(char* p_pcBuffer); + }; - /** - stcMDNS_RRQuestion - */ - struct stcMDNS_RRQuestion - { - stcMDNS_RRQuestion* m_pNext; - stcMDNS_RRHeader m_Header; - bool m_bUnicast; // Unicast reply requested + /** + stcMDNS_RRAttributes + */ + struct stcMDNS_RRAttributes + { + uint16_t m_u16Type; // Type + uint16_t m_u16Class; // Class, nearly always 'IN' - stcMDNS_RRQuestion(void); - }; + stcMDNS_RRAttributes(uint16_t p_u16Type = 0, + uint16_t p_u16Class = 1 /*DNS_RRCLASS_IN Internet*/); + stcMDNS_RRAttributes(const stcMDNS_RRAttributes& p_Other); - /** - enuAnswerType - */ - typedef enum _enuAnswerType - { - AnswerType_A, - AnswerType_PTR, - AnswerType_TXT, - AnswerType_AAAA, - AnswerType_SRV, - AnswerType_Generic - } enuAnswerType; + stcMDNS_RRAttributes& operator=(const stcMDNS_RRAttributes& p_Other); + }; - /** - stcMDNS_RRAnswer - */ - struct stcMDNS_RRAnswer - { - stcMDNS_RRAnswer* m_pNext; - const enuAnswerType m_AnswerType; - stcMDNS_RRHeader m_Header; - bool m_bCacheFlush; // Cache flush command bit - uint32_t m_u32TTL; // Validity time in seconds + /** + stcMDNS_RRHeader + */ + struct stcMDNS_RRHeader + { + stcMDNS_RRDomain m_Domain; + stcMDNS_RRAttributes m_Attributes; - virtual ~stcMDNS_RRAnswer(void); + stcMDNS_RRHeader(void); + stcMDNS_RRHeader(const stcMDNS_RRHeader& p_Other); - enuAnswerType answerType(void) const; + stcMDNS_RRHeader& operator=(const stcMDNS_RRHeader& p_Other); - bool clear(void); + bool clear(void); + }; - protected: - stcMDNS_RRAnswer(enuAnswerType p_AnswerType, - const stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL); - }; + /** + stcMDNS_RRQuestion + */ + struct stcMDNS_RRQuestion + { + stcMDNS_RRQuestion* m_pNext; + stcMDNS_RRHeader m_Header; + bool m_bUnicast; // Unicast reply requested + + stcMDNS_RRQuestion(void); + }; + + /** + enuAnswerType + */ + typedef enum _enuAnswerType + { + AnswerType_A, + AnswerType_PTR, + AnswerType_TXT, + AnswerType_AAAA, + AnswerType_SRV, + AnswerType_Generic + } enuAnswerType; + + /** + stcMDNS_RRAnswer + */ + struct stcMDNS_RRAnswer + { + stcMDNS_RRAnswer* m_pNext; + const enuAnswerType m_AnswerType; + stcMDNS_RRHeader m_Header; + bool m_bCacheFlush; // Cache flush command bit + uint32_t m_u32TTL; // Validity time in seconds + + virtual ~stcMDNS_RRAnswer(void); + + enuAnswerType answerType(void) const; + + bool clear(void); + + protected: + stcMDNS_RRAnswer(enuAnswerType p_AnswerType, const stcMDNS_RRHeader& p_Header, + uint32_t p_u32TTL); + }; #ifdef MDNS_IP4_SUPPORT - /** - stcMDNS_RRAnswerA - */ - struct stcMDNS_RRAnswerA : public stcMDNS_RRAnswer - { - IPAddress m_IPAddress; + /** + stcMDNS_RRAnswerA + */ + struct stcMDNS_RRAnswerA: public stcMDNS_RRAnswer + { + IPAddress m_IPAddress; - stcMDNS_RRAnswerA(const stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL); - ~stcMDNS_RRAnswerA(void); + stcMDNS_RRAnswerA(const stcMDNS_RRHeader& p_Header, uint32_t p_u32TTL); + ~stcMDNS_RRAnswerA(void); - bool clear(void); - }; + bool clear(void); + }; #endif - /** - stcMDNS_RRAnswerPTR - */ - struct stcMDNS_RRAnswerPTR : public stcMDNS_RRAnswer - { - stcMDNS_RRDomain m_PTRDomain; + /** + stcMDNS_RRAnswerPTR + */ + struct stcMDNS_RRAnswerPTR: public stcMDNS_RRAnswer + { + stcMDNS_RRDomain m_PTRDomain; - stcMDNS_RRAnswerPTR(const stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL); - ~stcMDNS_RRAnswerPTR(void); + stcMDNS_RRAnswerPTR(const stcMDNS_RRHeader& p_Header, uint32_t p_u32TTL); + ~stcMDNS_RRAnswerPTR(void); - bool clear(void); - }; + bool clear(void); + }; - /** - stcMDNS_RRAnswerTXT - */ - struct stcMDNS_RRAnswerTXT : public stcMDNS_RRAnswer - { - stcMDNSServiceTxts m_Txts; + /** + stcMDNS_RRAnswerTXT + */ + struct stcMDNS_RRAnswerTXT: public stcMDNS_RRAnswer + { + stcMDNSServiceTxts m_Txts; - stcMDNS_RRAnswerTXT(const stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL); - ~stcMDNS_RRAnswerTXT(void); + stcMDNS_RRAnswerTXT(const stcMDNS_RRHeader& p_Header, uint32_t p_u32TTL); + ~stcMDNS_RRAnswerTXT(void); - bool clear(void); - }; + bool clear(void); + }; #ifdef MDNS_IP6_SUPPORT - /** - stcMDNS_RRAnswerAAAA - */ - struct stcMDNS_RRAnswerAAAA : public stcMDNS_RRAnswer - { - //TODO: IP6Address m_IPAddress; + /** + stcMDNS_RRAnswerAAAA + */ + struct stcMDNS_RRAnswerAAAA: public stcMDNS_RRAnswer + { + // TODO: IP6Address m_IPAddress; - stcMDNS_RRAnswerAAAA(const stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL); - ~stcMDNS_RRAnswerAAAA(void); + stcMDNS_RRAnswerAAAA(const stcMDNS_RRHeader& p_Header, uint32_t p_u32TTL); + ~stcMDNS_RRAnswerAAAA(void); - bool clear(void); - }; + bool clear(void); + }; #endif - /** - stcMDNS_RRAnswerSRV - */ - struct stcMDNS_RRAnswerSRV : public stcMDNS_RRAnswer - { - uint16_t m_u16Priority; - uint16_t m_u16Weight; - uint16_t m_u16Port; - stcMDNS_RRDomain m_SRVDomain; - - stcMDNS_RRAnswerSRV(const stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL); - ~stcMDNS_RRAnswerSRV(void); - - bool clear(void); - }; + /** + stcMDNS_RRAnswerSRV + */ + struct stcMDNS_RRAnswerSRV: public stcMDNS_RRAnswer + { + uint16_t m_u16Priority; + uint16_t m_u16Weight; + uint16_t m_u16Port; + stcMDNS_RRDomain m_SRVDomain; - /** - stcMDNS_RRAnswerGeneric - */ - struct stcMDNS_RRAnswerGeneric : public stcMDNS_RRAnswer - { - uint16_t m_u16RDLength; // Length of variable answer - uint8_t* m_pu8RDData; // Offset of start of variable answer in packet + stcMDNS_RRAnswerSRV(const stcMDNS_RRHeader& p_Header, uint32_t p_u32TTL); + ~stcMDNS_RRAnswerSRV(void); - stcMDNS_RRAnswerGeneric(const stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL); - ~stcMDNS_RRAnswerGeneric(void); + bool clear(void); + }; - bool clear(void); - }; + /** + stcMDNS_RRAnswerGeneric + */ + struct stcMDNS_RRAnswerGeneric: public stcMDNS_RRAnswer + { + uint16_t m_u16RDLength; // Length of variable answer + uint8_t* m_pu8RDData; // Offset of start of variable answer in packet + stcMDNS_RRAnswerGeneric(const stcMDNS_RRHeader& p_Header, uint32_t p_u32TTL); + ~stcMDNS_RRAnswerGeneric(void); - /** - enuProbingStatus - */ - typedef enum _enuProbingStatus - { - ProbingStatus_WaitingForData, - ProbingStatus_ReadyToStart, - ProbingStatus_InProgress, - ProbingStatus_Done - } enuProbingStatus; + bool clear(void); + }; - /** - stcProbeInformation - */ - struct stcProbeInformation - { - enuProbingStatus m_ProbingStatus; - uint8_t m_u8SentCount; // Used for probes and announcements - esp8266::polledTimeout::oneShotMs m_Timeout; // Used for probes and announcements - //clsMDNSTimeFlag m_TimeFlag; // Used for probes and announcements - bool m_bConflict; - bool m_bTiebreakNeeded; - MDNSHostProbeFn m_fnHostProbeResultCallback; - MDNSServiceProbeFn m_fnServiceProbeResultCallback; - - stcProbeInformation(void); - - bool clear(bool p_bClearUserdata = false); - }; + /** + enuProbingStatus + */ + typedef enum _enuProbingStatus + { + ProbingStatus_WaitingForData, + ProbingStatus_ReadyToStart, + ProbingStatus_InProgress, + ProbingStatus_Done + } enuProbingStatus; + /** + stcProbeInformation + */ + struct stcProbeInformation + { + enuProbingStatus m_ProbingStatus; + uint8_t m_u8SentCount; // Used for probes and announcements + esp8266::polledTimeout::oneShotMs m_Timeout; // Used for probes and announcements + // clsMDNSTimeFlag m_TimeFlag; // Used for probes and + // announcements + bool m_bConflict; + bool m_bTiebreakNeeded; + MDNSHostProbeFn m_fnHostProbeResultCallback; + MDNSServiceProbeFn m_fnServiceProbeResultCallback; + + stcProbeInformation(void); + + bool clear(bool p_bClearUserdata = false); + }; - /** - stcMDNSService - */ - struct stcMDNSService - { - stcMDNSService* m_pNext; - char* m_pcName; - bool m_bAutoName; // Name was set automatically to hostname (if no name was supplied) - char* m_pcService; - char* m_pcProtocol; - uint16_t m_u16Port; - uint8_t m_u8ReplyMask; - stcMDNSServiceTxts m_Txts; - MDNSDynamicServiceTxtCallbackFunc m_fnTxtCallback; - stcProbeInformation m_ProbeInformation; - - stcMDNSService(const char* p_pcName = 0, - const char* p_pcService = 0, - const char* p_pcProtocol = 0); - ~stcMDNSService(void); - - bool setName(const char* p_pcName); - bool releaseName(void); - - bool setService(const char* p_pcService); - bool releaseService(void); - - bool setProtocol(const char* p_pcProtocol); - bool releaseProtocol(void); - }; + /** + stcMDNSService + */ + struct stcMDNSService + { + stcMDNSService* m_pNext; + char* m_pcName; + bool m_bAutoName; // Name was set automatically to hostname (if no name was supplied) + char* m_pcService; + char* m_pcProtocol; + uint16_t m_u16Port; + uint8_t m_u8ReplyMask; + stcMDNSServiceTxts m_Txts; + MDNSDynamicServiceTxtCallbackFunc m_fnTxtCallback; + stcProbeInformation m_ProbeInformation; + + stcMDNSService(const char* p_pcName = 0, const char* p_pcService = 0, + const char* p_pcProtocol = 0); + ~stcMDNSService(void); + + bool setName(const char* p_pcName); + bool releaseName(void); + + bool setService(const char* p_pcService); + bool releaseService(void); + + bool setProtocol(const char* p_pcProtocol); + bool releaseProtocol(void); + }; - /** - stcMDNSServiceQuery - */ - struct stcMDNSServiceQuery - { /** - stcAnswer + stcMDNSServiceQuery */ - struct stcAnswer + struct stcMDNSServiceQuery { /** - stcTTL + stcAnswer */ - struct stcTTL + struct stcAnswer { /** - timeoutLevel_t + stcTTL */ - typedef uint8_t timeoutLevel_t; + struct stcTTL + { + /** + timeoutLevel_t + */ + typedef uint8_t timeoutLevel_t; + /** + TIMEOUTLEVELs + */ + const timeoutLevel_t TIMEOUTLEVEL_UNSET = 0; + const timeoutLevel_t TIMEOUTLEVEL_BASE = 80; + const timeoutLevel_t TIMEOUTLEVEL_INTERVAL = 5; + const timeoutLevel_t TIMEOUTLEVEL_FINAL = 100; + + uint32_t m_u32TTL; + esp8266::polledTimeout::oneShotMs m_TTLTimeout; + timeoutLevel_t m_timeoutLevel; + + using timeoutBase = decltype(m_TTLTimeout); + + stcTTL(void); + bool set(uint32_t p_u32TTL); + + bool flagged(void); + bool restart(void); + + bool prepareDeletion(void); + bool finalTimeoutLevel(void) const; + + timeoutBase::timeType timeout(void) const; + }; +#ifdef MDNS_IP4_SUPPORT /** - TIMEOUTLEVELs + stcIP4Address */ - const timeoutLevel_t TIMEOUTLEVEL_UNSET = 0; - const timeoutLevel_t TIMEOUTLEVEL_BASE = 80; - const timeoutLevel_t TIMEOUTLEVEL_INTERVAL = 5; - const timeoutLevel_t TIMEOUTLEVEL_FINAL = 100; - - uint32_t m_u32TTL; - esp8266::polledTimeout::oneShotMs m_TTLTimeout; - timeoutLevel_t m_timeoutLevel; - - stcTTL(void); - bool set(uint32_t p_u32TTL); - - bool flagged(void); - bool restart(void); - - bool prepareDeletion(void); - bool finalTimeoutLevel(void) const; - - unsigned long timeout(void) const; - }; -#ifdef MDNS_IP4_SUPPORT - /** - stcIP4Address - */ - struct stcIP4Address - { - stcIP4Address* m_pNext; - IPAddress m_IPAddress; - stcTTL m_TTL; + struct stcIP4Address + { + stcIP4Address* m_pNext; + IPAddress m_IPAddress; + stcTTL m_TTL; - stcIP4Address(IPAddress p_IPAddress, - uint32_t p_u32TTL = 0); - }; + stcIP4Address(IPAddress p_IPAddress, uint32_t p_u32TTL = 0); + }; #endif #ifdef MDNS_IP6_SUPPORT - /** - stcIP6Address - */ - struct stcIP6Address - { - stcIP6Address* m_pNext; - IP6Address m_IPAddress; - stcTTL m_TTL; + /** + stcIP6Address + */ + struct stcIP6Address + { + stcIP6Address* m_pNext; + IP6Address m_IPAddress; + stcTTL m_TTL; - stcIP6Address(IPAddress p_IPAddress, - uint32_t p_u32TTL = 0); - }; + stcIP6Address(IPAddress p_IPAddress, uint32_t p_u32TTL = 0); + }; #endif - stcAnswer* m_pNext; - // The service domain is the first 'answer' (from PTR answer, using service and protocol) to be set - // Defines the key for additional answer, like host domain, etc. - stcMDNS_RRDomain m_ServiceDomain; // 1. level answer (PTR), eg. MyESP._http._tcp.local - char* m_pcServiceDomain; - stcTTL m_TTLServiceDomain; - stcMDNS_RRDomain m_HostDomain; // 2. level answer (SRV, using service domain), eg. esp8266.local - char* m_pcHostDomain; - uint16_t m_u16Port; // 2. level answer (SRV, using service domain), eg. 5000 - stcTTL m_TTLHostDomainAndPort; - stcMDNSServiceTxts m_Txts; // 2. level answer (TXT, using service domain), eg. c#=1 - char* m_pcTxts; - stcTTL m_TTLTxts; + stcAnswer* m_pNext; + // The service domain is the first 'answer' (from PTR answer, using service and + // protocol) to be set Defines the key for additional answer, like host domain, etc. + stcMDNS_RRDomain + m_ServiceDomain; // 1. level answer (PTR), eg. MyESP._http._tcp.local + char* m_pcServiceDomain; + stcTTL m_TTLServiceDomain; + stcMDNS_RRDomain + m_HostDomain; // 2. level answer (SRV, using service domain), eg. esp8266.local + char* m_pcHostDomain; + uint16_t m_u16Port; // 2. level answer (SRV, using service domain), eg. 5000 + stcTTL m_TTLHostDomainAndPort; + stcMDNSServiceTxts m_Txts; // 2. level answer (TXT, using service domain), eg. c#=1 + char* m_pcTxts; + stcTTL m_TTLTxts; #ifdef MDNS_IP4_SUPPORT - stcIP4Address* m_pIP4Addresses; // 3. level answer (A, using host domain), eg. 123.456.789.012 + stcIP4Address* + m_pIP4Addresses; // 3. level answer (A, using host domain), eg. 123.456.789.012 #endif #ifdef MDNS_IP6_SUPPORT - stcIP6Address* m_pIP6Addresses; // 3. level answer (AAAA, using host domain), eg. 1234::09 + stcIP6Address* + m_pIP6Addresses; // 3. level answer (AAAA, using host domain), eg. 1234::09 #endif - uint32_t m_u32ContentFlags; + uint32_t m_u32ContentFlags; - stcAnswer(void); - ~stcAnswer(void); + stcAnswer(void); + ~stcAnswer(void); - bool clear(void); + bool clear(void); - char* allocServiceDomain(size_t p_stLength); - bool releaseServiceDomain(void); + char* allocServiceDomain(size_t p_stLength); + bool releaseServiceDomain(void); - char* allocHostDomain(size_t p_stLength); - bool releaseHostDomain(void); + char* allocHostDomain(size_t p_stLength); + bool releaseHostDomain(void); - char* allocTxts(size_t p_stLength); - bool releaseTxts(void); + char* allocTxts(size_t p_stLength); + bool releaseTxts(void); #ifdef MDNS_IP4_SUPPORT - bool releaseIP4Addresses(void); - bool addIP4Address(stcIP4Address* p_pIP4Address); - bool removeIP4Address(stcIP4Address* p_pIP4Address); - const stcIP4Address* findIP4Address(const IPAddress& p_IPAddress) const; - stcIP4Address* findIP4Address(const IPAddress& p_IPAddress); - uint32_t IP4AddressCount(void) const; - const stcIP4Address* IP4AddressAtIndex(uint32_t p_u32Index) const; - stcIP4Address* IP4AddressAtIndex(uint32_t p_u32Index); + bool releaseIP4Addresses(void); + bool addIP4Address(stcIP4Address* p_pIP4Address); + bool removeIP4Address(stcIP4Address* p_pIP4Address); + const stcIP4Address* findIP4Address(const IPAddress& p_IPAddress) const; + stcIP4Address* findIP4Address(const IPAddress& p_IPAddress); + uint32_t IP4AddressCount(void) const; + const stcIP4Address* IP4AddressAtIndex(uint32_t p_u32Index) const; + stcIP4Address* IP4AddressAtIndex(uint32_t p_u32Index); #endif #ifdef MDNS_IP6_SUPPORT - bool releaseIP6Addresses(void); - bool addIP6Address(stcIP6Address* p_pIP6Address); - bool removeIP6Address(stcIP6Address* p_pIP6Address); - const stcIP6Address* findIP6Address(const IPAddress& p_IPAddress) const; - stcIP6Address* findIP6Address(const IPAddress& p_IPAddress); - uint32_t IP6AddressCount(void) const; - const stcIP6Address* IP6AddressAtIndex(uint32_t p_u32Index) const; - stcIP6Address* IP6AddressAtIndex(uint32_t p_u32Index); + bool releaseIP6Addresses(void); + bool addIP6Address(stcIP6Address* p_pIP6Address); + bool removeIP6Address(stcIP6Address* p_pIP6Address); + const stcIP6Address* findIP6Address(const IPAddress& p_IPAddress) const; + stcIP6Address* findIP6Address(const IPAddress& p_IPAddress); + uint32_t IP6AddressCount(void) const; + const stcIP6Address* IP6AddressAtIndex(uint32_t p_u32Index) const; + stcIP6Address* IP6AddressAtIndex(uint32_t p_u32Index); #endif - }; + }; - stcMDNSServiceQuery* m_pNext; - stcMDNS_RRDomain m_ServiceTypeDomain; // eg. _http._tcp.local - MDNSServiceQueryCallbackFunc m_fnCallback; - bool m_bLegacyQuery; - uint8_t m_u8SentCount; - esp8266::polledTimeout::oneShotMs m_ResendTimeout; - bool m_bAwaitingAnswers; - stcAnswer* m_pAnswers; + stcMDNSServiceQuery* m_pNext; + stcMDNS_RRDomain m_ServiceTypeDomain; // eg. _http._tcp.local + MDNSServiceQueryCallbackFunc m_fnCallback; + bool m_bLegacyQuery; + uint8_t m_u8SentCount; + esp8266::polledTimeout::oneShotMs m_ResendTimeout; + bool m_bAwaitingAnswers; + stcAnswer* m_pAnswers; - stcMDNSServiceQuery(void); - ~stcMDNSServiceQuery(void); + stcMDNSServiceQuery(void); + ~stcMDNSServiceQuery(void); - bool clear(void); + bool clear(void); - uint32_t answerCount(void) const; - const stcAnswer* answerAtIndex(uint32_t p_u32Index) const; - stcAnswer* answerAtIndex(uint32_t p_u32Index); - uint32_t indexOfAnswer(const stcAnswer* p_pAnswer) const; + uint32_t answerCount(void) const; + const stcAnswer* answerAtIndex(uint32_t p_u32Index) const; + stcAnswer* answerAtIndex(uint32_t p_u32Index); + uint32_t indexOfAnswer(const stcAnswer* p_pAnswer) const; - bool addAnswer(stcAnswer* p_pAnswer); - bool removeAnswer(stcAnswer* p_pAnswer); + bool addAnswer(stcAnswer* p_pAnswer); + bool removeAnswer(stcAnswer* p_pAnswer); - stcAnswer* findAnswerForServiceDomain(const stcMDNS_RRDomain& p_ServiceDomain); - stcAnswer* findAnswerForHostDomain(const stcMDNS_RRDomain& p_HostDomain); - }; + stcAnswer* findAnswerForServiceDomain(const stcMDNS_RRDomain& p_ServiceDomain); + stcAnswer* findAnswerForHostDomain(const stcMDNS_RRDomain& p_HostDomain); + }; - /** - stcMDNSSendParameter - */ - struct stcMDNSSendParameter - { - protected: /** - stcDomainCacheItem + stcMDNSSendParameter */ - struct stcDomainCacheItem + struct stcMDNSSendParameter { - stcDomainCacheItem* m_pNext; - const void* m_pHostnameOrService; // Opaque id for host or service domain (pointer) - bool m_bAdditionalData; // Opaque flag for special info (service domain included) - uint16_t m_u16Offset; // Offset in UDP output buffer - - stcDomainCacheItem(const void* p_pHostnameOrService, - bool p_bAdditionalData, - uint32_t p_u16Offset); - }; + protected: + /** + stcDomainCacheItem + */ + struct stcDomainCacheItem + { + stcDomainCacheItem* m_pNext; + const void* m_pHostnameOrService; // Opaque id for host or service domain (pointer) + bool m_bAdditionalData; // Opaque flag for special info (service domain included) + uint16_t m_u16Offset; // Offset in UDP output buffer - public: - uint16_t m_u16ID; // Query ID (used only in lagacy queries) - stcMDNS_RRQuestion* m_pQuestions; // A list of queries - uint8_t m_u8HostReplyMask; // Flags for reply components/answers - bool m_bLegacyQuery; // Flag: Legacy query - bool m_bResponse; // Flag: Response to a query - bool m_bAuthorative; // Flag: Authorative (owner) response - bool m_bCacheFlush; // Flag: Clients should flush their caches - bool m_bUnicast; // Flag: Unicast response - bool m_bUnannounce; // Flag: Unannounce service - uint16_t m_u16Offset; // Current offset in UDP write buffer (mainly for domain cache) - stcDomainCacheItem* m_pDomainCacheItems; // Cached host and service domains - - stcMDNSSendParameter(void); - ~stcMDNSSendParameter(void); - - bool clear(void); - bool clearCachedNames(void); - - bool shiftOffset(uint16_t p_u16Shift); - - bool addDomainCacheItem(const void* p_pHostnameOrService, - bool p_bAdditionalData, - uint16_t p_u16Offset); - uint16_t findCachedDomainOffset(const void* p_pHostnameOrService, - bool p_bAdditionalData) const; - }; + stcDomainCacheItem(const void* p_pHostnameOrService, bool p_bAdditionalData, + uint32_t p_u16Offset); + }; + + public: + uint16_t m_u16ID; // Query ID (used only in lagacy queries) + stcMDNS_RRQuestion* m_pQuestions; // A list of queries + uint8_t m_u8HostReplyMask; // Flags for reply components/answers + bool m_bLegacyQuery; // Flag: Legacy query + bool m_bResponse; // Flag: Response to a query + bool m_bAuthorative; // Flag: Authoritative (owner) response + bool m_bCacheFlush; // Flag: Clients should flush their caches + bool m_bUnicast; // Flag: Unicast response + bool m_bUnannounce; // Flag: Unannounce service + uint16_t m_u16Offset; // Current offset in UDP write buffer (mainly for domain cache) + stcDomainCacheItem* m_pDomainCacheItems; // Cached host and service domains + + stcMDNSSendParameter(void); + ~stcMDNSSendParameter(void); + + bool clear(void); + bool clearCachedNames(void); + + bool shiftOffset(uint16_t p_u16Shift); - // Instance variables - stcMDNSService* m_pServices; - UdpContext* m_pUDPContext; - char* m_pcHostname; - stcMDNSServiceQuery* m_pServiceQueries; - MDNSDynamicServiceTxtCallbackFunc m_fnServiceTxtCallback; - stcProbeInformation m_HostProbeInformation; - - /** CONTROL **/ - /* MAINTENANCE */ - bool _process(bool p_bUserContext); - bool _restart(void); - - /* RECEIVING */ - bool _parseMessage(void); - bool _parseQuery(const stcMDNS_MsgHeader& p_Header); - - bool _parseResponse(const stcMDNS_MsgHeader& p_Header); - bool _processAnswers(const stcMDNS_RRAnswer* p_pPTRAnswers); - bool _processPTRAnswer(const stcMDNS_RRAnswerPTR* p_pPTRAnswer, - bool& p_rbFoundNewKeyAnswer); - bool _processSRVAnswer(const stcMDNS_RRAnswerSRV* p_pSRVAnswer, - bool& p_rbFoundNewKeyAnswer); - bool _processTXTAnswer(const stcMDNS_RRAnswerTXT* p_pTXTAnswer); + bool addDomainCacheItem(const void* p_pHostnameOrService, bool p_bAdditionalData, + uint16_t p_u16Offset); + uint16_t findCachedDomainOffset(const void* p_pHostnameOrService, + bool p_bAdditionalData) const; + }; + + // Instance variables + stcMDNSService* m_pServices; + UdpContext* m_pUDPContext; + char* m_pcHostname; + stcMDNSServiceQuery* m_pServiceQueries; + MDNSDynamicServiceTxtCallbackFunc m_fnServiceTxtCallback; + stcProbeInformation m_HostProbeInformation; + bool m_bLwipCb; + bool m_bRestarting; + + /** CONTROL **/ + /* MAINTENANCE */ + bool _process(bool p_bUserContext); + bool _restart(void); + + /* RECEIVING */ + bool _parseMessage(void); + bool _parseQuery(const stcMDNS_MsgHeader& p_Header); + + bool _parseResponse(const stcMDNS_MsgHeader& p_Header); + bool _processAnswers(const stcMDNS_RRAnswer* p_pPTRAnswers); + bool _processPTRAnswer(const stcMDNS_RRAnswerPTR* p_pPTRAnswer, + bool& p_rbFoundNewKeyAnswer); + bool _processSRVAnswer(const stcMDNS_RRAnswerSRV* p_pSRVAnswer, + bool& p_rbFoundNewKeyAnswer); + bool _processTXTAnswer(const stcMDNS_RRAnswerTXT* p_pTXTAnswer); #ifdef MDNS_IP4_SUPPORT - bool _processAAnswer(const stcMDNS_RRAnswerA* p_pAAnswer); + bool _processAAnswer(const stcMDNS_RRAnswerA* p_pAAnswer); #endif #ifdef MDNS_IP6_SUPPORT - bool _processAAAAAnswer(const stcMDNS_RRAnswerAAAA* p_pAAAAAnswer); + bool _processAAAAAnswer(const stcMDNS_RRAnswerAAAA* p_pAAAAAnswer); #endif - /* PROBING */ - bool _updateProbeStatus(void); - bool _resetProbeStatus(bool p_bRestart = true); - bool _hasProbesWaitingForAnswers(void) const; - bool _sendHostProbe(void); - bool _sendServiceProbe(stcMDNSService& p_rService); - bool _cancelProbingForHost(void); - bool _cancelProbingForService(stcMDNSService& p_rService); - - /* ANNOUNCE */ - bool _announce(bool p_bAnnounce, - bool p_bIncludeServices); - bool _announceService(stcMDNSService& p_rService, - bool p_bAnnounce = true); - - /* SERVICE QUERY CACHE */ - bool _hasServiceQueriesWaitingForAnswers(void) const; - bool _checkServiceQueryCache(void); - - /** TRANSFER **/ - /* SENDING */ - bool _sendMDNSMessage(stcMDNSSendParameter& p_SendParameter); - bool _sendMDNSMessage_Multicast(MDNSResponder::stcMDNSSendParameter& p_rSendParameter); - bool _prepareMDNSMessage(stcMDNSSendParameter& p_SendParameter, - IPAddress p_IPAddress); - bool _sendMDNSServiceQuery(const stcMDNSServiceQuery& p_ServiceQuery); - bool _sendMDNSQuery(const stcMDNS_RRDomain& p_QueryDomain, - uint16_t p_u16QueryType, - stcMDNSServiceQuery::stcAnswer* p_pKnownAnswers = 0); - - uint8_t _replyMaskForHost(const stcMDNS_RRHeader& p_RRHeader, - bool* p_pbFullNameMatch = 0) const; - uint8_t _replyMaskForService(const stcMDNS_RRHeader& p_RRHeader, - const stcMDNSService& p_Service, - bool* p_pbFullNameMatch = 0) const; - - /* RESOURCE RECORD */ - bool _readRRQuestion(stcMDNS_RRQuestion& p_rQuestion); - bool _readRRAnswer(stcMDNS_RRAnswer*& p_rpAnswer); + /* PROBING */ + bool _updateProbeStatus(void); + bool _resetProbeStatus(bool p_bRestart = true); + bool _hasProbesWaitingForAnswers(void) const; + bool _sendHostProbe(void); + bool _sendServiceProbe(stcMDNSService& p_rService); + bool _cancelProbingForHost(void); + bool _cancelProbingForService(stcMDNSService& p_rService); + + /* ANNOUNCE */ + bool _announce(bool p_bAnnounce, bool p_bIncludeServices); + bool _announceService(stcMDNSService& p_rService, bool p_bAnnounce = true); + + /* SERVICE QUERY CACHE */ + bool _hasServiceQueriesWaitingForAnswers(void) const; + bool _checkServiceQueryCache(void); + + /** TRANSFER **/ + /* SENDING */ + bool _sendMDNSMessage(stcMDNSSendParameter& p_SendParameter); + bool _sendMDNSMessage_Multicast(MDNSResponder::stcMDNSSendParameter& p_rSendParameter); + bool _prepareMDNSMessage(stcMDNSSendParameter& p_SendParameter, IPAddress p_IPAddress); + bool _sendMDNSServiceQuery(const stcMDNSServiceQuery& p_ServiceQuery); + bool _sendMDNSQuery(const stcMDNS_RRDomain& p_QueryDomain, uint16_t p_u16QueryType, + stcMDNSServiceQuery::stcAnswer* p_pKnownAnswers = 0); + + uint8_t _replyMaskForHost(const stcMDNS_RRHeader& p_RRHeader, + bool* p_pbFullNameMatch = 0) const; + uint8_t _replyMaskForService(const stcMDNS_RRHeader& p_RRHeader, + const stcMDNSService& p_Service, + bool* p_pbFullNameMatch = 0) const; + + /* RESOURCE RECORD */ + bool _readRRQuestion(stcMDNS_RRQuestion& p_rQuestion); + bool _readRRAnswer(stcMDNS_RRAnswer*& p_rpAnswer); #ifdef MDNS_IP4_SUPPORT - bool _readRRAnswerA(stcMDNS_RRAnswerA& p_rRRAnswerA, - uint16_t p_u16RDLength); + bool _readRRAnswerA(stcMDNS_RRAnswerA& p_rRRAnswerA, uint16_t p_u16RDLength); #endif - bool _readRRAnswerPTR(stcMDNS_RRAnswerPTR& p_rRRAnswerPTR, - uint16_t p_u16RDLength); - bool _readRRAnswerTXT(stcMDNS_RRAnswerTXT& p_rRRAnswerTXT, - uint16_t p_u16RDLength); + bool _readRRAnswerPTR(stcMDNS_RRAnswerPTR& p_rRRAnswerPTR, uint16_t p_u16RDLength); + bool _readRRAnswerTXT(stcMDNS_RRAnswerTXT& p_rRRAnswerTXT, uint16_t p_u16RDLength); #ifdef MDNS_IP6_SUPPORT - bool _readRRAnswerAAAA(stcMDNS_RRAnswerAAAA& p_rRRAnswerAAAA, - uint16_t p_u16RDLength); + bool _readRRAnswerAAAA(stcMDNS_RRAnswerAAAA& p_rRRAnswerAAAA, uint16_t p_u16RDLength); #endif - bool _readRRAnswerSRV(stcMDNS_RRAnswerSRV& p_rRRAnswerSRV, - uint16_t p_u16RDLength); - bool _readRRAnswerGeneric(stcMDNS_RRAnswerGeneric& p_rRRAnswerGeneric, - uint16_t p_u16RDLength); - - bool _readRRHeader(stcMDNS_RRHeader& p_rHeader); - bool _readRRDomain(stcMDNS_RRDomain& p_rRRDomain); - bool _readRRDomain_Loop(stcMDNS_RRDomain& p_rRRDomain, - uint8_t p_u8Depth); - bool _readRRAttributes(stcMDNS_RRAttributes& p_rAttributes); - - /* DOMAIN NAMES */ - bool _buildDomainForHost(const char* p_pcHostname, - stcMDNS_RRDomain& p_rHostDomain) const; - bool _buildDomainForDNSSD(stcMDNS_RRDomain& p_rDNSSDDomain) const; - bool _buildDomainForService(const stcMDNSService& p_Service, - bool p_bIncludeName, - stcMDNS_RRDomain& p_rServiceDomain) const; - bool _buildDomainForService(const char* p_pcService, - const char* p_pcProtocol, - stcMDNS_RRDomain& p_rServiceDomain) const; + bool _readRRAnswerSRV(stcMDNS_RRAnswerSRV& p_rRRAnswerSRV, uint16_t p_u16RDLength); + bool _readRRAnswerGeneric(stcMDNS_RRAnswerGeneric& p_rRRAnswerGeneric, + uint16_t p_u16RDLength); + + bool _readRRHeader(stcMDNS_RRHeader& p_rHeader); + bool _readRRDomain(stcMDNS_RRDomain& p_rRRDomain); + bool _readRRDomain_Loop(stcMDNS_RRDomain& p_rRRDomain, uint8_t p_u8Depth); + bool _readRRAttributes(stcMDNS_RRAttributes& p_rAttributes); + + /* DOMAIN NAMES */ + bool _buildDomainForHost(const char* p_pcHostname, stcMDNS_RRDomain& p_rHostDomain) const; + bool _buildDomainForDNSSD(stcMDNS_RRDomain& p_rDNSSDDomain) const; + bool _buildDomainForService(const stcMDNSService& p_Service, bool p_bIncludeName, + stcMDNS_RRDomain& p_rServiceDomain) const; + bool _buildDomainForService(const char* p_pcService, const char* p_pcProtocol, + stcMDNS_RRDomain& p_rServiceDomain) const; #ifdef MDNS_IP4_SUPPORT - bool _buildDomainForReverseIP4(IPAddress p_IP4Address, - stcMDNS_RRDomain& p_rReverseIP4Domain) const; + bool _buildDomainForReverseIP4(IPAddress p_IP4Address, + stcMDNS_RRDomain& p_rReverseIP4Domain) const; #endif #ifdef MDNS_IP6_SUPPORT - bool _buildDomainForReverseIP6(IPAddress p_IP4Address, - stcMDNS_RRDomain& p_rReverseIP6Domain) const; + bool _buildDomainForReverseIP6(IPAddress p_IP4Address, + stcMDNS_RRDomain& p_rReverseIP6Domain) const; #endif - /* UDP */ - bool _udpReadBuffer(unsigned char* p_pBuffer, - size_t p_stLength); - bool _udpRead8(uint8_t& p_ru8Value); - bool _udpRead16(uint16_t& p_ru16Value); - bool _udpRead32(uint32_t& p_ru32Value); + /* UDP */ + bool _udpReadBuffer(unsigned char* p_pBuffer, size_t p_stLength); + bool _udpRead8(uint8_t& p_ru8Value); + bool _udpRead16(uint16_t& p_ru16Value); + bool _udpRead32(uint32_t& p_ru32Value); - bool _udpAppendBuffer(const unsigned char* p_pcBuffer, - size_t p_stLength); - bool _udpAppend8(uint8_t p_u8Value); - bool _udpAppend16(uint16_t p_u16Value); - bool _udpAppend32(uint32_t p_u32Value); + bool _udpAppendBuffer(const unsigned char* p_pcBuffer, size_t p_stLength); + bool _udpAppend8(uint8_t p_u8Value); + bool _udpAppend16(uint16_t p_u16Value); + bool _udpAppend32(uint32_t p_u32Value); #if not defined ESP_8266_MDNS_INCLUDE || defined DEBUG_ESP_MDNS_RESPONDER - bool _udpDump(bool p_bMovePointer = false); - bool _udpDump(unsigned p_uOffset, - unsigned p_uLength); + bool _udpDump(bool p_bMovePointer = false); + bool _udpDump(unsigned p_uOffset, unsigned p_uLength); #endif - /* READ/WRITE MDNS STRUCTS */ - bool _readMDNSMsgHeader(stcMDNS_MsgHeader& p_rMsgHeader); + /* READ/WRITE MDNS STRUCTS */ + bool _readMDNSMsgHeader(stcMDNS_MsgHeader& p_rMsgHeader); - bool _write8(uint8_t p_u8Value, - stcMDNSSendParameter& p_rSendParameter); - bool _write16(uint16_t p_u16Value, - stcMDNSSendParameter& p_rSendParameter); - bool _write32(uint32_t p_u32Value, - stcMDNSSendParameter& p_rSendParameter); + bool _write8(uint8_t p_u8Value, stcMDNSSendParameter& p_rSendParameter); + bool _write16(uint16_t p_u16Value, stcMDNSSendParameter& p_rSendParameter); + bool _write32(uint32_t p_u32Value, stcMDNSSendParameter& p_rSendParameter); - bool _writeMDNSMsgHeader(const stcMDNS_MsgHeader& p_MsgHeader, - stcMDNSSendParameter& p_rSendParameter); - bool _writeMDNSRRAttributes(const stcMDNS_RRAttributes& p_Attributes, + bool _writeMDNSMsgHeader(const stcMDNS_MsgHeader& p_MsgHeader, + stcMDNSSendParameter& p_rSendParameter); + bool _writeMDNSRRAttributes(const stcMDNS_RRAttributes& p_Attributes, + stcMDNSSendParameter& p_rSendParameter); + bool _writeMDNSRRDomain(const stcMDNS_RRDomain& p_Domain, + stcMDNSSendParameter& p_rSendParameter); + bool _writeMDNSHostDomain(const char* m_pcHostname, bool p_bPrependRDLength, + stcMDNSSendParameter& p_rSendParameter); + bool _writeMDNSServiceDomain(const stcMDNSService& p_Service, bool p_bIncludeName, + bool p_bPrependRDLength, + stcMDNSSendParameter& p_rSendParameter); + + bool _writeMDNSQuestion(stcMDNS_RRQuestion& p_Question, stcMDNSSendParameter& p_rSendParameter); - bool _writeMDNSRRDomain(const stcMDNS_RRDomain& p_Domain, - stcMDNSSendParameter& p_rSendParameter); - bool _writeMDNSHostDomain(const char* m_pcHostname, - bool p_bPrependRDLength, - stcMDNSSendParameter& p_rSendParameter); - bool _writeMDNSServiceDomain(const stcMDNSService& p_Service, - bool p_bIncludeName, - bool p_bPrependRDLength, - stcMDNSSendParameter& p_rSendParameter); - - bool _writeMDNSQuestion(stcMDNS_RRQuestion& p_Question, - stcMDNSSendParameter& p_rSendParameter); #ifdef MDNS_IP4_SUPPORT - bool _writeMDNSAnswer_A(IPAddress p_IPAddress, - stcMDNSSendParameter& p_rSendParameter); - bool _writeMDNSAnswer_PTR_IP4(IPAddress p_IPAddress, - stcMDNSSendParameter& p_rSendParameter); + bool _writeMDNSAnswer_A(IPAddress p_IPAddress, stcMDNSSendParameter& p_rSendParameter); + bool _writeMDNSAnswer_PTR_IP4(IPAddress p_IPAddress, + stcMDNSSendParameter& p_rSendParameter); #endif - bool _writeMDNSAnswer_PTR_TYPE(stcMDNSService& p_rService, - stcMDNSSendParameter& p_rSendParameter); - bool _writeMDNSAnswer_PTR_NAME(stcMDNSService& p_rService, - stcMDNSSendParameter& p_rSendParameter); - bool _writeMDNSAnswer_TXT(stcMDNSService& p_rService, - stcMDNSSendParameter& p_rSendParameter); -#ifdef MDNS_IP6_SUPPORT - bool _writeMDNSAnswer_AAAA(IPAddress p_IPAddress, - stcMDNSSendParameter& p_rSendParameter); - bool _writeMDNSAnswer_PTR_IP6(IPAddress p_IPAddress, + bool _writeMDNSAnswer_PTR_TYPE(stcMDNSService& p_rService, + stcMDNSSendParameter& p_rSendParameter); + bool _writeMDNSAnswer_PTR_NAME(stcMDNSService& p_rService, + stcMDNSSendParameter& p_rSendParameter); + bool _writeMDNSAnswer_TXT(stcMDNSService& p_rService, stcMDNSSendParameter& p_rSendParameter); +#ifdef MDNS_IP6_SUPPORT + bool _writeMDNSAnswer_AAAA(IPAddress p_IPAddress, stcMDNSSendParameter& p_rSendParameter); + bool _writeMDNSAnswer_PTR_IP6(IPAddress p_IPAddress, + stcMDNSSendParameter& p_rSendParameter); #endif - bool _writeMDNSAnswer_SRV(stcMDNSService& p_rService, - stcMDNSSendParameter& p_rSendParameter); - - /** HELPERS **/ - /* UDP CONTEXT */ - bool _callProcess(void); - bool _allocUDPContext(void); - bool _releaseUDPContext(void); - - /* SERVICE QUERY */ - stcMDNSServiceQuery* _allocServiceQuery(void); - bool _removeServiceQuery(stcMDNSServiceQuery* p_pServiceQuery); - bool _removeLegacyServiceQuery(void); - stcMDNSServiceQuery* _findServiceQuery(hMDNSServiceQuery p_hServiceQuery); - stcMDNSServiceQuery* _findLegacyServiceQuery(void); - bool _releaseServiceQueries(void); - stcMDNSServiceQuery* _findNextServiceQueryByServiceType(const stcMDNS_RRDomain& p_ServiceDomain, - const stcMDNSServiceQuery* p_pPrevServiceQuery); - - /* HOSTNAME */ - bool _setHostname(const char* p_pcHostname); - bool _releaseHostname(void); - - /* SERVICE */ - stcMDNSService* _allocService(const char* p_pcName, - const char* p_pcService, - const char* p_pcProtocol, - uint16_t p_u16Port); - bool _releaseService(stcMDNSService* p_pService); - bool _releaseServices(void); - - stcMDNSService* _findService(const char* p_pcName, - const char* p_pcService, - const char* p_pcProtocol); - stcMDNSService* _findService(const hMDNSService p_hService); - - size_t _countServices(void) const; - - /* SERVICE TXT */ - stcMDNSServiceTxt* _allocServiceTxt(stcMDNSService* p_pService, - const char* p_pcKey, - const char* p_pcValue, - bool p_bTemp); - bool _releaseServiceTxt(stcMDNSService* p_pService, - stcMDNSServiceTxt* p_pTxt); - stcMDNSServiceTxt* _updateServiceTxt(stcMDNSService* p_pService, - stcMDNSServiceTxt* p_pTxt, - const char* p_pcValue, - bool p_bTemp); - - stcMDNSServiceTxt* _findServiceTxt(stcMDNSService* p_pService, - const char* p_pcKey); - stcMDNSServiceTxt* _findServiceTxt(stcMDNSService* p_pService, - const hMDNSTxt p_hTxt); - - stcMDNSServiceTxt* _addServiceTxt(stcMDNSService* p_pService, - const char* p_pcKey, - const char* p_pcValue, - bool p_bTemp); - - stcMDNSServiceTxt* _answerKeyValue(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex); - - bool _collectServiceTxts(stcMDNSService& p_rService); - bool _releaseTempServiceTxts(stcMDNSService& p_rService); - const stcMDNSServiceTxt* _serviceTxts(const char* p_pcName, - const char* p_pcService, - const char* p_pcProtocol); - - /* MISC */ + bool _writeMDNSAnswer_SRV(stcMDNSService& p_rService, + stcMDNSSendParameter& p_rSendParameter); + + /** HELPERS **/ + /* UDP CONTEXT */ + bool _callProcess(void); + bool _allocUDPContext(void); + bool _releaseUDPContext(void); + + /* SERVICE QUERY */ + stcMDNSServiceQuery* _allocServiceQuery(void); + bool _removeServiceQuery(stcMDNSServiceQuery* p_pServiceQuery); + bool _removeLegacyServiceQuery(void); + stcMDNSServiceQuery* _findServiceQuery(hMDNSServiceQuery p_hServiceQuery); + stcMDNSServiceQuery* _findLegacyServiceQuery(void); + bool _releaseServiceQueries(void); + stcMDNSServiceQuery* + _findNextServiceQueryByServiceType(const stcMDNS_RRDomain& p_ServiceDomain, + const stcMDNSServiceQuery* p_pPrevServiceQuery); + + /* HOSTNAME */ + bool _setHostname(const char* p_pcHostname); + bool _releaseHostname(void); + + /* SERVICE */ + stcMDNSService* _allocService(const char* p_pcName, const char* p_pcService, + const char* p_pcProtocol, uint16_t p_u16Port); + bool _releaseService(stcMDNSService* p_pService); + bool _releaseServices(void); + + stcMDNSService* _findService(const char* p_pcName, const char* p_pcService, + const char* p_pcProtocol); + stcMDNSService* _findService(const hMDNSService p_hService); + + size_t _countServices(void) const; + + /* SERVICE TXT */ + stcMDNSServiceTxt* _allocServiceTxt(stcMDNSService* p_pService, const char* p_pcKey, + const char* p_pcValue, bool p_bTemp); + bool _releaseServiceTxt(stcMDNSService* p_pService, stcMDNSServiceTxt* p_pTxt); + stcMDNSServiceTxt* _updateServiceTxt(stcMDNSService* p_pService, stcMDNSServiceTxt* p_pTxt, + const char* p_pcValue, bool p_bTemp); + + stcMDNSServiceTxt* _findServiceTxt(stcMDNSService* p_pService, const char* p_pcKey); + stcMDNSServiceTxt* _findServiceTxt(stcMDNSService* p_pService, const hMDNSTxt p_hTxt); + + stcMDNSServiceTxt* _addServiceTxt(stcMDNSService* p_pService, const char* p_pcKey, + const char* p_pcValue, bool p_bTemp); + + stcMDNSServiceTxt* _answerKeyValue(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex); + + bool _collectServiceTxts(stcMDNSService& p_rService); + bool _releaseTempServiceTxts(stcMDNSService& p_rService); + const stcMDNSServiceTxt* _serviceTxts(const char* p_pcName, const char* p_pcService, + const char* p_pcProtocol); + + /* MISC */ #if not defined ESP_8266_MDNS_INCLUDE || defined DEBUG_ESP_MDNS_RESPONDER - bool _printRRDomain(const stcMDNS_RRDomain& p_rRRDomain) const; - bool _printRRAnswer(const MDNSResponder::stcMDNS_RRAnswer& p_RRAnswer) const; + bool _printRRDomain(const stcMDNS_RRDomain& p_rRRDomain) const; + bool _printRRAnswer(const MDNSResponder::stcMDNS_RRAnswer& p_RRAnswer) const; #endif -}; + }; -}// namespace MDNSImplementation +} // namespace MDNSImplementation -}// namespace esp8266 +} // namespace esp8266 -#endif // MDNS_H +#endif // MDNS_H diff --git a/libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp b/libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp index 677456fcc5..2cc32b053b 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp +++ b/libraries/ESP8266mDNS/src/LEAmDNS_Control.cpp @@ -33,7 +33,8 @@ ESP8266mDNS Control.cpp */ -extern "C" { +extern "C" +{ #include "user_interface.h" } @@ -49,2084 +50,2316 @@ namespace esp8266 namespace MDNSImplementation { -/** - CONTROL -*/ - - -/** - MAINTENANCE -*/ - -/* - MDNSResponder::_process + /** + CONTROL + */ - Run the MDNS process. - Is called, every time the UDPContext receives data AND - should be called in every 'loop' by calling 'MDNS::update()'. + /** + MAINTENANCE + */ -*/ -bool MDNSResponder::_process(bool p_bUserContext) -{ + /* + MDNSResponder::_process - bool bResult = true; + Run the MDNS process. + Is called, every time the UDPContext receives data AND + should be called in every 'loop' by calling 'MDNS::update()'. - if (!p_bUserContext) + */ + bool MDNSResponder::_process(bool p_bUserContext) { + bool bResult = true; - if ((m_pUDPContext) && // UDPContext available AND - (m_pUDPContext->next())) // has content + if (!p_bUserContext) { - - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _update: Calling _parseMessage\n"));); - bResult = _parseMessage(); - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parsePacket %s\n"), (bResult ? "succeeded" : "FAILED"));); + if ((m_pUDPContext) && // UDPContext available AND + (m_pUDPContext->next())) // has content + { + // DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _update: Calling + // _parseMessage\n"));); + bResult = _parseMessage(); + // DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parsePacket %s\n"), + // (bResult ? "succeeded" : "FAILED"));); + } } + else + { + bResult = _updateProbeStatus() && // Probing + _checkServiceQueryCache(); // Service query cache check + } + return bResult; } - else + + /* + MDNSResponder::_restart + */ + bool MDNSResponder::_restart(void) { - bResult = _updateProbeStatus() && // Probing - _checkServiceQueryCache(); // Service query cache check + return ((_resetProbeStatus(true /*restart*/)) && // Stop and restart probing + (_allocUDPContext())); // Restart UDP } - return bResult; -} - -/* - MDNSResponder::_restart -*/ -bool MDNSResponder::_restart(void) -{ - - return ((_resetProbeStatus(true/*restart*/)) && // Stop and restart probing - (_allocUDPContext())); // Restart UDP -} -/** - RECEIVING -*/ + /** + RECEIVING + */ -/* - MDNSResponder::_parseMessage -*/ -bool MDNSResponder::_parseMessage(void) -{ - DEBUG_EX_INFO( - unsigned long ulStartTime = millis(); - unsigned uStartMemory = ESP.getFreeHeap(); - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage (Time: %lu ms, heap: %u bytes, from %s(%u), to %s(%u))\n"), ulStartTime, uStartMemory, - IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str(), m_pUDPContext->getRemotePort(), - IPAddress(m_pUDPContext->getDestAddress()).toString().c_str(), m_pUDPContext->getLocalPort()); - ); - //DEBUG_EX_INFO(_udpDump();); - - bool bResult = false; - - stcMDNS_MsgHeader header; - if (_readMDNSMsgHeader(header)) + /* + MDNSResponder::_parseMessage + */ + bool MDNSResponder::_parseMessage(void) { - if (0 == header.m_4bOpcode) // A standard query + DEBUG_EX_INFO( + unsigned long ulStartTime = millis(); unsigned uStartMemory = ESP.getFreeHeap(); + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage (Time: %lu ms, heap: %u " + "bytes, from %s(%u), to %s(%u))\n"), + ulStartTime, uStartMemory, + IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str(), + m_pUDPContext->getRemotePort(), + IPAddress(m_pUDPContext->getDestAddress()).toString().c_str(), + m_pUDPContext->getLocalPort());); + // DEBUG_EX_INFO(_udpDump();); + + bool bResult = false; + + stcMDNS_MsgHeader header; + if (_readMDNSMsgHeader(header)) { - if (header.m_1bQR) // Received a response -> answers to a query + if (0 == header.m_4bOpcode) // A standard query { - //DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage: Reading answers: ID:%u, Q:%u, A:%u, NS:%u, AR:%u\n"), header.m_u16ID, header.m_u16QDCount, header.m_u16ANCount, header.m_u16NSCount, header.m_u16ARCount);); - bResult = _parseResponse(header); + if (header.m_1bQR) // Received a response -> answers to a query + { + // DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage: + // Reading answers: ID:%u, Q:%u, A:%u, NS:%u, AR:%u\n"), header.m_u16ID, + // header.m_u16QDCount, header.m_u16ANCount, header.m_u16NSCount, + // header.m_u16ARCount);); + bResult = _parseResponse(header); + } + else // Received a query (Questions) + { + // DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage: + // Reading query: ID:%u, Q:%u, A:%u, NS:%u, AR:%u\n"), header.m_u16ID, + // header.m_u16QDCount, header.m_u16ANCount, header.m_u16NSCount, + // header.m_u16ARCount);); + bResult = _parseQuery(header); + } } - else // Received a query (Questions) + else { - //DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage: Reading query: ID:%u, Q:%u, A:%u, NS:%u, AR:%u\n"), header.m_u16ID, header.m_u16QDCount, header.m_u16ANCount, header.m_u16NSCount, header.m_u16ARCount);); - bResult = _parseQuery(header); + DEBUG_EX_ERR( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage: Received UNEXPECTED " + "opcode:%u. Ignoring message!\n"), + header.m_4bOpcode);); + m_pUDPContext->flush(); } } else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage: Received UNEXPECTED opcode:%u. Ignoring message!\n"), header.m_4bOpcode);); + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseMessage: FAILED to read header\n"));); m_pUDPContext->flush(); } + DEBUG_EX_INFO(unsigned uFreeHeap = ESP.getFreeHeap(); DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseMessage: Done (%s after %lu ms, ate %i bytes, remaining " + "%u)\n\n"), + (bResult ? "Succeeded" : "FAILED"), (millis() - ulStartTime), + (uStartMemory - uFreeHeap), uFreeHeap);); + return bResult; } - else - { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage: FAILED to read header\n"));); - m_pUDPContext->flush(); - } - DEBUG_EX_INFO( - unsigned uFreeHeap = ESP.getFreeHeap(); - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseMessage: Done (%s after %lu ms, ate %i bytes, remaining %u)\n\n"), (bResult ? "Succeeded" : "FAILED"), (millis() - ulStartTime), (uStartMemory - uFreeHeap), uFreeHeap); - ); - return bResult; -} -/* - MDNSResponder::_parseQuery - - Queries are of interest in two cases: - 1. allow for tiebreaking while probing in the case of a race condition between two instances probing for - the same name at the same time - 2. provide answers to questions for our host domain or any presented service + /* + MDNSResponder::_parseQuery - When reading the questions, a set of (planned) responses is created, eg. a reverse PTR question for the host domain - gets an A (IP address) response, a PTR question for the _services._dns-sd domain gets a PTR (type) response for any - registered service, ... + Queries are of interest in two cases: + 1. allow for tiebreaking while probing in the case of a race condition between two instances + probing for the same name at the same time + 2. provide answers to questions for our host domain or any presented service - As any mDNS responder should be able to handle 'legacy' queries (from DNS clients), this case is handled here also. - Legacy queries have got only one (unicast) question and are directed to the local DNS port (not the multicast port). - - 1. -*/ -bool MDNSResponder::_parseQuery(const MDNSResponder::stcMDNS_MsgHeader& p_MsgHeader) -{ + When reading the questions, a set of (planned) responses is created, eg. a reverse PTR + question for the host domain gets an A (IP address) response, a PTR question for the + _services._dns-sd domain gets a PTR (type) response for any registered service, ... - bool bResult = true; + As any mDNS responder should be able to handle 'legacy' queries (from DNS clients), this + case is handled here also. Legacy queries have got only one (unicast) question and are + directed to the local DNS port (not the multicast port). - stcMDNSSendParameter sendParameter; - uint8_t u8HostOrServiceReplies = 0; - for (uint16_t qd = 0; ((bResult) && (qd < p_MsgHeader.m_u16QDCount)); ++qd) + 1. + */ + bool MDNSResponder::_parseQuery(const MDNSResponder::stcMDNS_MsgHeader& p_MsgHeader) { + bool bResult = true; - stcMDNS_RRQuestion questionRR; - if ((bResult = _readRRQuestion(questionRR))) - { - // Define host replies, BUT only answer queries after probing is done - u8HostOrServiceReplies = - sendParameter.m_u8HostReplyMask |= (((ProbingStatus_Done == m_HostProbeInformation.m_ProbingStatus)) - ? _replyMaskForHost(questionRR.m_Header, 0) - : 0); - DEBUG_EX_INFO(if (u8HostOrServiceReplies) + stcMDNSSendParameter sendParameter; + uint8_t u8HostOrServiceReplies = 0; + for (uint16_t qd = 0; ((bResult) && (qd < p_MsgHeader.m_u16QDCount)); ++qd) { - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Host reply needed 0x%X\n"), u8HostOrServiceReplies); - }); - - // Check tiebreak need for host domain - if (ProbingStatus_InProgress == m_HostProbeInformation.m_ProbingStatus) - { - bool bFullNameMatch = false; - if ((_replyMaskForHost(questionRR.m_Header, &bFullNameMatch)) && - (bFullNameMatch)) - { - // We're in 'probing' state and someone is asking for our host domain: this might be - // a race-condition: Two host with the same domain names try simutanously to probe their domains - // See: RFC 6762, 8.2 (Tiebraking) - // However, we're using a max. reduced approach for tiebreaking here: The higher IP-address wins! - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Possible race-condition for host domain detected while probing.\n"));); - - m_HostProbeInformation.m_bTiebreakNeeded = true; - } - } - - // Define service replies - for (stcMDNSService* pService = m_pServices; pService; pService = pService->m_pNext) + stcMDNS_RRQuestion questionRR; + if ((bResult = _readRRQuestion(questionRR))) { - // Define service replies, BUT only answer queries after probing is done - uint8_t u8ReplyMaskForQuestion = (((ProbingStatus_Done == pService->m_ProbeInformation.m_ProbingStatus)) - ? _replyMaskForService(questionRR.m_Header, *pService, 0) - : 0); - u8HostOrServiceReplies |= (pService->m_u8ReplyMask |= u8ReplyMaskForQuestion); - DEBUG_EX_INFO(if (u8ReplyMaskForQuestion) - { - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Service reply needed for (%s.%s.%s): 0x%X (%s)\n"), (pService->m_pcName ? : m_pcHostname), pService->m_pcService, pService->m_pcProtocol, u8ReplyMaskForQuestion, IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str()); + // Define host replies, BUT only answer queries after probing is done + u8HostOrServiceReplies = sendParameter.m_u8HostReplyMask + |= (((ProbingStatus_Done == m_HostProbeInformation.m_ProbingStatus)) + ? _replyMaskForHost(questionRR.m_Header, 0) + : 0); + DEBUG_EX_INFO(if (u8HostOrServiceReplies) { + DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseQuery: Host reply needed 0x%X\n"), + u8HostOrServiceReplies); }); - // Check tiebreak need for service domain - if (ProbingStatus_InProgress == pService->m_ProbeInformation.m_ProbingStatus) + // Check tiebreak need for host domain + if (ProbingStatus_InProgress == m_HostProbeInformation.m_ProbingStatus) { - bool bFullNameMatch = false; - if ((_replyMaskForService(questionRR.m_Header, *pService, &bFullNameMatch)) && - (bFullNameMatch)) + bool bFullNameMatch = false; + if ((_replyMaskForHost(questionRR.m_Header, &bFullNameMatch)) + && (bFullNameMatch)) { - // We're in 'probing' state and someone is asking for this service domain: this might be - // a race-condition: Two services with the same domain names try simutanously to probe their domains - // See: RFC 6762, 8.2 (Tiebraking) - // However, we're using a max. reduced approach for tiebreaking here: The 'higher' SRV host wins! - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Possible race-condition for service domain %s.%s.%s detected while probing.\n"), (pService->m_pcName ? : m_pcHostname), pService->m_pcService, pService->m_pcProtocol);); + // We're in 'probing' state and someone is asking for our host domain: this + // might be a race-condition: Two host with the same domain names try + // simutanously to probe their domains See: RFC 6762, 8.2 (Tiebraking) + // However, we're using a max. reduced approach for tiebreaking here: The + // higher IP-address wins! + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseQuery: Possible race-condition for host " + "domain detected while probing.\n"));); + + m_HostProbeInformation.m_bTiebreakNeeded = true; + } + } - pService->m_ProbeInformation.m_bTiebreakNeeded = true; + // Define service replies + for (stcMDNSService* pService = m_pServices; pService; pService = pService->m_pNext) + { + // Define service replies, BUT only answer queries after probing is done + uint8_t u8ReplyMaskForQuestion + = (((ProbingStatus_Done == pService->m_ProbeInformation.m_ProbingStatus)) + ? _replyMaskForService(questionRR.m_Header, *pService, 0) + : 0); + u8HostOrServiceReplies |= (pService->m_u8ReplyMask |= u8ReplyMaskForQuestion); + DEBUG_EX_INFO(if (u8ReplyMaskForQuestion) { + DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseQuery: Service reply needed for " + "(%s.%s.%s): 0x%X (%s)\n"), + (pService->m_pcName ?: m_pcHostname), pService->m_pcService, + pService->m_pcProtocol, u8ReplyMaskForQuestion, + IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str()); + }); + + // Check tiebreak need for service domain + if (ProbingStatus_InProgress == pService->m_ProbeInformation.m_ProbingStatus) + { + bool bFullNameMatch = false; + if ((_replyMaskForService(questionRR.m_Header, *pService, &bFullNameMatch)) + && (bFullNameMatch)) + { + // We're in 'probing' state and someone is asking for this service + // domain: this might be a race-condition: Two services with the same + // domain names try simutanously to probe their domains See: RFC + // 6762, 8.2 (Tiebraking) However, we're using a max. reduced approach + // for tiebreaking here: The 'higher' SRV host wins! + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseQuery: Possible race-condition for " + "service domain %s.%s.%s detected while probing.\n"), + (pService->m_pcName ?: m_pcHostname), pService->m_pcService, + pService->m_pcProtocol);); + + pService->m_ProbeInformation.m_bTiebreakNeeded = true; + } } } - } - // Handle unicast and legacy specialities - // If only one question asks for unicast reply, the whole reply packet is send unicast - if (((DNS_MQUERY_PORT != m_pUDPContext->getRemotePort()) || // Unicast (maybe legacy) query OR - (questionRR.m_bUnicast)) && // Expressivly unicast query + // Handle unicast and legacy specialities + // If only one question asks for unicast reply, the whole reply packet is send + // unicast + if (((DNS_MQUERY_PORT != m_pUDPContext->getRemotePort()) + || // Unicast (maybe legacy) query OR + (questionRR.m_bUnicast)) + && // Expressivly unicast query (!sendParameter.m_bUnicast)) - { - - sendParameter.m_bUnicast = true; - sendParameter.m_bCacheFlush = false; - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Unicast response for %s!\n"), IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str());); - - if ((DNS_MQUERY_PORT != m_pUDPContext->getRemotePort()) && // Unicast (maybe legacy) query AND - (1 == p_MsgHeader.m_u16QDCount) && // Only one question AND - ((sendParameter.m_u8HostReplyMask) || // Host replies OR - (u8HostOrServiceReplies))) // Host or service replies available { - // We're a match for this legacy query, BUT - // make sure, that the query comes from a local host - ip_info IPInfo_Local; - ip_info IPInfo_Remote; - if (((IPInfo_Remote.ip.addr = m_pUDPContext->getRemoteAddress())) && - (((wifi_get_ip_info(SOFTAP_IF, &IPInfo_Local)) && - (ip4_addr_netcmp(&IPInfo_Remote.ip, &IPInfo_Local.ip, &IPInfo_Local.netmask))) || // Remote IP in SOFTAP's subnet OR - ((wifi_get_ip_info(STATION_IF, &IPInfo_Local)) && - (ip4_addr_netcmp(&IPInfo_Remote.ip, &IPInfo_Local.ip, &IPInfo_Local.netmask))))) // Remote IP in STATION's subnet + sendParameter.m_bUnicast = true; + sendParameter.m_bCacheFlush = false; + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseQuery: Unicast response for %s!\n"), + IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str());); + + if ((DNS_MQUERY_PORT != m_pUDPContext->getRemotePort()) + && // Unicast (maybe legacy) query AND + (1 == p_MsgHeader.m_u16QDCount) && // Only one question AND + ((sendParameter.m_u8HostReplyMask) || // Host replies OR + (u8HostOrServiceReplies))) // Host or service replies available { - - DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Legacy query from local host %s, id %u!\n"), IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str(), p_MsgHeader.m_u16ID);); - - sendParameter.m_u16ID = p_MsgHeader.m_u16ID; - sendParameter.m_bLegacyQuery = true; - sendParameter.m_pQuestions = new stcMDNS_RRQuestion; - if ((bResult = (0 != sendParameter.m_pQuestions))) + // We're a match for this legacy query, BUT + // make sure, that the query comes from a local host + ip_info IPInfo_Local; + ip_info IPInfo_Remote; + if (((IPInfo_Remote.ip.addr = m_pUDPContext->getRemoteAddress())) + && (((wifi_get_ip_info(SOFTAP_IF, &IPInfo_Local)) + && (ip4_addr_netcmp(&IPInfo_Remote.ip, &IPInfo_Local.ip, + &IPInfo_Local.netmask))) + || // Remote IP in SOFTAP's subnet OR + ((wifi_get_ip_info(STATION_IF, &IPInfo_Local)) + && (ip4_addr_netcmp( + &IPInfo_Remote.ip, &IPInfo_Local.ip, + &IPInfo_Local.netmask))))) // Remote IP in STATION's subnet { - sendParameter.m_pQuestions->m_Header.m_Domain = questionRR.m_Header.m_Domain; - sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Type = questionRR.m_Header.m_Attributes.m_u16Type; - sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Class = questionRR.m_Header.m_Attributes.m_u16Class; + DEBUG_EX_RX(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseQuery: Legacy query from local host " + "%s, id %u!\n"), + IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str(), + p_MsgHeader.m_u16ID);); + + sendParameter.m_u16ID = p_MsgHeader.m_u16ID; + sendParameter.m_bLegacyQuery = true; + sendParameter.m_pQuestions = new stcMDNS_RRQuestion; + if ((bResult = (0 != sendParameter.m_pQuestions))) + { + sendParameter.m_pQuestions->m_Header.m_Domain + = questionRR.m_Header.m_Domain; + sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Type + = questionRR.m_Header.m_Attributes.m_u16Type; + sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Class + = questionRR.m_Header.m_Attributes.m_u16Class; + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseQuery: FAILED to add legacy " + "question!\n"));); + } } else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: FAILED to add legacy question!\n"));); + DEBUG_EX_RX( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Legacy " + "query from NON-LOCAL host!\n"));); + bResult = false; } } - else - { - DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Legacy query from NON-LOCAL host!\n"));); - bResult = false; - } } } - } - else - { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: FAILED to read question!\n"));); - } - } // for questions - - //DEBUG_EX_INFO(if (u8HostOrServiceReplies) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Reply needed: %u (%s: %s->%s)\n"), u8HostOrServiceReplies, clsTimeSyncer::timestr(), IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str(), IPAddress(m_pUDPContext->getDestAddress()).toString().c_str()); } ); - - // Handle known answers - uint32_t u32Answers = (p_MsgHeader.m_u16ANCount + p_MsgHeader.m_u16NSCount + p_MsgHeader.m_u16ARCount); - DEBUG_EX_INFO(if ((u8HostOrServiceReplies) && (u32Answers)) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Known answers(%u):\n"), u32Answers); - }); - - for (uint32_t an = 0; ((bResult) && (an < u32Answers)); ++an) - { - stcMDNS_RRAnswer* pKnownRRAnswer = 0; - if (((bResult = _readRRAnswer(pKnownRRAnswer))) && - (pKnownRRAnswer)) + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseQuery: FAILED to read question!\n"));); + } + } // for questions + + // DEBUG_EX_INFO(if (u8HostOrServiceReplies) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] + // _parseQuery: Reply needed: %u (%s: %s->%s)\n"), u8HostOrServiceReplies, + // clsTimeSyncer::timestr(), + // IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str(), + // IPAddress(m_pUDPContext->getDestAddress()).toString().c_str()); } ); + + // Handle known answers + uint32_t u32Answers + = (p_MsgHeader.m_u16ANCount + p_MsgHeader.m_u16NSCount + p_MsgHeader.m_u16ARCount); + DEBUG_EX_INFO(if ((u8HostOrServiceReplies) && (u32Answers)) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Known answers(%u):\n"), + u32Answers); + }); + + for (uint32_t an = 0; ((bResult) && (an < u32Answers)); ++an) { - - if ((DNS_RRTYPE_ANY != pKnownRRAnswer->m_Header.m_Attributes.m_u16Type) && // No ANY type answer - (DNS_RRCLASS_ANY != pKnownRRAnswer->m_Header.m_Attributes.m_u16Class)) // No ANY class answer + stcMDNS_RRAnswer* pKnownRRAnswer = 0; + if (((bResult = _readRRAnswer(pKnownRRAnswer))) && (pKnownRRAnswer)) { - - // Find match between planned answer (sendParameter.m_u8HostReplyMask) and this 'known answer' - uint8_t u8HostMatchMask = (sendParameter.m_u8HostReplyMask & _replyMaskForHost(pKnownRRAnswer->m_Header)); - if ((u8HostMatchMask) && // The RR in the known answer matches an RR we are planning to send, AND - ((MDNS_HOST_TTL / 2) <= pKnownRRAnswer->m_u32TTL)) // The TTL of the known answer is longer than half of the new host TTL (120s) + if ((DNS_RRTYPE_ANY != pKnownRRAnswer->m_Header.m_Attributes.m_u16Type) + && // No ANY type answer + (DNS_RRCLASS_ANY + != pKnownRRAnswer->m_Header.m_Attributes.m_u16Class)) // No ANY class answer { - - // Compare contents - if (AnswerType_PTR == pKnownRRAnswer->answerType()) + // Find match between planned answer (sendParameter.m_u8HostReplyMask) and this + // 'known answer' + uint8_t u8HostMatchMask = (sendParameter.m_u8HostReplyMask + & _replyMaskForHost(pKnownRRAnswer->m_Header)); + if ((u8HostMatchMask) + && // The RR in the known answer matches an RR we are planning to send, AND + ((MDNS_HOST_TTL / 2) + <= pKnownRRAnswer->m_u32TTL)) // The TTL of the known answer is longer + // than half of the new host TTL (120s) { - stcMDNS_RRDomain hostDomain; - if ((_buildDomainForHost(m_pcHostname, hostDomain)) && - (((stcMDNS_RRAnswerPTR*)pKnownRRAnswer)->m_PTRDomain == hostDomain)) + // Compare contents + if (AnswerType_PTR == pKnownRRAnswer->answerType()) { - // Host domain match -#ifdef MDNS_IP4_SUPPORT - if (u8HostMatchMask & ContentFlag_PTR_IP4) + stcMDNS_RRDomain hostDomain; + if ((_buildDomainForHost(m_pcHostname, hostDomain)) + && (((stcMDNS_RRAnswerPTR*)pKnownRRAnswer)->m_PTRDomain + == hostDomain)) { - // IP4 PTR was asked for, but is already known -> skipping - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: IP4 PTR already known... skipping!\n"));); - sendParameter.m_u8HostReplyMask &= ~ContentFlag_PTR_IP4; - } + // Host domain match +#ifdef MDNS_IP4_SUPPORT + if (u8HostMatchMask & ContentFlag_PTR_IP4) + { + // IP4 PTR was asked for, but is already known -> skipping + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseQuery: IP4 PTR already " + "known... skipping!\n"));); + sendParameter.m_u8HostReplyMask &= ~ContentFlag_PTR_IP4; + } #endif #ifdef MDNS_IP6_SUPPORT - if (u8HostMatchMask & ContentFlag_PTR_IP6) - { - // IP6 PTR was asked for, but is already known -> skipping - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: IP6 PTR already known... skipping!\n"));); - sendParameter.m_u8HostReplyMask &= ~ContentFlag_PTR_IP6; - } + if (u8HostMatchMask & ContentFlag_PTR_IP6) + { + // IP6 PTR was asked for, but is already known -> skipping + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseQuery: IP6 PTR already " + "known... skipping!\n"));); + sendParameter.m_u8HostReplyMask &= ~ContentFlag_PTR_IP6; + } #endif + } } - } - else if (u8HostMatchMask & ContentFlag_A) - { - // IP4 address was asked for -#ifdef MDNS_IP4_SUPPORT - if ((AnswerType_A == pKnownRRAnswer->answerType()) && - (((stcMDNS_RRAnswerA*)pKnownRRAnswer)->m_IPAddress == m_pUDPContext->getInputNetif()->ip_addr)) + else if (u8HostMatchMask & ContentFlag_A) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: IP4 address already known... skipping!\n"));); - sendParameter.m_u8HostReplyMask &= ~ContentFlag_A; - } // else: RData NOT IP4 length !! + // IP4 address was asked for +#ifdef MDNS_IP4_SUPPORT + if ((AnswerType_A == pKnownRRAnswer->answerType()) + && (((stcMDNS_RRAnswerA*)pKnownRRAnswer)->m_IPAddress + == m_pUDPContext->getInputNetif()->ip_addr)) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseQuery: IP4 address already " + "known... skipping!\n"));); + sendParameter.m_u8HostReplyMask &= ~ContentFlag_A; + } // else: RData NOT IP4 length !! #endif - } - else if (u8HostMatchMask & ContentFlag_AAAA) - { - // IP6 address was asked for -#ifdef MDNS_IP6_SUPPORT - if ((AnswerType_AAAA == pAnswerRR->answerType()) && - (((stcMDNS_RRAnswerAAAA*)pAnswerRR)->m_IPAddress == _getResponseMulticastInterface())) + } + else if (u8HostMatchMask & ContentFlag_AAAA) { - - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: IP6 address already known... skipping!\n"));); - sendParameter.m_u8HostReplyMask &= ~ContentFlag_AAAA; - } // else: RData NOT IP6 length !! + // IP6 address was asked for +#ifdef MDNS_IP6_SUPPORT + if ((AnswerType_AAAA == pAnswerRR->answerType()) + && (((stcMDNS_RRAnswerAAAA*)pAnswerRR)->m_IPAddress + == _getResponseMulticastInterface())) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseQuery: IP6 address already " + "known... skipping!\n"));); + sendParameter.m_u8HostReplyMask &= ~ContentFlag_AAAA; + } // else: RData NOT IP6 length !! #endif - } - } // Host match /*and TTL*/ + } + } // Host match /*and TTL*/ - // - // Check host tiebreak possibility - if (m_HostProbeInformation.m_bTiebreakNeeded) - { - stcMDNS_RRDomain hostDomain; - if ((_buildDomainForHost(m_pcHostname, hostDomain)) && - (pKnownRRAnswer->m_Header.m_Domain == hostDomain)) + // + // Check host tiebreak possibility + if (m_HostProbeInformation.m_bTiebreakNeeded) { - // Host domain match -#ifdef MDNS_IP4_SUPPORT - if (AnswerType_A == pKnownRRAnswer->answerType()) + stcMDNS_RRDomain hostDomain; + if ((_buildDomainForHost(m_pcHostname, hostDomain)) + && (pKnownRRAnswer->m_Header.m_Domain == hostDomain)) { - - IPAddress localIPAddress(m_pUDPContext->getInputNetif()->ip_addr); - if (((stcMDNS_RRAnswerA*)pKnownRRAnswer)->m_IPAddress == localIPAddress) - { - // SAME IP address -> We've received an old message from ourselfs (same IP) - DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Tiebreak (IP4) WON (was an old message)!\n"));); - m_HostProbeInformation.m_bTiebreakNeeded = false; - } - else + // Host domain match +#ifdef MDNS_IP4_SUPPORT + if (AnswerType_A == pKnownRRAnswer->answerType()) { - if ((uint32_t)(((stcMDNS_RRAnswerA*)pKnownRRAnswer)->m_IPAddress) > (uint32_t)localIPAddress) // The OTHER IP is 'higher' -> LOST + IPAddress localIPAddress(m_pUDPContext->getInputNetif()->ip_addr); + if (((stcMDNS_RRAnswerA*)pKnownRRAnswer)->m_IPAddress + == localIPAddress) { - // LOST tiebreak - DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Tiebreak (IP4) LOST (lower)!\n"));); - _cancelProbingForHost(); + // SAME IP address -> We've received an old message from + // ourselves (same IP) + DEBUG_EX_RX(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseQuery: Tiebreak (IP4) WON (was " + "an old message)!\n"));); m_HostProbeInformation.m_bTiebreakNeeded = false; } - else // WON tiebreak + else { - //TiebreakState = TiebreakState_Won; // We received an 'old' message from ourselfs -> Just ignore - DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Tiebreak (IP4) WON (higher IP)!\n"));); - m_HostProbeInformation.m_bTiebreakNeeded = false; + if ((uint32_t)(((stcMDNS_RRAnswerA*)pKnownRRAnswer) + ->m_IPAddress) + > (uint32_t) + localIPAddress) // The OTHER IP is 'higher' -> LOST + { + // LOST tiebreak + DEBUG_EX_RX(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseQuery: Tiebreak (IP4) LOST " + "(lower)!\n"));); + _cancelProbingForHost(); + m_HostProbeInformation.m_bTiebreakNeeded = false; + } + else // WON tiebreak + { + // TiebreakState = TiebreakState_Won; // We received an + // 'old' message from ourselves -> Just ignore + DEBUG_EX_RX(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseQuery: Tiebreak (IP4) WON " + "(higher IP)!\n"));); + m_HostProbeInformation.m_bTiebreakNeeded = false; + } } } - } #endif #ifdef MDNS_IP6_SUPPORT - if (AnswerType_AAAA == pAnswerRR->answerType()) - { - // TODO - } + if (AnswerType_AAAA == pAnswerRR->answerType()) + { + // TODO + } #endif - } - } // Host tiebreak possibility - - // Check service answers - for (stcMDNSService* pService = m_pServices; pService; pService = pService->m_pNext) - { - - uint8_t u8ServiceMatchMask = (pService->m_u8ReplyMask & _replyMaskForService(pKnownRRAnswer->m_Header, *pService)); + } + } // Host tiebreak possibility - if ((u8ServiceMatchMask) && // The RR in the known answer matches an RR we are planning to send, AND - ((MDNS_SERVICE_TTL / 2) <= pKnownRRAnswer->m_u32TTL)) // The TTL of the known answer is longer than half of the new service TTL (4500s) + // Check service answers + for (stcMDNSService* pService = m_pServices; pService; + pService = pService->m_pNext) { - - if (AnswerType_PTR == pKnownRRAnswer->answerType()) + uint8_t u8ServiceMatchMask + = (pService->m_u8ReplyMask + & _replyMaskForService(pKnownRRAnswer->m_Header, *pService)); + + if ((u8ServiceMatchMask) && // The RR in the known answer matches an RR we + // are planning to send, AND + ((MDNS_SERVICE_TTL / 2) + <= pKnownRRAnswer + ->m_u32TTL)) // The TTL of the known answer is longer than half + // of the new service TTL (4500s) { - stcMDNS_RRDomain serviceDomain; - if ((u8ServiceMatchMask & ContentFlag_PTR_TYPE) && - (_buildDomainForService(*pService, false, serviceDomain)) && - (serviceDomain == ((stcMDNS_RRAnswerPTR*)pKnownRRAnswer)->m_PTRDomain)) + if (AnswerType_PTR == pKnownRRAnswer->answerType()) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Service type PTR already known... skipping!\n"));); - pService->m_u8ReplyMask &= ~ContentFlag_PTR_TYPE; - } - if ((u8ServiceMatchMask & ContentFlag_PTR_NAME) && - (_buildDomainForService(*pService, true, serviceDomain)) && - (serviceDomain == ((stcMDNS_RRAnswerPTR*)pKnownRRAnswer)->m_PTRDomain)) - { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Service name PTR already known... skipping!\n"));); - pService->m_u8ReplyMask &= ~ContentFlag_PTR_NAME; + stcMDNS_RRDomain serviceDomain; + if ((u8ServiceMatchMask & ContentFlag_PTR_TYPE) + && (_buildDomainForService(*pService, false, serviceDomain)) + && (serviceDomain + == ((stcMDNS_RRAnswerPTR*)pKnownRRAnswer)->m_PTRDomain)) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseQuery: Service type PTR " + "already known... skipping!\n"));); + pService->m_u8ReplyMask &= ~ContentFlag_PTR_TYPE; + } + if ((u8ServiceMatchMask & ContentFlag_PTR_NAME) + && (_buildDomainForService(*pService, true, serviceDomain)) + && (serviceDomain + == ((stcMDNS_RRAnswerPTR*)pKnownRRAnswer)->m_PTRDomain)) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseQuery: Service name PTR " + "already known... skipping!\n"));); + pService->m_u8ReplyMask &= ~ContentFlag_PTR_NAME; + } } - } - else if (u8ServiceMatchMask & ContentFlag_SRV) - { - DEBUG_EX_ERR(if (AnswerType_SRV != pKnownRRAnswer->answerType()) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: ERROR! INVALID answer type (SRV)!\n"));); - stcMDNS_RRDomain hostDomain; - if ((_buildDomainForHost(m_pcHostname, hostDomain)) && - (hostDomain == ((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_SRVDomain)) // Host domain match + else if (u8ServiceMatchMask & ContentFlag_SRV) { - - if ((MDNS_SRV_PRIORITY == ((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_u16Priority) && - (MDNS_SRV_WEIGHT == ((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_u16Weight) && - (pService->m_u16Port == ((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_u16Port)) + DEBUG_EX_ERR(if (AnswerType_SRV != pKnownRRAnswer->answerType()) + DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseQuery: ERROR! " + "INVALID answer type (SRV)!\n"));); + stcMDNS_RRDomain hostDomain; + if ((_buildDomainForHost(m_pcHostname, hostDomain)) + && (hostDomain + == ((stcMDNS_RRAnswerSRV*)pKnownRRAnswer) + ->m_SRVDomain)) // Host domain match { - - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Service SRV answer already known... skipping!\n"));); - pService->m_u8ReplyMask &= ~ContentFlag_SRV; - } // else: Small differences -> send update message + if ((MDNS_SRV_PRIORITY + == ((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_u16Priority) + && (MDNS_SRV_WEIGHT + == ((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_u16Weight) + && (pService->m_u16Port + == ((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_u16Port)) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseQuery: Service SRV answer " + "already known... skipping!\n"));); + pService->m_u8ReplyMask &= ~ContentFlag_SRV; + } // else: Small differences -> send update message + } } - } - else if (u8ServiceMatchMask & ContentFlag_TXT) - { - DEBUG_EX_ERR(if (AnswerType_TXT != pKnownRRAnswer->answerType()) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: ERROR! INVALID answer type (TXT)!\n"));); - _collectServiceTxts(*pService); - if (pService->m_Txts == ((stcMDNS_RRAnswerTXT*)pKnownRRAnswer)->m_Txts) + else if (u8ServiceMatchMask & ContentFlag_TXT) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Service TXT answer already known... skipping!\n"));); - pService->m_u8ReplyMask &= ~ContentFlag_TXT; + DEBUG_EX_ERR(if (AnswerType_TXT != pKnownRRAnswer->answerType()) + DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseQuery: ERROR! " + "INVALID answer type (TXT)!\n"));); + _collectServiceTxts(*pService); + if (pService->m_Txts + == ((stcMDNS_RRAnswerTXT*)pKnownRRAnswer)->m_Txts) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseQuery: Service TXT answer " + "already known... skipping!\n"));); + pService->m_u8ReplyMask &= ~ContentFlag_TXT; + } + _releaseTempServiceTxts(*pService); } - _releaseTempServiceTxts(*pService); - } - } // Service match and enough TTL + } // Service match and enough TTL - // - // Check service tiebreak possibility - if (pService->m_ProbeInformation.m_bTiebreakNeeded) - { - stcMDNS_RRDomain serviceDomain; - if ((_buildDomainForService(*pService, true, serviceDomain)) && - (pKnownRRAnswer->m_Header.m_Domain == serviceDomain)) + // + // Check service tiebreak possibility + if (pService->m_ProbeInformation.m_bTiebreakNeeded) { - // Service domain match - if (AnswerType_SRV == pKnownRRAnswer->answerType()) + stcMDNS_RRDomain serviceDomain; + if ((_buildDomainForService(*pService, true, serviceDomain)) + && (pKnownRRAnswer->m_Header.m_Domain == serviceDomain)) { - stcMDNS_RRDomain hostDomain; - if ((_buildDomainForHost(m_pcHostname, hostDomain)) && - (hostDomain == ((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_SRVDomain)) // Host domain match - { - - // We've received an old message from ourselfs (same SRV) - DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Tiebreak (SRV) won (was an old message)!\n"));); - pService->m_ProbeInformation.m_bTiebreakNeeded = false; - } - else + // Service domain match + if (AnswerType_SRV == pKnownRRAnswer->answerType()) { - if (((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_SRVDomain > hostDomain) // The OTHER domain is 'higher' -> LOST + stcMDNS_RRDomain hostDomain; + if ((_buildDomainForHost(m_pcHostname, hostDomain)) + && (hostDomain + == ((stcMDNS_RRAnswerSRV*)pKnownRRAnswer) + ->m_SRVDomain)) // Host domain match { - // LOST tiebreak - DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Tiebreak (SRV) LOST (lower)!\n"));); - _cancelProbingForService(*pService); + // We've received an old message from ourselves (same SRV) + DEBUG_EX_RX(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseQuery: Tiebreak (SRV) won " + "(was an old message)!\n"));); pService->m_ProbeInformation.m_bTiebreakNeeded = false; } - else // WON tiebreak + else { - //TiebreakState = TiebreakState_Won; // We received an 'old' message from ourselfs -> Just ignore - DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Tiebreak (SRV) won (higher)!\n"));); - pService->m_ProbeInformation.m_bTiebreakNeeded = false; + if (((stcMDNS_RRAnswerSRV*)pKnownRRAnswer)->m_SRVDomain + > hostDomain) // The OTHER domain is 'higher' -> LOST + { + // LOST tiebreak + DEBUG_EX_RX(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseQuery: Tiebreak (SRV) " + "LOST (lower)!\n"));); + _cancelProbingForService(*pService); + pService->m_ProbeInformation.m_bTiebreakNeeded = false; + } + else // WON tiebreak + { + // TiebreakState = TiebreakState_Won; // We received + // an 'old' message from ourselves -> Just ignore + DEBUG_EX_RX(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseQuery: Tiebreak (SRV) " + "won (higher)!\n"));); + pService->m_ProbeInformation.m_bTiebreakNeeded = false; + } } } } - } - } // service tiebreak possibility - } // for services - } // ANY answers - } - else - { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: FAILED to read known answer!\n"));); - } + } // service tiebreak possibility + } // for services + } // ANY answers + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseQuery: FAILED to read known answer!\n"));); + } - if (pKnownRRAnswer) - { - delete pKnownRRAnswer; - pKnownRRAnswer = 0; - } - } // for answers + if (pKnownRRAnswer) + { + delete pKnownRRAnswer; + pKnownRRAnswer = 0; + } + } // for answers - if (bResult) - { - // Check, if a reply is needed - uint8_t u8ReplyNeeded = sendParameter.m_u8HostReplyMask; - for (stcMDNSService* pService = m_pServices; pService; pService = pService->m_pNext) + if (bResult) { - u8ReplyNeeded |= pService->m_u8ReplyMask; - } + // Check, if a reply is needed + uint8_t u8ReplyNeeded = sendParameter.m_u8HostReplyMask; + for (stcMDNSService* pService = m_pServices; pService; pService = pService->m_pNext) + { + u8ReplyNeeded |= pService->m_u8ReplyMask; + } - if (u8ReplyNeeded) - { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Sending answer(0x%X)...\n"), u8ReplyNeeded);); + if (u8ReplyNeeded) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseQuery: Sending answer(0x%X)...\n"), + u8ReplyNeeded);); - sendParameter.m_bResponse = true; - sendParameter.m_bAuthorative = true; + sendParameter.m_bResponse = true; + sendParameter.m_bAuthorative = true; - bResult = _sendMDNSMessage(sendParameter); + bResult = _sendMDNSMessage(sendParameter); + } + DEBUG_EX_INFO(else { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: No reply needed\n")); + }); } - DEBUG_EX_INFO( - else + else { - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: No reply needed\n")); + DEBUG_EX_ERR( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Something FAILED!\n"));); + m_pUDPContext->flush(); } - ); - } - else - { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: Something FAILED!\n"));); - m_pUDPContext->flush(); - } - // - // Check and reset tiebreak-states - if (m_HostProbeInformation.m_bTiebreakNeeded) - { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: UNSOLVED tiebreak-need for host domain!\n"));); - m_HostProbeInformation.m_bTiebreakNeeded = false; - } - for (stcMDNSService* pService = m_pServices; pService; pService = pService->m_pNext) - { - if (pService->m_ProbeInformation.m_bTiebreakNeeded) + // + // Check and reset tiebreak-states + if (m_HostProbeInformation.m_bTiebreakNeeded) + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseQuery: UNSOLVED tiebreak-need for host domain!\n"));); + m_HostProbeInformation.m_bTiebreakNeeded = false; + } + for (stcMDNSService* pService = m_pServices; pService; pService = pService->m_pNext) { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: UNSOLVED tiebreak-need for service domain (%s.%s.%s)\n"), (pService->m_pcName ? : m_pcHostname), pService->m_pcService, pService->m_pcProtocol);); - pService->m_ProbeInformation.m_bTiebreakNeeded = false; + if (pService->m_ProbeInformation.m_bTiebreakNeeded) + { + DEBUG_EX_ERR( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: UNSOLVED " + "tiebreak-need for service domain (%s.%s.%s)\n"), + (pService->m_pcName ?: m_pcHostname), + pService->m_pcService, pService->m_pcProtocol);); + pService->m_ProbeInformation.m_bTiebreakNeeded = false; + } } + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: FAILED!\n")); + }); + return bResult; } - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseQuery: FAILED!\n")); - }); - return bResult; -} - -/* - MDNSResponder::_parseResponse - - Responses are of interest in two cases: - 1. find domain name conflicts while probing - 2. get answers to service queries - - In both cases any included questions are ignored - - 1. If any answer has a domain name similar to one of the domain names we're planning to use (and are probing for), - then we've got a 'probing conflict'. The conflict has to be solved on our side of the conflict (eg. by - setting a new hostname and restart probing). The callback 'm_fnProbeResultCallback' is called with - 'p_bProbeResult=false' in this case. - - 2. Service queries like '_http._tcp.local' will (if available) produce PTR, SRV, TXT and A/AAAA answers. - All stored answers are pivoted by the service instance name (from the PTR record). Other answer parts, - like host domain or IP address are than attached to this element. - Any answer part carries a TTL, this is also stored (incl. the reception time); if the TTL is '0' the - answer (part) is withdrawn by the sender and should be removed from any cache. RFC 6762, 10.1 proposes to - set the caches TTL-value to 1 second in such a case and to delete the item only, if no update has - has taken place in this second. - Answer parts may arrive in 'unsorted' order, so they are grouped into three levels: - Level 1: PRT - names the service instance (and is used as pivot), voids all other parts if is withdrawn or outdates - Level 2: SRV - links the instance name to a host domain and port, voids A/AAAA parts if is withdrawn or outdates - TXT - links the instance name to services TXTs - Level 3: A/AAAA - links the host domain to an IP address -*/ -bool MDNSResponder::_parseResponse(const MDNSResponder::stcMDNS_MsgHeader& p_MsgHeader) -{ - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse\n"));); - //DEBUG_EX_INFO(_udpDump();); - bool bResult = false; - - // A response should be the result of a query or a probe - if ((_hasServiceQueriesWaitingForAnswers()) || // Waiting for query answers OR - (_hasProbesWaitingForAnswers())) // Probe responses + /* + MDNSResponder::_parseResponse + + Responses are of interest in two cases: + 1. find domain name conflicts while probing + 2. get answers to service queries + + In both cases any included questions are ignored + + 1. If any answer has a domain name similar to one of the domain names we're planning to use + (and are probing for), then we've got a 'probing conflict'. The conflict has to be solved on + our side of the conflict (eg. by setting a new hostname and restart probing). The callback + 'm_fnProbeResultCallback' is called with 'p_bProbeResult=false' in this case. + + 2. Service queries like '_http._tcp.local' will (if available) produce PTR, SRV, TXT and + A/AAAA answers. All stored answers are pivoted by the service instance name (from the PTR + record). Other answer parts, like host domain or IP address are than attached to this + element. Any answer part carries a TTL, this is also stored (incl. the reception time); if + the TTL is '0' the answer (part) is withdrawn by the sender and should be removed from any + cache. RFC 6762, 10.1 proposes to set the caches TTL-value to 1 second in such a case and to + delete the item only, if no update has has taken place in this second. Answer parts may + arrive in 'unsorted' order, so they are grouped into three levels: Level 1: PRT - names the + service instance (and is used as pivot), voids all other parts if is withdrawn or outdates + Level 2: SRV - links the instance name to a host domain and port, voids A/AAAA parts if is + withdrawn or outdates TXT - links the instance name to services TXTs Level 3: A/AAAA - links + the host domain to an IP address + */ + bool MDNSResponder::_parseResponse(const MDNSResponder::stcMDNS_MsgHeader& p_MsgHeader) { + // DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse\n"));); + // DEBUG_EX_INFO(_udpDump();); - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: Received a response\n")); - //_udpDump(); - ); + bool bResult = false; - bResult = true; - // - // Ignore questions here - stcMDNS_RRQuestion dummyRRQ; - for (uint16_t qd = 0; ((bResult) && (qd < p_MsgHeader.m_u16QDCount)); ++qd) + // A response should be the result of a query or a probe + if ((_hasServiceQueriesWaitingForAnswers()) || // Waiting for query answers OR + (_hasProbesWaitingForAnswers())) // Probe responses { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: Received a response containing a question... ignoring!\n"));); - bResult = _readRRQuestion(dummyRRQ); - } // for queries + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseResponse: Received a response\n")); + //_udpDump(); + ); - // - // Read and collect answers - stcMDNS_RRAnswer* pCollectedRRAnswers = 0; - uint32_t u32NumberOfAnswerRRs = (p_MsgHeader.m_u16ANCount + p_MsgHeader.m_u16NSCount + p_MsgHeader.m_u16ARCount); - for (uint32_t an = 0; ((bResult) && (an < u32NumberOfAnswerRRs)); ++an) - { - stcMDNS_RRAnswer* pRRAnswer = 0; - if (((bResult = _readRRAnswer(pRRAnswer))) && - (pRRAnswer)) + bResult = true; + // + // Ignore questions here + stcMDNS_RRQuestion dummyRRQ; + for (uint16_t qd = 0; ((bResult) && (qd < p_MsgHeader.m_u16QDCount)); ++qd) { - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: ADDING answer!\n"));); - pRRAnswer->m_pNext = pCollectedRRAnswers; - pCollectedRRAnswers = pRRAnswer; - } - else + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: Received a " + "response containing a question... ignoring!\n"));); + bResult = _readRRQuestion(dummyRRQ); + } // for queries + + // + // Read and collect answers + stcMDNS_RRAnswer* pCollectedRRAnswers = 0; + uint32_t u32NumberOfAnswerRRs + = (p_MsgHeader.m_u16ANCount + p_MsgHeader.m_u16NSCount + p_MsgHeader.m_u16ARCount); + for (uint32_t an = 0; ((bResult) && (an < u32NumberOfAnswerRRs)); ++an) { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: FAILED to read answer!\n"));); - if (pRRAnswer) + stcMDNS_RRAnswer* pRRAnswer = 0; + if (((bResult = _readRRAnswer(pRRAnswer))) && (pRRAnswer)) + { + // DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: + // ADDING answer!\n"));); + pRRAnswer->m_pNext = pCollectedRRAnswers; + pCollectedRRAnswers = pRRAnswer; + } + else { - delete pRRAnswer; - pRRAnswer = 0; + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseResponse: FAILED to read answer!\n"));); + if (pRRAnswer) + { + delete pRRAnswer; + pRRAnswer = 0; + } + bResult = false; } - bResult = false; + } // for answers + + // + // Process answers + if (bResult) + { + bResult = ((!pCollectedRRAnswers) || (_processAnswers(pCollectedRRAnswers))); + } + else // Some failure while reading answers + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _parseResponse: FAILED to read answers!\n"));); + m_pUDPContext->flush(); } - } // for answers - // - // Process answers - if (bResult) - { - bResult = ((!pCollectedRRAnswers) || - (_processAnswers(pCollectedRRAnswers))); + // Delete collected answers + while (pCollectedRRAnswers) + { + // DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: + // DELETING answer!\n"));); + stcMDNS_RRAnswer* pNextAnswer = pCollectedRRAnswers->m_pNext; + delete pCollectedRRAnswers; + pCollectedRRAnswers = pNextAnswer; + } } - else // Some failure while reading answers + else // Received an unexpected response -> ignore { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: FAILED to read answers!\n"));); + /* DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: Received an + unexpected response... ignoring!\nDUMP:\n")); bool bDumpResult = true; for + (uint16_t qd=0; ((bDumpResult) && (qdflush(); + bResult = true; } - - // Delete collected answers - while (pCollectedRRAnswers) - { - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: DELETING answer!\n"));); - stcMDNS_RRAnswer* pNextAnswer = pCollectedRRAnswers->m_pNext; - delete pCollectedRRAnswers; - pCollectedRRAnswers = pNextAnswer; - } - } - else // Received an unexpected response -> ignore - { - /* DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: Received an unexpected response... ignoring!\nDUMP:\n")); - bool bDumpResult = true; - for (uint16_t qd=0; ((bDumpResult) && (qdflush(); - bResult = true; + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: FAILED!\n")); + }); + return bResult; } - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _parseResponse: FAILED!\n")); - }); - return bResult; -} -/* - MDNSResponder::_processAnswers - Host: - A (0x01): eg. esp8266.local A OP TTL 123.456.789.012 - AAAA (01Cx): eg. esp8266.local AAAA OP TTL 1234:5678::90 - PTR (0x0C, IP4): eg. 012.789.456.123.in-addr.arpa PTR OP TTL esp8266.local - PTR (0x0C, IP6): eg. 90.0.0.0.0.0.0.0.0.0.0.0.78.56.34.12.ip6.arpa PTR OP TTL esp8266.local - Service: - PTR (0x0C, srv name): eg. _http._tcp.local PTR OP TTL MyESP._http._tcp.local - PTR (0x0C, srv type): eg. _services._dns-sd._udp.local PTR OP TTL _http._tcp.local - SRV (0x21): eg. MyESP._http._tcp.local SRV OP TTL PRIORITY WEIGHT PORT esp8266.local - TXT (0x10): eg. MyESP._http._tcp.local TXT OP TTL c#=1 + /* + MDNSResponder::_processAnswers + Host: + A (0x01): eg. esp8266.local A OP TTL 123.456.789.012 + AAAA (01Cx): eg. esp8266.local AAAA OP TTL 1234:5678::90 + PTR (0x0C, IP4): eg. 012.789.456.123.in-addr.arpa PTR OP TTL esp8266.local + PTR (0x0C, IP6): eg. 90.0.0.0.0.0.0.0.0.0.0.0.78.56.34.12.ip6.arpa PTR OP TTL + esp8266.local Service: PTR (0x0C, srv name): eg. _http._tcp.local PTR OP TTL + MyESP._http._tcp.local PTR (0x0C, srv type): eg. _services._dns-sd._udp.local PTR OP TTL + _http._tcp.local SRV (0x21): eg. MyESP._http._tcp.local SRV OP TTL PRIORITY + WEIGHT PORT esp8266.local TXT (0x10): eg. MyESP._http._tcp.local TXT OP TTL c#=1 + + */ + bool MDNSResponder::_processAnswers(const MDNSResponder::stcMDNS_RRAnswer* p_pAnswers) + { + bool bResult = false; -*/ -bool MDNSResponder::_processAnswers(const MDNSResponder::stcMDNS_RRAnswer* p_pAnswers) -{ - - bool bResult = false; - - if (p_pAnswers) - { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAnswers: Processing answers...\n"));); - bResult = true; - - // Answers may arrive in an unexpected order. So we loop our answers as long, as we - // can connect new information to service queries - bool bFoundNewKeyAnswer; - do + if (p_pAnswers) { - bFoundNewKeyAnswer = false; - - const stcMDNS_RRAnswer* pRRAnswer = p_pAnswers; - while ((pRRAnswer) && - (bResult)) + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _processAnswers: Processing answers...\n"));); + bResult = true; + + // Answers may arrive in an unexpected order. So we loop our answers as long, as we + // can connect new information to service queries + bool bFoundNewKeyAnswer; + do { - // 1. level answer (PTR) - if (AnswerType_PTR == pRRAnswer->answerType()) - { - // eg. _http._tcp.local PTR xxxx xx MyESP._http._tcp.local - bResult = _processPTRAnswer((stcMDNS_RRAnswerPTR*)pRRAnswer, bFoundNewKeyAnswer); // May 'enable' new SRV or TXT answers to be linked to queries - } - // 2. level answers - // SRV -> host domain and port - else if (AnswerType_SRV == pRRAnswer->answerType()) - { - // eg. MyESP_http._tcp.local SRV xxxx xx yy zz 5000 esp8266.local - bResult = _processSRVAnswer((stcMDNS_RRAnswerSRV*)pRRAnswer, bFoundNewKeyAnswer); // May 'enable' new A/AAAA answers to be linked to queries - } - // TXT -> Txts - else if (AnswerType_TXT == pRRAnswer->answerType()) + bFoundNewKeyAnswer = false; + + const stcMDNS_RRAnswer* pRRAnswer = p_pAnswers; + while ((pRRAnswer) && (bResult)) { - // eg. MyESP_http._tcp.local TXT xxxx xx c#=1 - bResult = _processTXTAnswer((stcMDNS_RRAnswerTXT*)pRRAnswer); - } - // 3. level answers + // 1. level answer (PTR) + if (AnswerType_PTR == pRRAnswer->answerType()) + { + // eg. _http._tcp.local PTR xxxx xx MyESP._http._tcp.local + bResult = _processPTRAnswer( + (stcMDNS_RRAnswerPTR*)pRRAnswer, + bFoundNewKeyAnswer); // May 'enable' new SRV or TXT answers to be + // linked to queries + } + // 2. level answers + // SRV -> host domain and port + else if (AnswerType_SRV == pRRAnswer->answerType()) + { + // eg. MyESP_http._tcp.local SRV xxxx xx yy zz 5000 esp8266.local + bResult = _processSRVAnswer( + (stcMDNS_RRAnswerSRV*)pRRAnswer, + bFoundNewKeyAnswer); // May 'enable' new A/AAAA answers to be linked to + // queries + } + // TXT -> Txts + else if (AnswerType_TXT == pRRAnswer->answerType()) + { + // eg. MyESP_http._tcp.local TXT xxxx xx c#=1 + bResult = _processTXTAnswer((stcMDNS_RRAnswerTXT*)pRRAnswer); + } + // 3. level answers #ifdef MDNS_IP4_SUPPORT - // A -> IP4Address - else if (AnswerType_A == pRRAnswer->answerType()) - { - // eg. esp8266.local A xxxx xx 192.168.2.120 - bResult = _processAAnswer((stcMDNS_RRAnswerA*)pRRAnswer); - } + // A -> IP4Address + else if (AnswerType_A == pRRAnswer->answerType()) + { + // eg. esp8266.local A xxxx xx 192.168.2.120 + bResult = _processAAnswer((stcMDNS_RRAnswerA*)pRRAnswer); + } #endif #ifdef MDNS_IP6_SUPPORT - // AAAA -> IP6Address - else if (AnswerType_AAAA == pRRAnswer->answerType()) - { - // eg. esp8266.local AAAA xxxx xx 09cf::0c - bResult = _processAAAAAnswer((stcMDNS_RRAnswerAAAA*)pRRAnswer); - } + // AAAA -> IP6Address + else if (AnswerType_AAAA == pRRAnswer->answerType()) + { + // eg. esp8266.local AAAA xxxx xx 09cf::0c + bResult = _processAAAAAnswer((stcMDNS_RRAnswerAAAA*)pRRAnswer); + } #endif - // Finally check for probing conflicts - // Host domain - if ((ProbingStatus_InProgress == m_HostProbeInformation.m_ProbingStatus) && - ((AnswerType_A == pRRAnswer->answerType()) || - (AnswerType_AAAA == pRRAnswer->answerType()))) - { - - stcMDNS_RRDomain hostDomain; - if ((_buildDomainForHost(m_pcHostname, hostDomain)) && - (pRRAnswer->m_Header.m_Domain == hostDomain)) + // Finally check for probing conflicts + // Host domain + if ((ProbingStatus_InProgress == m_HostProbeInformation.m_ProbingStatus) + && ((AnswerType_A == pRRAnswer->answerType()) + || (AnswerType_AAAA == pRRAnswer->answerType()))) { - - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAnswers: Probing CONFLICT found with: %s.local\n"), m_pcHostname);); - _cancelProbingForHost(); + stcMDNS_RRDomain hostDomain; + if ((_buildDomainForHost(m_pcHostname, hostDomain)) + && (pRRAnswer->m_Header.m_Domain == hostDomain)) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _processAnswers: Probing CONFLICT found " + "with: %s.local\n"), + m_pcHostname);); + _cancelProbingForHost(); + } } - } - // Service domains - for (stcMDNSService* pService = m_pServices; pService; pService = pService->m_pNext) - { - if ((ProbingStatus_InProgress == pService->m_ProbeInformation.m_ProbingStatus) && - ((AnswerType_TXT == pRRAnswer->answerType()) || - (AnswerType_SRV == pRRAnswer->answerType()))) + // Service domains + for (stcMDNSService* pService = m_pServices; pService; + pService = pService->m_pNext) { - - stcMDNS_RRDomain serviceDomain; - if ((_buildDomainForService(*pService, true, serviceDomain)) && - (pRRAnswer->m_Header.m_Domain == serviceDomain)) + if ((ProbingStatus_InProgress + == pService->m_ProbeInformation.m_ProbingStatus) + && ((AnswerType_TXT == pRRAnswer->answerType()) + || (AnswerType_SRV == pRRAnswer->answerType()))) { - - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAnswers: Probing CONFLICT found with: %s.%s.%s\n"), (pService->m_pcName ? : m_pcHostname), pService->m_pcService, pService->m_pcProtocol);); - _cancelProbingForService(*pService); + stcMDNS_RRDomain serviceDomain; + if ((_buildDomainForService(*pService, true, serviceDomain)) + && (pRRAnswer->m_Header.m_Domain == serviceDomain)) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _processAnswers: Probing CONFLICT found " + "with: %s.%s.%s\n"), + (pService->m_pcName ?: m_pcHostname), pService->m_pcService, + pService->m_pcProtocol);); + _cancelProbingForService(*pService); + } } } - } - pRRAnswer = pRRAnswer->m_pNext; // Next collected answer - } // while (answers) - } while ((bFoundNewKeyAnswer) && - (bResult)); - } // else: No answers provided - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAnswers: FAILED!\n")); - }); - return bResult; -} - -/* - MDNSResponder::_processPTRAnswer -*/ -bool MDNSResponder::_processPTRAnswer(const MDNSResponder::stcMDNS_RRAnswerPTR* p_pPTRAnswer, - bool& p_rbFoundNewKeyAnswer) -{ - - bool bResult = false; + pRRAnswer = pRRAnswer->m_pNext; // Next collected answer + } // while (answers) + } while ((bFoundNewKeyAnswer) && (bResult)); + } // else: No answers provided + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAnswers: FAILED!\n")); + }); + return bResult; + } - if ((bResult = (0 != p_pPTRAnswer))) + /* + MDNSResponder::_processPTRAnswer + */ + bool MDNSResponder::_processPTRAnswer(const MDNSResponder::stcMDNS_RRAnswerPTR* p_pPTRAnswer, + bool& p_rbFoundNewKeyAnswer) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processPTRAnswer: Processing PTR answers...\n"));); - // eg. _http._tcp.local PTR xxxx xx MyESP._http._tcp.local - // Check pending service queries for eg. '_http._tcp' + bool bResult = false; - stcMDNSServiceQuery* pServiceQuery = _findNextServiceQueryByServiceType(p_pPTRAnswer->m_Header.m_Domain, 0); - while (pServiceQuery) + if ((bResult = (0 != p_pPTRAnswer))) { - if (pServiceQuery->m_bAwaitingAnswers) + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _processPTRAnswer: Processing PTR answers...\n"));); + // eg. _http._tcp.local PTR xxxx xx MyESP._http._tcp.local + // Check pending service queries for eg. '_http._tcp' + + stcMDNSServiceQuery* pServiceQuery + = _findNextServiceQueryByServiceType(p_pPTRAnswer->m_Header.m_Domain, 0); + while (pServiceQuery) { - // Find answer for service domain (eg. MyESP._http._tcp.local) - stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->findAnswerForServiceDomain(p_pPTRAnswer->m_PTRDomain); - if (pSQAnswer) // existing answer + if (pServiceQuery->m_bAwaitingAnswers) { - if (p_pPTRAnswer->m_u32TTL) // Received update message - { - pSQAnswer->m_TTLServiceDomain.set(p_pPTRAnswer->m_u32TTL); // Update TTL tag - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processPTRAnswer: Updated TTL(%d) for "), (int)p_pPTRAnswer->m_u32TTL); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR("\n")); - ); - } - else // received goodbye-message + // Find answer for service domain (eg. MyESP._http._tcp.local) + stcMDNSServiceQuery::stcAnswer* pSQAnswer + = pServiceQuery->findAnswerForServiceDomain(p_pPTRAnswer->m_PTRDomain); + if (pSQAnswer) // existing answer { - pSQAnswer->m_TTLServiceDomain.prepareDeletion(); // Prepare answer deletion according to RFC 6762, 10.1 - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processPTRAnswer: 'Goodbye' received for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR("\n")); - ); + if (p_pPTRAnswer->m_u32TTL) // Received update message + { + pSQAnswer->m_TTLServiceDomain.set( + p_pPTRAnswer->m_u32TTL); // Update TTL tag + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _processPTRAnswer: Updated TTL(%d) for "), + (int)p_pPTRAnswer->m_u32TTL); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR("\n"));); + } + else // received goodbye-message + { + pSQAnswer->m_TTLServiceDomain + .prepareDeletion(); // Prepare answer deletion according to RFC + // 6762, 10.1 + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _processPTRAnswer: 'Goodbye' received for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR("\n"));); + } } - } - else if ((p_pPTRAnswer->m_u32TTL) && // Not just a goodbye-message - ((pSQAnswer = new stcMDNSServiceQuery::stcAnswer))) // Not yet included -> add answer - { - pSQAnswer->m_ServiceDomain = p_pPTRAnswer->m_PTRDomain; - pSQAnswer->m_u32ContentFlags |= ServiceQueryAnswerType_ServiceDomain; - pSQAnswer->m_TTLServiceDomain.set(p_pPTRAnswer->m_u32TTL); - pSQAnswer->releaseServiceDomain(); - - bResult = pServiceQuery->addAnswer(pSQAnswer); - p_rbFoundNewKeyAnswer = true; - if (pServiceQuery->m_fnCallback) + else if ((p_pPTRAnswer->m_u32TTL) && // Not just a goodbye-message + ((pSQAnswer + = new stcMDNSServiceQuery::stcAnswer))) // Not yet included -> add + // answer { - MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); - pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_ServiceDomain), true); + pSQAnswer->m_ServiceDomain = p_pPTRAnswer->m_PTRDomain; + pSQAnswer->m_u32ContentFlags |= ServiceQueryAnswerType_ServiceDomain; + pSQAnswer->m_TTLServiceDomain.set(p_pPTRAnswer->m_u32TTL); + pSQAnswer->releaseServiceDomain(); + + bResult = pServiceQuery->addAnswer(pSQAnswer); + p_rbFoundNewKeyAnswer = true; + if (pServiceQuery->m_fnCallback) + { + MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, + pServiceQuery->indexOfAnswer(pSQAnswer)); + pServiceQuery->m_fnCallback( + serviceInfo, + static_cast(ServiceQueryAnswerType_ServiceDomain), + true); + } } } + pServiceQuery = _findNextServiceQueryByServiceType(p_pPTRAnswer->m_Header.m_Domain, + pServiceQuery); } - pServiceQuery = _findNextServiceQueryByServiceType(p_pPTRAnswer->m_Header.m_Domain, pServiceQuery); - } - } // else: No p_pPTRAnswer - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processPTRAnswer: FAILED!\n")); - }); - return bResult; -} - -/* - MDNSResponder::_processSRVAnswer -*/ -bool MDNSResponder::_processSRVAnswer(const MDNSResponder::stcMDNS_RRAnswerSRV* p_pSRVAnswer, - bool& p_rbFoundNewKeyAnswer) -{ - - bool bResult = false; + } // else: No p_pPTRAnswer + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processPTRAnswer: FAILED!\n")); + }); + return bResult; + } - if ((bResult = (0 != p_pSRVAnswer))) + /* + MDNSResponder::_processSRVAnswer + */ + bool MDNSResponder::_processSRVAnswer(const MDNSResponder::stcMDNS_RRAnswerSRV* p_pSRVAnswer, + bool& p_rbFoundNewKeyAnswer) { - // eg. MyESP._http._tcp.local SRV xxxx xx yy zz 5000 esp8266.local + bool bResult = false; - stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; - while (pServiceQuery) + if ((bResult = (0 != p_pSRVAnswer))) { - stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->findAnswerForServiceDomain(p_pSRVAnswer->m_Header.m_Domain); - if (pSQAnswer) // Answer for this service domain (eg. MyESP._http._tcp.local) available + // eg. MyESP._http._tcp.local SRV xxxx xx yy zz 5000 esp8266.local + + stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; + while (pServiceQuery) { - if (p_pSRVAnswer->m_u32TTL) // First or update message (TTL != 0) + stcMDNSServiceQuery::stcAnswer* pSQAnswer + = pServiceQuery->findAnswerForServiceDomain(p_pSRVAnswer->m_Header.m_Domain); + if (pSQAnswer) // Answer for this service domain (eg. MyESP._http._tcp.local) + // available { - pSQAnswer->m_TTLHostDomainAndPort.set(p_pSRVAnswer->m_u32TTL); // Update TTL tag - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processSRVAnswer: Updated TTL(%d) for "), (int)p_pSRVAnswer->m_u32TTL); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" host domain and port\n")); - ); - // Host domain & Port - if ((pSQAnswer->m_HostDomain != p_pSRVAnswer->m_SRVDomain) || - (pSQAnswer->m_u16Port != p_pSRVAnswer->m_u16Port)) + if (p_pSRVAnswer->m_u32TTL) // First or update message (TTL != 0) { - - pSQAnswer->m_HostDomain = p_pSRVAnswer->m_SRVDomain; - pSQAnswer->releaseHostDomain(); - pSQAnswer->m_u16Port = p_pSRVAnswer->m_u16Port; - pSQAnswer->m_u32ContentFlags |= ServiceQueryAnswerType_HostDomainAndPort; - - p_rbFoundNewKeyAnswer = true; - if (pServiceQuery->m_fnCallback) + pSQAnswer->m_TTLHostDomainAndPort.set( + p_pSRVAnswer->m_u32TTL); // Update TTL tag + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _processSRVAnswer: Updated TTL(%d) for "), + (int)p_pSRVAnswer->m_u32TTL); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" host domain and port\n"));); + // Host domain & Port + if ((pSQAnswer->m_HostDomain != p_pSRVAnswer->m_SRVDomain) + || (pSQAnswer->m_u16Port != p_pSRVAnswer->m_u16Port)) { - MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); - pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_HostDomainAndPort), true); + pSQAnswer->m_HostDomain = p_pSRVAnswer->m_SRVDomain; + pSQAnswer->releaseHostDomain(); + pSQAnswer->m_u16Port = p_pSRVAnswer->m_u16Port; + pSQAnswer->m_u32ContentFlags + |= ServiceQueryAnswerType_HostDomainAndPort; + + p_rbFoundNewKeyAnswer = true; + if (pServiceQuery->m_fnCallback) + { + MDNSServiceInfo serviceInfo( + *this, (hMDNSServiceQuery)pServiceQuery, + pServiceQuery->indexOfAnswer(pSQAnswer)); + pServiceQuery->m_fnCallback( + serviceInfo, + static_cast( + ServiceQueryAnswerType_HostDomainAndPort), + true); + } } } + else // Goodby message + { + pSQAnswer->m_TTLHostDomainAndPort + .prepareDeletion(); // Prepare answer deletion according to RFC + // 6762, 10.1 + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _processSRVAnswer: 'Goodbye' received for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" host domain and port\n"));); + } } - else // Goodby message - { - pSQAnswer->m_TTLHostDomainAndPort.prepareDeletion(); // Prepare answer deletion according to RFC 6762, 10.1 - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processSRVAnswer: 'Goodbye' received for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" host domain and port\n")); - ); - } - } - pServiceQuery = pServiceQuery->m_pNext; - } // while(service query) - } // else: No p_pSRVAnswer - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processSRVAnswer: FAILED!\n")); - }); - return bResult; -} - -/* - MDNSResponder::_processTXTAnswer -*/ -bool MDNSResponder::_processTXTAnswer(const MDNSResponder::stcMDNS_RRAnswerTXT* p_pTXTAnswer) -{ - - bool bResult = false; + pServiceQuery = pServiceQuery->m_pNext; + } // while(service query) + } // else: No p_pSRVAnswer + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processSRVAnswer: FAILED!\n")); + }); + return bResult; + } - if ((bResult = (0 != p_pTXTAnswer))) + /* + MDNSResponder::_processTXTAnswer + */ + bool MDNSResponder::_processTXTAnswer(const MDNSResponder::stcMDNS_RRAnswerTXT* p_pTXTAnswer) { - // eg. MyESP._http._tcp.local TXT xxxx xx c#=1 + bool bResult = false; - stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; - while (pServiceQuery) + if ((bResult = (0 != p_pTXTAnswer))) { - stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->findAnswerForServiceDomain(p_pTXTAnswer->m_Header.m_Domain); - if (pSQAnswer) // Answer for this service domain (eg. MyESP._http._tcp.local) available + // eg. MyESP._http._tcp.local TXT xxxx xx c#=1 + + stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; + while (pServiceQuery) { - if (p_pTXTAnswer->m_u32TTL) // First or update message + stcMDNSServiceQuery::stcAnswer* pSQAnswer + = pServiceQuery->findAnswerForServiceDomain(p_pTXTAnswer->m_Header.m_Domain); + if (pSQAnswer) // Answer for this service domain (eg. MyESP._http._tcp.local) + // available { - pSQAnswer->m_TTLTxts.set(p_pTXTAnswer->m_u32TTL); // Update TTL tag - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processTXTAnswer: Updated TTL(%d) for "), (int)p_pTXTAnswer->m_u32TTL); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" TXTs\n")); - ); - if (!pSQAnswer->m_Txts.compare(p_pTXTAnswer->m_Txts)) + if (p_pTXTAnswer->m_u32TTL) // First or update message { - pSQAnswer->m_Txts = p_pTXTAnswer->m_Txts; - pSQAnswer->m_u32ContentFlags |= ServiceQueryAnswerType_Txts; - pSQAnswer->releaseTxts(); - - if (pServiceQuery->m_fnCallback) + pSQAnswer->m_TTLTxts.set(p_pTXTAnswer->m_u32TTL); // Update TTL tag + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _processTXTAnswer: Updated TTL(%d) for "), + (int)p_pTXTAnswer->m_u32TTL); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" TXTs\n"));); + if (!pSQAnswer->m_Txts.compare(p_pTXTAnswer->m_Txts)) { - MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); - pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_Txts), true); + pSQAnswer->m_Txts = p_pTXTAnswer->m_Txts; + pSQAnswer->m_u32ContentFlags |= ServiceQueryAnswerType_Txts; + pSQAnswer->releaseTxts(); + + if (pServiceQuery->m_fnCallback) + { + MDNSServiceInfo serviceInfo( + *this, (hMDNSServiceQuery)pServiceQuery, + pServiceQuery->indexOfAnswer(pSQAnswer)); + pServiceQuery->m_fnCallback( + serviceInfo, + static_cast(ServiceQueryAnswerType_Txts), true); + } } } + else // Goodby message + { + pSQAnswer->m_TTLTxts.prepareDeletion(); // Prepare answer deletion + // according to RFC 6762, 10.1 + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _processTXTAnswer: 'Goodbye' received for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" TXTs\n"));); + } } - else // Goodby message - { - pSQAnswer->m_TTLTxts.prepareDeletion(); // Prepare answer deletion according to RFC 6762, 10.1 - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processTXTAnswer: 'Goodbye' received for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" TXTs\n")); - ); - } - } - pServiceQuery = pServiceQuery->m_pNext; - } // while(service query) - } // else: No p_pTXTAnswer - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processTXTAnswer: FAILED!\n")); - }); - return bResult; -} + pServiceQuery = pServiceQuery->m_pNext; + } // while(service query) + } // else: No p_pTXTAnswer + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processTXTAnswer: FAILED!\n")); + }); + return bResult; + } #ifdef MDNS_IP4_SUPPORT -/* - MDNSResponder::_processAAnswer -*/ -bool MDNSResponder::_processAAnswer(const MDNSResponder::stcMDNS_RRAnswerA* p_pAAnswer) -{ - - bool bResult = false; - - if ((bResult = (0 != p_pAAnswer))) + /* + MDNSResponder::_processAAnswer + */ + bool MDNSResponder::_processAAnswer(const MDNSResponder::stcMDNS_RRAnswerA* p_pAAnswer) { - // eg. esp8266.local A xxxx xx 192.168.2.120 + bool bResult = false; - stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; - while (pServiceQuery) + if ((bResult = (0 != p_pAAnswer))) { - stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->findAnswerForHostDomain(p_pAAnswer->m_Header.m_Domain); - if (pSQAnswer) // Answer for this host domain (eg. esp8266.local) available + // eg. esp8266.local A xxxx xx 192.168.2.120 + + stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; + while (pServiceQuery) { - stcMDNSServiceQuery::stcAnswer::stcIP4Address* pIP4Address = pSQAnswer->findIP4Address(p_pAAnswer->m_IPAddress); - if (pIP4Address) + stcMDNSServiceQuery::stcAnswer* pSQAnswer + = pServiceQuery->findAnswerForHostDomain(p_pAAnswer->m_Header.m_Domain); + if (pSQAnswer) // Answer for this host domain (eg. esp8266.local) available { - // Already known IP4 address - if (p_pAAnswer->m_u32TTL) // Valid TTL -> Update answers TTL - { - pIP4Address->m_TTL.set(p_pAAnswer->m_u32TTL); - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: Updated TTL(%d) for "), (int)p_pAAnswer->m_u32TTL); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" IP4Address (%s)\n"), pIP4Address->m_IPAddress.toString().c_str()); - ); - } - else // 'Goodbye' message for known IP4 address + stcMDNSServiceQuery::stcAnswer::stcIP4Address* pIP4Address + = pSQAnswer->findIP4Address(p_pAAnswer->m_IPAddress); + if (pIP4Address) { - pIP4Address->m_TTL.prepareDeletion(); // Prepare answer deletion according to RFC 6762, 10.1 - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: 'Goodbye' received for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" IP4 address (%s)\n"), pIP4Address->m_IPAddress.toString().c_str()); - ); + // Already known IP4 address + if (p_pAAnswer->m_u32TTL) // Valid TTL -> Update answers TTL + { + pIP4Address->m_TTL.set(p_pAAnswer->m_u32TTL); + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _processAAnswer: Updated TTL(%d) for "), + (int)p_pAAnswer->m_u32TTL); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P( + PSTR(" IP4Address (%s)\n"), + pIP4Address->m_IPAddress.toString().c_str());); + } + else // 'Goodbye' message for known IP4 address + { + pIP4Address->m_TTL.prepareDeletion(); // Prepare answer deletion + // according to RFC 6762, 10.1 + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _processAAnswer: 'Goodbye' received for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P( + PSTR(" IP4 address (%s)\n"), + pIP4Address->m_IPAddress.toString().c_str());); + } } - } - else - { - // Until now unknown IP4 address -> Add (if the message isn't just a 'Goodbye' note) - if (p_pAAnswer->m_u32TTL) // NOT just a 'Goodbye' message + else { - pIP4Address = new stcMDNSServiceQuery::stcAnswer::stcIP4Address(p_pAAnswer->m_IPAddress, p_pAAnswer->m_u32TTL); - if ((pIP4Address) && - (pSQAnswer->addIP4Address(pIP4Address))) + // Until now unknown IP4 address -> Add (if the message isn't just a + // 'Goodbye' note) + if (p_pAAnswer->m_u32TTL) // NOT just a 'Goodbye' message { - - pSQAnswer->m_u32ContentFlags |= ServiceQueryAnswerType_IP4Address; - if (pServiceQuery->m_fnCallback) + pIP4Address = new stcMDNSServiceQuery::stcAnswer::stcIP4Address( + p_pAAnswer->m_IPAddress, p_pAAnswer->m_u32TTL); + if ((pIP4Address) && (pSQAnswer->addIP4Address(pIP4Address))) { - MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); - pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_IP4Address), true); + pSQAnswer->m_u32ContentFlags |= ServiceQueryAnswerType_IP4Address; + if (pServiceQuery->m_fnCallback) + { + MDNSServiceInfo serviceInfo( + *this, (hMDNSServiceQuery)pServiceQuery, + pServiceQuery->indexOfAnswer(pSQAnswer)); + pServiceQuery->m_fnCallback( + serviceInfo, + static_cast(ServiceQueryAnswerType_IP4Address), + true); + } + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _processAAnswer: FAILED to add IP4 " + "address (%s)!\n"), + p_pAAnswer->m_IPAddress.toString().c_str());); } - } - else - { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: FAILED to add IP4 address (%s)!\n"), p_pAAnswer->m_IPAddress.toString().c_str());); } } } - } - pServiceQuery = pServiceQuery->m_pNext; - } // while(service query) - } // else: No p_pAAnswer - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: FAILED!\n")); - }); - return bResult; -} + pServiceQuery = pServiceQuery->m_pNext; + } // while(service query) + } // else: No p_pAAnswer + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: FAILED!\n")); + }); + return bResult; + } #endif #ifdef MDNS_IP6_SUPPORT -/* - MDNSResponder::_processAAAAAnswer -*/ -bool MDNSResponder::_processAAAAAnswer(const MDNSResponder::stcMDNS_RRAnswerAAAA* p_pAAAAAnswer) -{ - - bool bResult = false; - - if ((bResult = (0 != p_pAAAAAnswer))) + /* + MDNSResponder::_processAAAAAnswer + */ + bool MDNSResponder::_processAAAAAnswer(const MDNSResponder::stcMDNS_RRAnswerAAAA* p_pAAAAAnswer) { - // eg. esp8266.local AAAA xxxx xx 0bf3::0c + bool bResult = false; - stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; - while (pServiceQuery) + if ((bResult = (0 != p_pAAAAAnswer))) { - stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->findAnswerForHostDomain(p_pAAAAAnswer->m_Header.m_Domain); - if (pSQAnswer) // Answer for this host domain (eg. esp8266.local) available + // eg. esp8266.local AAAA xxxx xx 0bf3::0c + + stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; + while (pServiceQuery) { - stcIP6Address* pIP6Address = pSQAnswer->findIP6Address(p_pAAAAAnswer->m_IPAddress); - if (pIP6Address) + stcMDNSServiceQuery::stcAnswer* pSQAnswer + = pServiceQuery->findAnswerForHostDomain(p_pAAAAAnswer->m_Header.m_Domain); + if (pSQAnswer) // Answer for this host domain (eg. esp8266.local) available { - // Already known IP6 address - if (p_pAAAAAnswer->m_u32TTL) // Valid TTL -> Update answers TTL + stcIP6Address* pIP6Address + = pSQAnswer->findIP6Address(p_pAAAAAnswer->m_IPAddress); + if (pIP6Address) { - pIP6Address->m_TTL.set(p_pAAAAAnswer->m_u32TTL); - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: Updated TTL(%lu) for "), p_pAAAAAnswer->m_u32TTL); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" IP6 address (%s)\n"), pIP6Address->m_IPAddress.toString().c_str()); - ); - } - else // 'Goodbye' message for known IP6 address - { - pIP6Address->m_TTL.prepareDeletion(); // Prepare answer deletion according to RFC 6762, 10.1 - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: 'Goodbye' received for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" IP6 address (%s)\n"), pIP6Address->m_IPAddress.toString().c_str()); - ); + // Already known IP6 address + if (p_pAAAAAnswer->m_u32TTL) // Valid TTL -> Update answers TTL + { + pIP6Address->m_TTL.set(p_pAAAAAnswer->m_u32TTL); + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _processAAnswer: Updated TTL(%lu) for "), + p_pAAAAAnswer->m_u32TTL); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P( + PSTR(" IP6 address (%s)\n"), + pIP6Address->m_IPAddress.toString().c_str());); + } + else // 'Goodbye' message for known IP6 address + { + pIP6Address->m_TTL.prepareDeletion(); // Prepare answer deletion + // according to RFC 6762, 10.1 + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _processAAnswer: 'Goodbye' received for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P( + PSTR(" IP6 address (%s)\n"), + pIP6Address->m_IPAddress.toString().c_str());); + } } - } - else - { - // Until now unknown IP6 address -> Add (if the message isn't just a 'Goodbye' note) - if (p_pAAAAAnswer->m_u32TTL) // NOT just a 'Goodbye' message + else { - pIP6Address = new stcIP6Address(p_pAAAAAnswer->m_IPAddress, p_pAAAAAnswer->m_u32TTL); - if ((pIP6Address) && - (pSQAnswer->addIP6Address(pIP6Address))) + // Until now unknown IP6 address -> Add (if the message isn't just a + // 'Goodbye' note) + if (p_pAAAAAnswer->m_u32TTL) // NOT just a 'Goodbye' message { + pIP6Address = new stcIP6Address(p_pAAAAAnswer->m_IPAddress, + p_pAAAAAnswer->m_u32TTL); + if ((pIP6Address) && (pSQAnswer->addIP6Address(pIP6Address))) + { + pSQAnswer->m_u32ContentFlags |= ServiceQueryAnswerType_IP6Address; - pSQAnswer->m_u32ContentFlags |= ServiceQueryAnswerType_IP6Address; - - if (pServiceQuery->m_fnCallback) + if (pServiceQuery->m_fnCallback) + { + pServiceQuery->m_fnCallback( + this, (hMDNSServiceQuery)pServiceQuery, + pServiceQuery->indexOfAnswer(pSQAnswer), + ServiceQueryAnswerType_IP6Address, true, + pServiceQuery->m_pUserdata); + } + } + else { - pServiceQuery->m_fnCallback(this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer), ServiceQueryAnswerType_IP6Address, true, pServiceQuery->m_pUserdata); + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _processAAnswer: FAILED to add IP6 " + "address (%s)!\n"), + p_pAAAAAnswer->m_IPAddress.toString().c_str());); } } - else - { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _processAAnswer: FAILED to add IP6 address (%s)!\n"), p_pAAAAAnswer->m_IPAddress.toString().c_str());); - } } } - } - pServiceQuery = pServiceQuery->m_pNext; - } // while(service query) - } // else: No p_pAAAAAnswer + pServiceQuery = pServiceQuery->m_pNext; + } // while(service query) + } // else: No p_pAAAAAnswer - return bResult; -} + return bResult; + } #endif + /* + PROBING + */ -/* - PROBING -*/ - -/* - MDNSResponder::_updateProbeStatus - - Manages the (outgoing) probing process. - - If probing has not been started yet (ProbingStatus_NotStarted), the initial delay (see RFC 6762) is determined and - the process is started - - After timeout (of initial or subsequential delay) a probe message is send out for three times. If the message has - already been sent out three times, the probing has been successful and is finished. - - Conflict management is handled in '_parseResponse ff.' - Tiebraking is handled in 'parseQuery ff.' -*/ -bool MDNSResponder::_updateProbeStatus(void) -{ + /* + MDNSResponder::_updateProbeStatus - bool bResult = true; + Manages the (outgoing) probing process. + - If probing has not been started yet (ProbingStatus_NotStarted), the initial delay (see RFC + 6762) is determined and the process is started + - After timeout (of initial or subsequential delay) a probe message is send out for three + times. If the message has already been sent out three times, the probing has been successful + and is finished. - // - // Probe host domain - if (ProbingStatus_ReadyToStart == m_HostProbeInformation.m_ProbingStatus) + Conflict management is handled in '_parseResponse ff.' + Tiebraking is handled in 'parseQuery ff.' + */ + bool MDNSResponder::_updateProbeStatus(void) { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Starting host probing...\n"));); + bool bResult = true; - // First probe delay SHOULD be random 0-250 ms - m_HostProbeInformation.m_Timeout.reset(rand() % MDNS_PROBE_DELAY); - m_HostProbeInformation.m_ProbingStatus = ProbingStatus_InProgress; - } - else if ((ProbingStatus_InProgress == m_HostProbeInformation.m_ProbingStatus) && // Probing AND - (m_HostProbeInformation.m_Timeout.expired())) // Time for next probe - { + // + // Probe host domain + if (ProbingStatus_ReadyToStart == m_HostProbeInformation.m_ProbingStatus) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _updateProbeStatus: Starting host probing...\n"));); - if (MDNS_PROBE_COUNT > m_HostProbeInformation.m_u8SentCount) // Send next probe + // First probe delay SHOULD be random 0-250 ms + m_HostProbeInformation.m_Timeout.reset(rand() % MDNS_PROBE_DELAY); + m_HostProbeInformation.m_ProbingStatus = ProbingStatus_InProgress; + } + else if ((ProbingStatus_InProgress == m_HostProbeInformation.m_ProbingStatus) + && // Probing AND + (m_HostProbeInformation.m_Timeout.expired())) // Time for next probe { - if ((bResult = _sendHostProbe())) + if (MDNS_PROBE_COUNT > m_HostProbeInformation.m_u8SentCount) // Send next probe { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Did sent host probe\n\n"));); + if ((bResult = _sendHostProbe())) + { + 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 - { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Done host probing.\n"));); - m_HostProbeInformation.m_ProbingStatus = ProbingStatus_Done; - m_HostProbeInformation.m_Timeout.resetToNeverExpires(); - if (m_HostProbeInformation.m_fnHostProbeResultCallback) + else // Probing finished { - m_HostProbeInformation.m_fnHostProbeResultCallback(m_pcHostname, true); - } - - // Prepare to announce host - m_HostProbeInformation.m_u8SentCount = 0; - m_HostProbeInformation.m_Timeout.reset(MDNS_ANNOUNCE_DELAY); - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Prepared host announcing.\n\n"));); - } - } // else: Probing already finished OR waiting for next time slot - else if ((ProbingStatus_Done == m_HostProbeInformation.m_ProbingStatus) && - (m_HostProbeInformation.m_Timeout.expired())) - { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _updateProbeStatus: Done host probing.\n"));); + m_HostProbeInformation.m_ProbingStatus = ProbingStatus_Done; + m_HostProbeInformation.m_Timeout.resetToNeverExpires(); + if (m_HostProbeInformation.m_fnHostProbeResultCallback) + { + m_HostProbeInformation.m_fnHostProbeResultCallback(m_pcHostname, true); + } - if ((bResult = _announce(true, false))) // Don't announce services here + // Prepare to announce host + m_HostProbeInformation.m_u8SentCount = 0; + m_HostProbeInformation.m_Timeout.reset(MDNS_ANNOUNCE_DELAY); + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _updateProbeStatus: Prepared host announcing.\n\n"));); + } + } // else: Probing already finished OR waiting for next time slot + else if ((ProbingStatus_Done == m_HostProbeInformation.m_ProbingStatus) + && (m_HostProbeInformation.m_Timeout.expired())) { + _announce(true, false); // Don't announce services here + ++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);); + 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"));); + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _updateProbeStatus: Done host announcing.\n\n"));); } } - } - // - // Probe services - for (stcMDNSService* pService = m_pServices; ((bResult) && (pService)); pService = pService->m_pNext) - { - if (ProbingStatus_ReadyToStart == pService->m_ProbeInformation.m_ProbingStatus) // Ready to get started - { - - pService->m_ProbeInformation.m_Timeout.reset(MDNS_PROBE_DELAY); // More or equal than first probe for host domain - pService->m_ProbeInformation.m_ProbingStatus = ProbingStatus_InProgress; - } - else if ((ProbingStatus_InProgress == pService->m_ProbeInformation.m_ProbingStatus) && // Probing AND - (pService->m_ProbeInformation.m_Timeout.expired())) // Time for next probe + // + // Probe services + for (stcMDNSService* pService = m_pServices; ((bResult) && (pService)); + pService = pService->m_pNext) { - - if (MDNS_PROBE_COUNT > pService->m_ProbeInformation.m_u8SentCount) // Send next probe + if (ProbingStatus_ReadyToStart + == pService->m_ProbeInformation.m_ProbingStatus) // Ready to get started + { + pService->m_ProbeInformation.m_Timeout.reset( + MDNS_PROBE_DELAY); // More or equal than first probe for host domain + pService->m_ProbeInformation.m_ProbingStatus = ProbingStatus_InProgress; + } + else if ((ProbingStatus_InProgress == pService->m_ProbeInformation.m_ProbingStatus) + && // Probing AND + (pService->m_ProbeInformation.m_Timeout.expired())) // Time for next probe { - if ((bResult = _sendServiceProbe(*pService))) + if (MDNS_PROBE_COUNT + > pService->m_ProbeInformation.m_u8SentCount) // Send next probe { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Did sent service probe (%u)\n\n"), (pService->m_ProbeInformation.m_u8SentCount + 1));); + if ((bResult = _sendServiceProbe(*pService))) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _updateProbeStatus: Did sent service probe to " + "all links " + "(%u)\n\n"), + (pService->m_ProbeInformation.m_u8SentCount + 1));); + } + 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 - { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Done service probing %s.%s.%s\n\n"), (pService->m_pcName ? : m_pcHostname), pService->m_pcService, pService->m_pcProtocol);); - pService->m_ProbeInformation.m_ProbingStatus = ProbingStatus_Done; - pService->m_ProbeInformation.m_Timeout.resetToNeverExpires(); - if (pService->m_ProbeInformation.m_fnServiceProbeResultCallback) + else // Probing finished { - pService->m_ProbeInformation.m_fnServiceProbeResultCallback(pService->m_pcName, pService, true); + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: " + "Done service probing %s.%s.%s\n\n"), + (pService->m_pcName ?: m_pcHostname), + pService->m_pcService, + pService->m_pcProtocol);); + pService->m_ProbeInformation.m_ProbingStatus = ProbingStatus_Done; + pService->m_ProbeInformation.m_Timeout.resetToNeverExpires(); + if (pService->m_ProbeInformation.m_fnServiceProbeResultCallback) + { + pService->m_ProbeInformation.m_fnServiceProbeResultCallback( + pService->m_pcName, pService, true); + } + // Prepare to announce service + pService->m_ProbeInformation.m_u8SentCount = 0; + pService->m_ProbeInformation.m_Timeout.reset(MDNS_ANNOUNCE_DELAY); + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR( + "[MDNSResponder] _updateProbeStatus: Prepared service announcing.\n\n"));); } - // Prepare to announce service - pService->m_ProbeInformation.m_u8SentCount = 0; - pService->m_ProbeInformation.m_Timeout.reset(MDNS_ANNOUNCE_DELAY); - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: Prepared service announcing.\n\n"));); - } - } // else: Probing already finished OR waiting for next time slot - else if ((ProbingStatus_Done == pService->m_ProbeInformation.m_ProbingStatus) && - (pService->m_ProbeInformation.m_Timeout.expired())) - { - - if ((bResult = _announceService(*pService))) // Announce service + } // else: Probing already finished OR waiting for next time slot + else if ((ProbingStatus_Done == pService->m_ProbeInformation.m_ProbingStatus) + && (pService->m_ProbeInformation.m_Timeout.expired())) { + _announceService(*pService); // Announce service + ++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);); + 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);); + 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);); } } } + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: FAILED!\n\n")); + }); + return bResult; } - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _updateProbeStatus: FAILED!\n\n")); - }); - return bResult; -} - -/* - MDNSResponder::_resetProbeStatus - Resets the probe status. - If 'p_bRestart' is set, the status is set to ProbingStatus_NotStarted. Consequently, - when running 'updateProbeStatus' (which is done in every '_update' loop), the probing - process is restarted. -*/ -bool MDNSResponder::_resetProbeStatus(bool p_bRestart /*= true*/) -{ + /* + MDNSResponder::_resetProbeStatus - m_HostProbeInformation.clear(false); - m_HostProbeInformation.m_ProbingStatus = (p_bRestart ? ProbingStatus_ReadyToStart : ProbingStatus_Done); - - for (stcMDNSService* pService = m_pServices; pService; pService = pService->m_pNext) + Resets the probe status. + If 'p_bRestart' is set, the status is set to ProbingStatus_NotStarted. Consequently, + when running 'updateProbeStatus' (which is done in every '_update' loop), the probing + process is restarted. + */ + bool MDNSResponder::_resetProbeStatus(bool p_bRestart /*= true*/) { - pService->m_ProbeInformation.clear(false); - pService->m_ProbeInformation.m_ProbingStatus = (p_bRestart ? ProbingStatus_ReadyToStart : ProbingStatus_Done); - } - return true; -} - -/* - MDNSResponder::_hasProbesWaitingForAnswers -*/ -bool MDNSResponder::_hasProbesWaitingForAnswers(void) const -{ + m_HostProbeInformation.clear(false); + m_HostProbeInformation.m_ProbingStatus + = (p_bRestart ? ProbingStatus_ReadyToStart : ProbingStatus_Done); - bool bResult = ((ProbingStatus_InProgress == m_HostProbeInformation.m_ProbingStatus) && // Probing - (0 < m_HostProbeInformation.m_u8SentCount)); // And really probing + for (stcMDNSService* pService = m_pServices; pService; pService = pService->m_pNext) + { + pService->m_ProbeInformation.clear(false); + pService->m_ProbeInformation.m_ProbingStatus + = (p_bRestart ? ProbingStatus_ReadyToStart : ProbingStatus_Done); + } + return true; + } - for (stcMDNSService* pService = m_pServices; ((!bResult) && (pService)); pService = pService->m_pNext) + /* + MDNSResponder::_hasProbesWaitingForAnswers + */ + bool MDNSResponder::_hasProbesWaitingForAnswers(void) const { - bResult = ((ProbingStatus_InProgress == pService->m_ProbeInformation.m_ProbingStatus) && // Probing - (0 < pService->m_ProbeInformation.m_u8SentCount)); // And really probing - } - return bResult; -} + bool bResult + = ((ProbingStatus_InProgress == m_HostProbeInformation.m_ProbingStatus) && // Probing + (0 < m_HostProbeInformation.m_u8SentCount)); // And really probing -/* - MDNSResponder::_sendHostProbe + for (stcMDNSService* pService = m_pServices; ((!bResult) && (pService)); + pService = pService->m_pNext) + { + bResult = ((ProbingStatus_InProgress == pService->m_ProbeInformation.m_ProbingStatus) + && // Probing + (0 < pService->m_ProbeInformation.m_u8SentCount)); // And really probing + } + return bResult; + } - Asks (probes) in the local network for the planned host domain - - (eg. esp8266.local) + /* + MDNSResponder::_sendHostProbe - To allow 'tiebreaking' (see '_parseQuery'), the answers for these questions are delivered in - the 'knwon answers' section of the query. - Host domain: - - A/AAAA (eg. esp8266.esp -> 192.168.2.120) -*/ -bool MDNSResponder::_sendHostProbe(void) -{ - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendHostProbe (%s, %lu)\n"), m_pcHostname, millis());); + Asks (probes) in the local network for the planned host domain + - (eg. esp8266.local) - bool bResult = true; + To allow 'tiebreaking' (see '_parseQuery'), the answers for these questions are delivered in + the 'knwon answers' section of the query. + Host domain: + - A/AAAA (eg. esp8266.esp -> 192.168.2.120) + */ + bool MDNSResponder::_sendHostProbe(void) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendHostProbe (%s, %lu)\n"), + m_pcHostname, millis());); - // Requests for host domain - stcMDNSSendParameter sendParameter; - sendParameter.m_bCacheFlush = false; // RFC 6762 10.2 + bool bResult = true; - sendParameter.m_pQuestions = new stcMDNS_RRQuestion; - if (((bResult = (0 != sendParameter.m_pQuestions))) && - ((bResult = _buildDomainForHost(m_pcHostname, sendParameter.m_pQuestions->m_Header.m_Domain)))) - { + // Requests for host domain + stcMDNSSendParameter sendParameter; + sendParameter.m_bCacheFlush = false; // RFC 6762 10.2 - //sendParameter.m_pQuestions->m_bUnicast = true; - sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Type = DNS_RRTYPE_ANY; - sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Class = (0x8000 | DNS_RRCLASS_IN); // Unicast & INternet + sendParameter.m_pQuestions = new stcMDNS_RRQuestion; + if (((bResult = (0 != sendParameter.m_pQuestions))) + && ((bResult = _buildDomainForHost(m_pcHostname, + sendParameter.m_pQuestions->m_Header.m_Domain)))) + { + // sendParameter.m_pQuestions->m_bUnicast = true; + sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Type = DNS_RRTYPE_ANY; + sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Class + = (0x8000 | DNS_RRCLASS_IN); // Unicast & INternet - // Add known answers + // Add known answers #ifdef MDNS_IP4_SUPPORT - sendParameter.m_u8HostReplyMask |= ContentFlag_A; // Add A answer + sendParameter.m_u8HostReplyMask |= ContentFlag_A; // Add A answer #endif #ifdef MDNS_IP6_SUPPORT - sendParameter.m_u8HostReplyMask |= ContentFlag_AAAA; // Add AAAA answer + sendParameter.m_u8HostReplyMask |= ContentFlag_AAAA; // Add AAAA answer #endif - } - else - { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendHostProbe: FAILED to create host question!\n"));); - if (sendParameter.m_pQuestions) + } + else { - delete sendParameter.m_pQuestions; - sendParameter.m_pQuestions = 0; + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _sendHostProbe: FAILED to create host question!\n"));); + if (sendParameter.m_pQuestions) + { + delete sendParameter.m_pQuestions; + sendParameter.m_pQuestions = 0; + } } + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendHostProbe: FAILED!\n")); + }); + return ((bResult) && (_sendMDNSMessage(sendParameter))); } - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendHostProbe: FAILED!\n")); - }); - return ((bResult) && - (_sendMDNSMessage(sendParameter))); -} -/* - MDNSResponder::_sendServiceProbe + /* + MDNSResponder::_sendServiceProbe - Asks (probes) in the local network for the planned service instance domain - - (eg. MyESP._http._tcp.local). + Asks (probes) in the local network for the planned service instance domain + - (eg. MyESP._http._tcp.local). - To allow 'tiebreaking' (see '_parseQuery'), the answers for these questions are delivered in - the 'knwon answers' section of the query. - Service domain: - - SRV (eg. MyESP._http._tcp.local -> 5000 esp8266.local) - - PTR NAME (eg. _http._tcp.local -> MyESP._http._tcp.local) (TODO: Check if needed, maybe TXT is better) -*/ -bool MDNSResponder::_sendServiceProbe(stcMDNSService& p_rService) -{ - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendServiceProbe (%s.%s.%s, %lu)\n"), (p_rService.m_pcName ? : m_pcHostname), p_rService.m_pcService, p_rService.m_pcProtocol, millis());); - - bool bResult = true; - - // Requests for service instance domain - stcMDNSSendParameter sendParameter; - sendParameter.m_bCacheFlush = false; // RFC 6762 10.2 - - sendParameter.m_pQuestions = new stcMDNS_RRQuestion; - if (((bResult = (0 != sendParameter.m_pQuestions))) && - ((bResult = _buildDomainForService(p_rService, true, sendParameter.m_pQuestions->m_Header.m_Domain)))) + To allow 'tiebreaking' (see '_parseQuery'), the answers for these questions are delivered in + the 'knwon answers' section of the query. + Service domain: + - SRV (eg. MyESP._http._tcp.local -> 5000 esp8266.local) + - PTR NAME (eg. _http._tcp.local -> MyESP._http._tcp.local) (TODO: Check if needed, maybe + TXT is better) + */ + bool MDNSResponder::_sendServiceProbe(stcMDNSService& p_rService) { + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendServiceProbe (%s.%s.%s, %lu)\n"), + (p_rService.m_pcName ?: m_pcHostname), p_rService.m_pcService, + p_rService.m_pcProtocol, millis());); - sendParameter.m_pQuestions->m_bUnicast = true; - sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Type = DNS_RRTYPE_ANY; - sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Class = (0x8000 | DNS_RRCLASS_IN); // Unicast & INternet + bool bResult = true; - // Add known answers - p_rService.m_u8ReplyMask = (ContentFlag_SRV | ContentFlag_PTR_NAME); // Add SRV and PTR NAME answers - } - else - { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendServiceProbe: FAILED to create service question!\n"));); - if (sendParameter.m_pQuestions) + // Requests for service instance domain + stcMDNSSendParameter sendParameter; + sendParameter.m_bCacheFlush = false; // RFC 6762 10.2 + + sendParameter.m_pQuestions = new stcMDNS_RRQuestion; + if (((bResult = (0 != sendParameter.m_pQuestions))) + && ((bResult = _buildDomainForService(p_rService, true, + sendParameter.m_pQuestions->m_Header.m_Domain)))) + { + sendParameter.m_pQuestions->m_bUnicast = true; + sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Type = DNS_RRTYPE_ANY; + sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Class + = (0x8000 | DNS_RRCLASS_IN); // Unicast & INternet + + // Add known answers + p_rService.m_u8ReplyMask + = (ContentFlag_SRV | ContentFlag_PTR_NAME); // Add SRV and PTR NAME answers + } + else { - delete sendParameter.m_pQuestions; - sendParameter.m_pQuestions = 0; + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _sendServiceProbe: FAILED to create service question!\n"));); + if (sendParameter.m_pQuestions) + { + delete sendParameter.m_pQuestions; + sendParameter.m_pQuestions = 0; + } } + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendServiceProbe: FAILED!\n")); + }); + return ((bResult) && (_sendMDNSMessage(sendParameter))); } - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendServiceProbe: FAILED!\n")); - }); - return ((bResult) && - (_sendMDNSMessage(sendParameter))); -} -/* - MDNSResponder::_cancelProbingForHost -*/ -bool MDNSResponder::_cancelProbingForHost(void) -{ + /* + MDNSResponder::_cancelProbingForHost + */ + bool MDNSResponder::_cancelProbingForHost(void) + { + bool bResult = false; - bool bResult = false; + m_HostProbeInformation.clear(false); + // Send host notification + if (m_HostProbeInformation.m_fnHostProbeResultCallback) + { + m_HostProbeInformation.m_fnHostProbeResultCallback(m_pcHostname, false); - m_HostProbeInformation.clear(false); - // Send host notification - if (m_HostProbeInformation.m_fnHostProbeResultCallback) - { - m_HostProbeInformation.m_fnHostProbeResultCallback(m_pcHostname, false); + bResult = true; + } - bResult = true; + for (stcMDNSService* pService = m_pServices; ((!bResult) && (pService)); + pService = pService->m_pNext) + { + bResult = _cancelProbingForService(*pService); + } + return bResult; } - for (stcMDNSService* pService = m_pServices; ((!bResult) && (pService)); pService = pService->m_pNext) + /* + MDNSResponder::_cancelProbingForService + */ + bool MDNSResponder::_cancelProbingForService(stcMDNSService& p_rService) { - bResult = _cancelProbingForService(*pService); - } - return bResult; -} + bool bResult = false; -/* - MDNSResponder::_cancelProbingForService -*/ -bool MDNSResponder::_cancelProbingForService(stcMDNSService& p_rService) -{ - - bool bResult = false; - - p_rService.m_ProbeInformation.clear(false); - // Send notification - if (p_rService.m_ProbeInformation.m_fnServiceProbeResultCallback) - { - p_rService.m_ProbeInformation.m_fnServiceProbeResultCallback(p_rService.m_pcName, &p_rService, false); - bResult = true; + p_rService.m_ProbeInformation.clear(false); + // Send notification + if (p_rService.m_ProbeInformation.m_fnServiceProbeResultCallback) + { + p_rService.m_ProbeInformation.m_fnServiceProbeResultCallback(p_rService.m_pcName, + &p_rService, false); + bResult = true; + } + return bResult; } - return bResult; -} - + /** + ANNOUNCING + */ -/** - ANNOUNCING -*/ - -/* - MDNSResponder::_announce + /* + MDNSResponder::_announce - Announces the host domain: - - A/AAAA (eg. esp8266.local -> 192.168.2.120) - - PTR (eg. 192.168.2.120.in-addr.arpa -> esp8266.local) + Announces the host domain: + - A/AAAA (eg. esp8266.local -> 192.168.2.120) + - PTR (eg. 192.168.2.120.in-addr.arpa -> esp8266.local) - and all presented services: - - PTR_TYPE (_services._dns-sd._udp.local -> _http._tcp.local) - - PTR_NAME (eg. _http._tcp.local -> MyESP8266._http._tcp.local) - - SRV (eg. MyESP8266._http._tcp.local -> 5000 esp8266.local) - - TXT (eg. MyESP8266._http._tcp.local -> c#=1) + and all presented services: + - PTR_TYPE (_services._dns-sd._udp.local -> _http._tcp.local) + - PTR_NAME (eg. _http._tcp.local -> MyESP8266._http._tcp.local) + - SRV (eg. MyESP8266._http._tcp.local -> 5000 esp8266.local) + - TXT (eg. MyESP8266._http._tcp.local -> c#=1) - Goodbye (Un-Announcing) for the host domain and all services is also handled here. - Goodbye messages are created by setting the TTL for the answer to 0, this happens - inside the '_writeXXXAnswer' procs via 'sendParameter.m_bUnannounce = true' -*/ -bool MDNSResponder::_announce(bool p_bAnnounce, - bool p_bIncludeServices) -{ - - bool bResult = false; - - stcMDNSSendParameter sendParameter; - if (ProbingStatus_Done == m_HostProbeInformation.m_ProbingStatus) + Goodbye (Un-Announcing) for the host domain and all services is also handled here. + Goodbye messages are created by setting the TTL for the answer to 0, this happens + inside the '_writeXXXAnswer' procs via 'sendParameter.m_bUnannounce = true' + */ + bool MDNSResponder::_announce(bool p_bAnnounce, bool p_bIncludeServices) { + bool bResult = false; - bResult = true; + stcMDNSSendParameter sendParameter; + if (ProbingStatus_Done == m_HostProbeInformation.m_ProbingStatus) + { + bResult = true; - sendParameter.m_bResponse = true; // Announces are 'Unsolicited authorative responses' - sendParameter.m_bAuthorative = true; - sendParameter.m_bUnannounce = !p_bAnnounce; // When unannouncing, the TTL is set to '0' while creating the answers + sendParameter.m_bResponse + = true; // Announces are 'Unsolicited authoritative responses' + sendParameter.m_bAuthorative = true; + sendParameter.m_bUnannounce = !p_bAnnounce; // When unannouncing, the TTL is set to '0' + // while creating the answers - // Announce host - sendParameter.m_u8HostReplyMask = 0; + // Announce host + sendParameter.m_u8HostReplyMask = 0; #ifdef MDNS_IP4_SUPPORT - sendParameter.m_u8HostReplyMask |= ContentFlag_A; // A answer - sendParameter.m_u8HostReplyMask |= ContentFlag_PTR_IP4; // PTR_IP4 answer + sendParameter.m_u8HostReplyMask |= ContentFlag_A; // A answer + sendParameter.m_u8HostReplyMask |= ContentFlag_PTR_IP4; // PTR_IP4 answer #endif #ifdef MDNS_IP6_SUPPORT - sendParameter.m_u8HostReplyMask |= ContentFlag_AAAA; // AAAA answer - sendParameter.m_u8HostReplyMask |= ContentFlag_PTR_IP6; // PTR_IP6 answer + sendParameter.m_u8HostReplyMask |= ContentFlag_AAAA; // AAAA answer + sendParameter.m_u8HostReplyMask |= ContentFlag_PTR_IP6; // PTR_IP6 answer #endif - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _announce: Announcing host %s (content 0x%X)\n"), m_pcHostname, sendParameter.m_u8HostReplyMask);); + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _announce: Announcing host %s (content 0x%X)\n"), + m_pcHostname, sendParameter.m_u8HostReplyMask);); - if (p_bIncludeServices) - { - // Announce services (service type, name, SRV (location) and TXTs) - for (stcMDNSService* pService = m_pServices; ((bResult) && (pService)); pService = pService->m_pNext) + if (p_bIncludeServices) { - if (ProbingStatus_Done == pService->m_ProbeInformation.m_ProbingStatus) + // Announce services (service type, name, SRV (location) and TXTs) + for (stcMDNSService* pService = m_pServices; ((bResult) && (pService)); + pService = pService->m_pNext) { - pService->m_u8ReplyMask = (ContentFlag_PTR_TYPE | ContentFlag_PTR_NAME | ContentFlag_SRV | ContentFlag_TXT); - - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _announce: Announcing service %s.%s.%s (content %u)\n"), (pService->m_pcName ? : m_pcHostname), pService->m_pcService, pService->m_pcProtocol, pService->m_u8ReplyMask);); + if (ProbingStatus_Done == pService->m_ProbeInformation.m_ProbingStatus) + { + pService->m_u8ReplyMask = (ContentFlag_PTR_TYPE | ContentFlag_PTR_NAME + | ContentFlag_SRV | ContentFlag_TXT); + + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _announce: Announcing service %s.%s.%s (content " + "%u)\n"), + (pService->m_pcName ?: m_pcHostname), pService->m_pcService, + pService->m_pcProtocol, pService->m_u8ReplyMask);); + } } } } + DEBUG_EX_ERR( + if (!bResult) { DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _announce: FAILED!\n")); }); + return ((bResult) && (_sendMDNSMessage(sendParameter))); } - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _announce: FAILED!\n")); - }); - return ((bResult) && - (_sendMDNSMessage(sendParameter))); -} - -/* - MDNSResponder::_announceService -*/ -bool MDNSResponder::_announceService(stcMDNSService& p_rService, - bool p_bAnnounce /*= true*/) -{ - bool bResult = false; - - stcMDNSSendParameter sendParameter; - if (ProbingStatus_Done == p_rService.m_ProbeInformation.m_ProbingStatus) + /* + MDNSResponder::_announceService + */ + bool MDNSResponder::_announceService(stcMDNSService& p_rService, bool p_bAnnounce /*= true*/) { + bool bResult = false; - sendParameter.m_bResponse = true; // Announces are 'Unsolicited authorative responses' - sendParameter.m_bAuthorative = true; - sendParameter.m_bUnannounce = !p_bAnnounce; // When unannouncing, the TTL is set to '0' while creating the answers - - // DON'T announce host - sendParameter.m_u8HostReplyMask = 0; + stcMDNSSendParameter sendParameter; + if (ProbingStatus_Done == p_rService.m_ProbeInformation.m_ProbingStatus) + { + sendParameter.m_bResponse + = true; // Announces are 'Unsolicited authoritative responses' + sendParameter.m_bAuthorative = true; + sendParameter.m_bUnannounce = !p_bAnnounce; // When unannouncing, the TTL is set to '0' + // while creating the answers - // Announce services (service type, name, SRV (location) and TXTs) - p_rService.m_u8ReplyMask = (ContentFlag_PTR_TYPE | ContentFlag_PTR_NAME | ContentFlag_SRV | ContentFlag_TXT); - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _announceService: Announcing service %s.%s.%s (content 0x%X)\n"), (p_rService.m_pcName ? : m_pcHostname), p_rService.m_pcService, p_rService.m_pcProtocol, p_rService.m_u8ReplyMask);); + // DON'T announce host + sendParameter.m_u8HostReplyMask = 0; - bResult = true; + // Announce services (service type, name, SRV (location) and TXTs) + p_rService.m_u8ReplyMask + = (ContentFlag_PTR_TYPE | ContentFlag_PTR_NAME | ContentFlag_SRV | ContentFlag_TXT); + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _announceService: Announcing " + "service %s.%s.%s (content 0x%X)\n"), + (p_rService.m_pcName ?: m_pcHostname), + p_rService.m_pcService, p_rService.m_pcProtocol, + p_rService.m_u8ReplyMask);); + + bResult = true; + } + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _announceService: FAILED!\n")); + }); + return ((bResult) && (_sendMDNSMessage(sendParameter))); } - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _announceService: FAILED!\n")); - }); - return ((bResult) && - (_sendMDNSMessage(sendParameter))); -} + /** + SERVICE QUERY CACHE + */ -/** - SERVICE QUERY CACHE -*/ - -/* - MDNSResponder::_hasServiceQueriesWaitingForAnswers -*/ -bool MDNSResponder::_hasServiceQueriesWaitingForAnswers(void) const -{ - - bool bOpenQueries = false; - - for (stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; pServiceQuery; pServiceQuery = pServiceQuery->m_pNext) + /* + MDNSResponder::_hasServiceQueriesWaitingForAnswers + */ + bool MDNSResponder::_hasServiceQueriesWaitingForAnswers(void) const { - if (pServiceQuery->m_bAwaitingAnswers) + bool bOpenQueries = false; + + for (stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; pServiceQuery; + pServiceQuery = pServiceQuery->m_pNext) { - bOpenQueries = true; - break; + if (pServiceQuery->m_bAwaitingAnswers) + { + bOpenQueries = true; + break; + } } + return bOpenQueries; } - return bOpenQueries; -} - -/* - MDNSResponder::_checkServiceQueryCache - - For any 'living' service query (m_bAwaitingAnswers == true) all available answers (their components) - are checked for topicality based on the stored reception time and the answers TTL. - When the components TTL is outlasted by more than 80%, a new question is generated, to get updated information. - When no update arrived (in time), the component is removed from the answer (cache). -*/ -bool MDNSResponder::_checkServiceQueryCache(void) -{ + /* + MDNSResponder::_checkServiceQueryCache - bool bResult = true; + For any 'living' service query (m_bAwaitingAnswers == true) all available answers (their + components) are checked for topicality based on the stored reception time and the answers + TTL. When the components TTL is outlasted by more than 80%, a new question is generated, to + get updated information. When no update arrived (in time), the component is removed from the + answer (cache). - DEBUG_EX_INFO( - bool printedInfo = false; - ); - for (stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; ((bResult) && (pServiceQuery)); pServiceQuery = pServiceQuery->m_pNext) + */ + bool MDNSResponder::_checkServiceQueryCache(void) { + bool bResult = true; - // - // Resend dynamic service queries, if not already done often enough - if ((!pServiceQuery->m_bLegacyQuery) && - (MDNS_DYNAMIC_QUERY_RESEND_COUNT > pServiceQuery->m_u8SentCount) && - (pServiceQuery->m_ResendTimeout.expired())) + DEBUG_EX_INFO(bool printedInfo = false;); + for (stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; ((bResult) && (pServiceQuery)); + pServiceQuery = pServiceQuery->m_pNext) { - - if ((bResult = _sendMDNSServiceQuery(*pServiceQuery))) + // + // Resend dynamic service queries, if not already done often enough + if ((!pServiceQuery->m_bLegacyQuery) + && (MDNS_DYNAMIC_QUERY_RESEND_COUNT > pServiceQuery->m_u8SentCount) + && (pServiceQuery->m_ResendTimeout.expired())) { - ++pServiceQuery->m_u8SentCount; - pServiceQuery->m_ResendTimeout.reset((MDNS_DYNAMIC_QUERY_RESEND_COUNT > pServiceQuery->m_u8SentCount) - ? (MDNS_DYNAMIC_QUERY_RESEND_DELAY * (pServiceQuery->m_u8SentCount - 1)) - : esp8266::polledTimeout::oneShotMs::neverExpires); + if ((bResult = _sendMDNSServiceQuery(*pServiceQuery))) + { + ++pServiceQuery->m_u8SentCount; + pServiceQuery->m_ResendTimeout.reset( + (MDNS_DYNAMIC_QUERY_RESEND_COUNT > pServiceQuery->m_u8SentCount) + ? (MDNS_DYNAMIC_QUERY_RESEND_DELAY * (pServiceQuery->m_u8SentCount - 1)) + : esp8266::polledTimeout::oneShotMs::neverExpires); + } + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _checkServiceQueryCache: %s to resend service query!"), + (bResult ? "Succeeded" : "FAILED")); + printedInfo = true;); } - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: %s to resend service query!"), (bResult ? "Succeeded" : "FAILED")); - printedInfo = true; - ); - } - // - // Schedule updates for cached answers - if (pServiceQuery->m_bAwaitingAnswers) - { - stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->m_pAnswers; - while ((bResult) && - (pSQAnswer)) + // + // Schedule updates for cached answers + if (pServiceQuery->m_bAwaitingAnswers) { - stcMDNSServiceQuery::stcAnswer* pNextSQAnswer = pSQAnswer->m_pNext; - - // 1. level answer - if ((bResult) && - (pSQAnswer->m_TTLServiceDomain.flagged())) + stcMDNSServiceQuery::stcAnswer* pSQAnswer = pServiceQuery->m_pAnswers; + while ((bResult) && (pSQAnswer)) { + stcMDNSServiceQuery::stcAnswer* pNextSQAnswer = pSQAnswer->m_pNext; - if (!pSQAnswer->m_TTLServiceDomain.finalTimeoutLevel()) - { - - bResult = ((_sendMDNSServiceQuery(*pServiceQuery)) && - (pSQAnswer->m_TTLServiceDomain.restart())); - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: PTR update scheduled for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" %s\n"), (bResult ? "OK" : "FAILURE")); - printedInfo = true; - ); - } - else + // 1. level answer + if ((bResult) && (pSQAnswer->m_TTLServiceDomain.flagged())) { - // Timed out! -> Delete - if (pServiceQuery->m_fnCallback) + if (!pSQAnswer->m_TTLServiceDomain.finalTimeoutLevel()) { - MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); - pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_ServiceDomain), false); + bResult = ((_sendMDNSServiceQuery(*pServiceQuery)) + && (pSQAnswer->m_TTLServiceDomain.restart())); + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _checkServiceQueryCache: PTR update " + "scheduled for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" %s\n"), (bResult ? "OK" : "FAILURE")); + printedInfo = true;); } - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: Will remove PTR answer for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR("\n")); - printedInfo = true; - ); - - bResult = pServiceQuery->removeAnswer(pSQAnswer); - pSQAnswer = 0; - continue; // Don't use this answer anymore - } - } // ServiceDomain flagged - - // 2. level answers - // HostDomain & Port (from SRV) - if ((bResult) && - (pSQAnswer->m_TTLHostDomainAndPort.flagged())) - { - - if (!pSQAnswer->m_TTLHostDomainAndPort.finalTimeoutLevel()) - { + else + { + // Timed out! -> Delete + if (pServiceQuery->m_fnCallback) + { + MDNSServiceInfo serviceInfo( + *this, (hMDNSServiceQuery)pServiceQuery, + pServiceQuery->indexOfAnswer(pSQAnswer)); + pServiceQuery->m_fnCallback( + serviceInfo, + static_cast(ServiceQueryAnswerType_ServiceDomain), + false); + } + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _checkServiceQueryCache: Will remove PTR " + "answer for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR("\n")); printedInfo = true;); + + bResult = pServiceQuery->removeAnswer(pSQAnswer); + pSQAnswer = 0; + continue; // Don't use this answer anymore + } + } // ServiceDomain flagged - bResult = ((_sendMDNSQuery(pSQAnswer->m_ServiceDomain, DNS_RRTYPE_SRV)) && - (pSQAnswer->m_TTLHostDomainAndPort.restart())); - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: SRV update scheduled for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" host domain and port %s\n"), (bResult ? "OK" : "FAILURE")); - printedInfo = true; - ); - } - else + // 2. level answers + // HostDomain & Port (from SRV) + if ((bResult) && (pSQAnswer->m_TTLHostDomainAndPort.flagged())) { - // Timed out! -> Delete - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: Will remove SRV answer for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" host domain and port\n")); - printedInfo = true; - ); - // Delete - pSQAnswer->m_HostDomain.clear(); - pSQAnswer->releaseHostDomain(); - pSQAnswer->m_u16Port = 0; - pSQAnswer->m_TTLHostDomainAndPort.set(0); - uint32_t u32ContentFlags = ServiceQueryAnswerType_HostDomainAndPort; - // As the host domain is the base for the IP4- and IP6Address, remove these too + if (!pSQAnswer->m_TTLHostDomainAndPort.finalTimeoutLevel()) + { + bResult = ((_sendMDNSQuery(pSQAnswer->m_ServiceDomain, DNS_RRTYPE_SRV)) + && (pSQAnswer->m_TTLHostDomainAndPort.restart())); + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _checkServiceQueryCache: SRV update " + "scheduled for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" host domain and port %s\n"), + (bResult ? "OK" : "FAILURE")); + printedInfo = true;); + } + else + { + // Timed out! -> Delete + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _checkServiceQueryCache: Will remove SRV " + "answer for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" host domain and port\n")); + printedInfo = true;); + // Delete + pSQAnswer->m_HostDomain.clear(); + pSQAnswer->releaseHostDomain(); + pSQAnswer->m_u16Port = 0; + pSQAnswer->m_TTLHostDomainAndPort.set(0); + uint32_t u32ContentFlags = ServiceQueryAnswerType_HostDomainAndPort; + // As the host domain is the base for the IP4- and IP6Address, remove + // these too #ifdef MDNS_IP4_SUPPORT - pSQAnswer->releaseIP4Addresses(); - u32ContentFlags |= ServiceQueryAnswerType_IP4Address; + pSQAnswer->releaseIP4Addresses(); + u32ContentFlags |= ServiceQueryAnswerType_IP4Address; #endif #ifdef MDNS_IP6_SUPPORT - pSQAnswer->releaseIP6Addresses(); - u32ContentFlags |= ServiceQueryAnswerType_IP6Address; + pSQAnswer->releaseIP6Addresses(); + u32ContentFlags |= ServiceQueryAnswerType_IP6Address; #endif - // Remove content flags for deleted answer parts - pSQAnswer->m_u32ContentFlags &= ~u32ContentFlags; - if (pServiceQuery->m_fnCallback) - { - MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); - pServiceQuery->m_fnCallback(serviceInfo, static_cast(u32ContentFlags), false); + // Remove content flags for deleted answer parts + pSQAnswer->m_u32ContentFlags &= ~u32ContentFlags; + if (pServiceQuery->m_fnCallback) + { + MDNSServiceInfo serviceInfo( + *this, (hMDNSServiceQuery)pServiceQuery, + pServiceQuery->indexOfAnswer(pSQAnswer)); + pServiceQuery->m_fnCallback( + serviceInfo, static_cast(u32ContentFlags), false); + } } - } - } // HostDomainAndPort flagged + } // HostDomainAndPort flagged - // Txts (from TXT) - if ((bResult) && - (pSQAnswer->m_TTLTxts.flagged())) - { - - if (!pSQAnswer->m_TTLTxts.finalTimeoutLevel()) + // Txts (from TXT) + if ((bResult) && (pSQAnswer->m_TTLTxts.flagged())) { - - bResult = ((_sendMDNSQuery(pSQAnswer->m_ServiceDomain, DNS_RRTYPE_TXT)) && - (pSQAnswer->m_TTLTxts.restart())); - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: TXT update scheduled for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" TXTs %s\n"), (bResult ? "OK" : "FAILURE")); - printedInfo = true; - ); - } - else - { - // Timed out! -> Delete - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: Will remove TXT answer for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" TXTs\n")); - printedInfo = true; - ); - // Delete - pSQAnswer->m_Txts.clear(); - pSQAnswer->m_TTLTxts.set(0); - - // Remove content flags for deleted answer parts - pSQAnswer->m_u32ContentFlags &= ~ServiceQueryAnswerType_Txts; - - if (pServiceQuery->m_fnCallback) + if (!pSQAnswer->m_TTLTxts.finalTimeoutLevel()) { - MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); - pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_Txts), false); + bResult = ((_sendMDNSQuery(pSQAnswer->m_ServiceDomain, DNS_RRTYPE_TXT)) + && (pSQAnswer->m_TTLTxts.restart())); + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _checkServiceQueryCache: TXT update " + "scheduled for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" TXTs %s\n"), + (bResult ? "OK" : "FAILURE")); + printedInfo = true;); } - } - } // TXTs flagged - - // 3. level answers -#ifdef MDNS_IP4_SUPPORT - // IP4Address (from A) - stcMDNSServiceQuery::stcAnswer::stcIP4Address* pIP4Address = pSQAnswer->m_pIP4Addresses; - bool bAUpdateQuerySent = false; - while ((pIP4Address) && - (bResult)) - { - - stcMDNSServiceQuery::stcAnswer::stcIP4Address* pNextIP4Address = pIP4Address->m_pNext; // Get 'next' early, as 'current' may be deleted at the end... - - if (pIP4Address->m_TTL.flagged()) - { - - if (!pIP4Address->m_TTL.finalTimeoutLevel()) // Needs update + else { + // Timed out! -> Delete + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _checkServiceQueryCache: Will remove TXT " + "answer for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" TXTs\n")); + printedInfo = true;); + // Delete + pSQAnswer->m_Txts.clear(); + pSQAnswer->m_TTLTxts.set(0); + + // Remove content flags for deleted answer parts + pSQAnswer->m_u32ContentFlags &= ~ServiceQueryAnswerType_Txts; - if ((bAUpdateQuerySent) || - ((bResult = _sendMDNSQuery(pSQAnswer->m_HostDomain, DNS_RRTYPE_A)))) + if (pServiceQuery->m_fnCallback) { - - pIP4Address->m_TTL.restart(); - bAUpdateQuerySent = true; - - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: IP4 update scheduled for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" IP4 address (%s)\n"), (pIP4Address->m_IPAddress.toString().c_str())); - printedInfo = true; - ); + MDNSServiceInfo serviceInfo( + *this, (hMDNSServiceQuery)pServiceQuery, + pServiceQuery->indexOfAnswer(pSQAnswer)); + pServiceQuery->m_fnCallback( + serviceInfo, + static_cast(ServiceQueryAnswerType_Txts), false); } } - else + } // TXTs flagged + + // 3. level answers +#ifdef MDNS_IP4_SUPPORT + // IP4Address (from A) + stcMDNSServiceQuery::stcAnswer::stcIP4Address* pIP4Address + = pSQAnswer->m_pIP4Addresses; + bool bAUpdateQuerySent = false; + while ((pIP4Address) && (bResult)) + { + stcMDNSServiceQuery::stcAnswer::stcIP4Address* pNextIP4Address + = pIP4Address->m_pNext; // Get 'next' early, as 'current' may be + // deleted at the end... + + if (pIP4Address->m_TTL.flagged()) { - // Timed out! -> Delete - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: Will remove IP4 answer for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" IP4 address\n")); - printedInfo = true; - ); - pSQAnswer->removeIP4Address(pIP4Address); - if (!pSQAnswer->m_pIP4Addresses) // NO IP4 address left -> remove content flag + if (!pIP4Address->m_TTL.finalTimeoutLevel()) // Needs update { - pSQAnswer->m_u32ContentFlags &= ~ServiceQueryAnswerType_IP4Address; + if ((bAUpdateQuerySent) + || ((bResult + = _sendMDNSQuery(pSQAnswer->m_HostDomain, DNS_RRTYPE_A)))) + { + pIP4Address->m_TTL.restart(); + bAUpdateQuerySent = true; + + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _checkServiceQueryCache: IP4 " + "update scheduled for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P( + PSTR(" IP4 address (%s)\n"), + (pIP4Address->m_IPAddress.toString().c_str())); + printedInfo = true;); + } } - // Notify client - if (pServiceQuery->m_fnCallback) + else { - MDNSServiceInfo serviceInfo(*this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer)); - pServiceQuery->m_fnCallback(serviceInfo, static_cast(ServiceQueryAnswerType_IP4Address), false); + // Timed out! -> Delete + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _checkServiceQueryCache: Will remove IP4 " + "answer for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" IP4 address\n")); + printedInfo = true;); + pSQAnswer->removeIP4Address(pIP4Address); + if (!pSQAnswer->m_pIP4Addresses) // NO IP4 address left -> remove + // content flag + { + pSQAnswer->m_u32ContentFlags + &= ~ServiceQueryAnswerType_IP4Address; + } + // Notify client + if (pServiceQuery->m_fnCallback) + { + MDNSServiceInfo serviceInfo( + *this, (hMDNSServiceQuery)pServiceQuery, + pServiceQuery->indexOfAnswer(pSQAnswer)); + pServiceQuery->m_fnCallback( + serviceInfo, + static_cast(ServiceQueryAnswerType_IP4Address), + false); + } } - } - } // IP4 flagged + } // IP4 flagged - pIP4Address = pNextIP4Address; // Next - } // while + pIP4Address = pNextIP4Address; // Next + } // while #endif #ifdef MDNS_IP6_SUPPORT - // IP6Address (from AAAA) - stcMDNSServiceQuery::stcAnswer::stcIP6Address* pIP6Address = pSQAnswer->m_pIP6Addresses; - bool bAAAAUpdateQuerySent = false; - while ((pIP6Address) && - (bResult)) - { - - stcMDNSServiceQuery::stcAnswer::stcIP6Address* pNextIP6Address = pIP6Address->m_pNext; // Get 'next' early, as 'current' may be deleted at the end... - - if (pIP6Address->m_TTL.flagged()) + // IP6Address (from AAAA) + stcMDNSServiceQuery::stcAnswer::stcIP6Address* pIP6Address + = pSQAnswer->m_pIP6Addresses; + bool bAAAAUpdateQuerySent = false; + while ((pIP6Address) && (bResult)) { + stcMDNSServiceQuery::stcAnswer::stcIP6Address* pNextIP6Address + = pIP6Address->m_pNext; // Get 'next' early, as 'current' may be + // deleted at the end... - if (!pIP6Address->m_TTL.finalTimeoutLevel()) // Needs update - { - - if ((bAAAAUpdateQuerySent) || - ((bResult = _sendMDNSQuery(pSQAnswer->m_HostDomain, DNS_RRTYPE_AAAA)))) - { - - pIP6Address->m_TTL.restart(); - bAAAAUpdateQuerySent = true; - - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: IP6 update scheduled for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" IP6 address (%s)\n"), (pIP6Address->m_IPAddress.toString().c_str())); - printedInfo = true; - ); - } - } - else + if (pIP6Address->m_TTL.flagged()) { - // Timed out! -> Delete - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: Will remove answer for ")); - _printRRDomain(pSQAnswer->m_ServiceDomain); - DEBUG_OUTPUT.printf_P(PSTR(" IP6Address\n")); - printedInfo = true; - ); - pSQAnswer->removeIP6Address(pIP6Address); - if (!pSQAnswer->m_pIP6Addresses) // NO IP6 address left -> remove content flag + if (!pIP6Address->m_TTL.finalTimeoutLevel()) // Needs update { - pSQAnswer->m_u32ContentFlags &= ~ServiceQueryAnswerType_IP6Address; + if ((bAAAAUpdateQuerySent) + || ((bResult = _sendMDNSQuery(pSQAnswer->m_HostDomain, + DNS_RRTYPE_AAAA)))) + { + pIP6Address->m_TTL.restart(); + bAAAAUpdateQuerySent = true; + + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _checkServiceQueryCache: IP6 " + "update scheduled for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P( + PSTR(" IP6 address (%s)\n"), + (pIP6Address->m_IPAddress.toString().c_str())); + printedInfo = true;); + } } - // Notify client - if (pServiceQuery->m_fnCallback) + else { - pServiceQuery->m_fnCallback(this, (hMDNSServiceQuery)pServiceQuery, pServiceQuery->indexOfAnswer(pSQAnswer), ServiceQueryAnswerType_IP6Address, false, pServiceQuery->m_pUserdata); + // Timed out! -> Delete + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _checkServiceQueryCache: Will remove " + "answer for ")); + _printRRDomain(pSQAnswer->m_ServiceDomain); + DEBUG_OUTPUT.printf_P(PSTR(" IP6Address\n")); + printedInfo = true;); + pSQAnswer->removeIP6Address(pIP6Address); + if (!pSQAnswer->m_pIP6Addresses) // NO IP6 address left -> remove + // content flag + { + pSQAnswer->m_u32ContentFlags + &= ~ServiceQueryAnswerType_IP6Address; + } + // Notify client + if (pServiceQuery->m_fnCallback) + { + pServiceQuery->m_fnCallback( + this, (hMDNSServiceQuery)pServiceQuery, + pServiceQuery->indexOfAnswer(pSQAnswer), + ServiceQueryAnswerType_IP6Address, false, + pServiceQuery->m_pUserdata); + } } - } - } // IP6 flagged + } // IP6 flagged - pIP6Address = pNextIP6Address; // Next - } // while + pIP6Address = pNextIP6Address; // Next + } // while #endif - pSQAnswer = pNextSQAnswer; + pSQAnswer = pNextSQAnswer; + } } } + DEBUG_EX_INFO(if (printedInfo) { DEBUG_OUTPUT.printf_P(PSTR("\n")); }); + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: FAILED!\n")); + }); + return bResult; } - DEBUG_EX_INFO( - if (printedInfo) -{ - DEBUG_OUTPUT.printf_P(PSTR("\n")); - } - ); - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _checkServiceQueryCache: FAILED!\n")); - }); - return bResult; -} + /* + MDNSResponder::_replyMaskForHost -/* - MDNSResponder::_replyMaskForHost - - Determines the relevant host answers for the given question. - - A question for the hostname (eg. esp8266.local) will result in an A/AAAA (eg. 192.168.2.129) reply. - - A question for the reverse IP address (eg. 192-168.2.120.inarpa.arpa) will result in an PTR_IP4 (eg. esp8266.local) reply. - - In addition, a full name match (question domain == host domain) is marked. -*/ -uint8_t MDNSResponder::_replyMaskForHost(const MDNSResponder::stcMDNS_RRHeader& p_RRHeader, - bool* p_pbFullNameMatch /*= 0*/) const -{ - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _replyMaskForHost\n"));); - - uint8_t u8ReplyMask = 0; - (p_pbFullNameMatch ? *p_pbFullNameMatch = false : 0); + Determines the relevant host answers for the given question. + - A question for the hostname (eg. esp8266.local) will result in an A/AAAA (eg. + 192.168.2.129) reply. + - A question for the reverse IP address (eg. 192-168.2.120.inarpa.arpa) will result in an + PTR_IP4 (eg. esp8266.local) reply. - if ((DNS_RRCLASS_IN == p_RRHeader.m_Attributes.m_u16Class) || - (DNS_RRCLASS_ANY == p_RRHeader.m_Attributes.m_u16Class)) + In addition, a full name match (question domain == host domain) is marked. + */ + uint8_t MDNSResponder::_replyMaskForHost(const MDNSResponder::stcMDNS_RRHeader& p_RRHeader, + bool* p_pbFullNameMatch /*= 0*/) const { + // DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _replyMaskForHost\n"));); - if ((DNS_RRTYPE_PTR == p_RRHeader.m_Attributes.m_u16Type) || - (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) + uint8_t u8ReplyMask = 0; + (p_pbFullNameMatch ? * p_pbFullNameMatch = false : 0); + + if ((DNS_RRCLASS_IN == p_RRHeader.m_Attributes.m_u16Class) + || (DNS_RRCLASS_ANY == p_RRHeader.m_Attributes.m_u16Class)) { - // PTR request -#ifdef MDNS_IP4_SUPPORT - stcMDNS_RRDomain reverseIP4Domain; - for (netif* pNetIf = netif_list; pNetIf; pNetIf = pNetIf->next) + if ((DNS_RRTYPE_PTR == p_RRHeader.m_Attributes.m_u16Type) + || (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) { - if (netif_is_up(pNetIf)) + // PTR request +#ifdef MDNS_IP4_SUPPORT + stcMDNS_RRDomain reverseIP4Domain; + for (netif* pNetIf = netif_list; pNetIf; pNetIf = pNetIf->next) { - if ((_buildDomainForReverseIP4(pNetIf->ip_addr, reverseIP4Domain)) && - (p_RRHeader.m_Domain == reverseIP4Domain)) + if (netif_is_up(pNetIf) && IPAddress(pNetIf->ip_addr).isSet()) { - // Reverse domain match - u8ReplyMask |= ContentFlag_PTR_IP4; + if ((_buildDomainForReverseIP4(pNetIf->ip_addr, reverseIP4Domain)) + && (p_RRHeader.m_Domain == reverseIP4Domain)) + { + // Reverse domain match + u8ReplyMask |= ContentFlag_PTR_IP4; + } } } - } #endif #ifdef MDNS_IP6_SUPPORT - // TODO + // TODO #endif - } // Address qeuest - - stcMDNS_RRDomain hostDomain; - if ((_buildDomainForHost(m_pcHostname, hostDomain)) && - (p_RRHeader.m_Domain == hostDomain)) // Host domain match - { + } // Address qeuest - (p_pbFullNameMatch ? (*p_pbFullNameMatch = true) : (0)); + stcMDNS_RRDomain hostDomain; + if ((_buildDomainForHost(m_pcHostname, hostDomain)) + && (p_RRHeader.m_Domain == hostDomain)) // Host domain match + { + (p_pbFullNameMatch ? (*p_pbFullNameMatch = true) : (0)); #ifdef MDNS_IP4_SUPPORT - if ((DNS_RRTYPE_A == p_RRHeader.m_Attributes.m_u16Type) || - (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) - { - // IP4 address request - u8ReplyMask |= ContentFlag_A; - } + if ((DNS_RRTYPE_A == p_RRHeader.m_Attributes.m_u16Type) + || (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) + { + // IP4 address request + u8ReplyMask |= ContentFlag_A; + } #endif #ifdef MDNS_IP6_SUPPORT - if ((DNS_RRTYPE_AAAA == p_RRHeader.m_Attributes.m_u16Type) || - (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) - { - // IP6 address request - u8ReplyMask |= ContentFlag_AAAA; - } + if ((DNS_RRTYPE_AAAA == p_RRHeader.m_Attributes.m_u16Type) + || (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) + { + // IP6 address request + u8ReplyMask |= ContentFlag_AAAA; + } #endif + } } - } - else - { - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _replyMaskForHost: INVALID RR-class (0x%04X)!\n"), p_RRHeader.m_Attributes.m_u16Class);); - } - DEBUG_EX_INFO(if (u8ReplyMask) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _replyMaskForHost: 0x%X\n"), u8ReplyMask); - }); - return u8ReplyMask; -} - -/* - MDNSResponder::_replyMaskForService - - Determines the relevant service answers for the given question - - A PTR dns-sd service enum question (_services.dns-sd._udp.local) will result into an PTR_TYPE (eg. _http._tcp.local) answer - - A PTR service type question (eg. _http._tcp.local) will result into an PTR_NAME (eg. MyESP._http._tcp.local) answer - - A PTR service name question (eg. MyESP._http._tcp.local) will result into an PTR_NAME (eg. MyESP._http._tcp.local) answer - - A SRV service name question (eg. MyESP._http._tcp.local) will result into an SRV (eg. 5000 MyESP.local) answer - - A TXT service name question (eg. MyESP._http._tcp.local) will result into an TXT (eg. c#=1) answer - - In addition, a full name match (question domain == service instance domain) is marked. -*/ -uint8_t MDNSResponder::_replyMaskForService(const MDNSResponder::stcMDNS_RRHeader& p_RRHeader, - const MDNSResponder::stcMDNSService& p_Service, - bool* p_pbFullNameMatch /*= 0*/) const -{ - - uint8_t u8ReplyMask = 0; - (p_pbFullNameMatch ? *p_pbFullNameMatch = false : 0); - - if ((DNS_RRCLASS_IN == p_RRHeader.m_Attributes.m_u16Class) || - (DNS_RRCLASS_ANY == p_RRHeader.m_Attributes.m_u16Class)) - { - - stcMDNS_RRDomain DNSSDDomain; - if ((_buildDomainForDNSSD(DNSSDDomain)) && // _services._dns-sd._udp.local - (p_RRHeader.m_Domain == DNSSDDomain) && - ((DNS_RRTYPE_PTR == p_RRHeader.m_Attributes.m_u16Type) || - (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type))) + else { - // Common service info requested - u8ReplyMask |= ContentFlag_PTR_TYPE; + // DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _replyMaskForHost: INVALID + // RR-class (0x%04X)!\n"), p_RRHeader.m_Attributes.m_u16Class);); } + DEBUG_EX_INFO(if (u8ReplyMask) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _replyMaskForHost: 0x%X\n"), u8ReplyMask); + }); + return u8ReplyMask; + } - stcMDNS_RRDomain serviceDomain; - if ((_buildDomainForService(p_Service, false, serviceDomain)) && // eg. _http._tcp.local - (p_RRHeader.m_Domain == serviceDomain) && - ((DNS_RRTYPE_PTR == p_RRHeader.m_Attributes.m_u16Type) || - (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type))) - { - // Special service info requested - u8ReplyMask |= ContentFlag_PTR_NAME; - } + /* + MDNSResponder::_replyMaskForService + + Determines the relevant service answers for the given question + - A PTR dns-sd service enum question (_services.dns-sd._udp.local) will result into an + PTR_TYPE (eg. _http._tcp.local) answer + - A PTR service type question (eg. _http._tcp.local) will result into an PTR_NAME (eg. + MyESP._http._tcp.local) answer + - A PTR service name question (eg. MyESP._http._tcp.local) will result into an PTR_NAME (eg. + MyESP._http._tcp.local) answer + - A SRV service name question (eg. MyESP._http._tcp.local) will result into an SRV (eg. 5000 + MyESP.local) answer + - A TXT service name question (eg. MyESP._http._tcp.local) will result into an TXT (eg. + c#=1) answer + + In addition, a full name match (question domain == service instance domain) is marked. + */ + uint8_t MDNSResponder::_replyMaskForService(const MDNSResponder::stcMDNS_RRHeader& p_RRHeader, + const MDNSResponder::stcMDNSService& p_Service, + bool* p_pbFullNameMatch /*= 0*/) const + { + uint8_t u8ReplyMask = 0; + (p_pbFullNameMatch ? * p_pbFullNameMatch = false : 0); - if ((_buildDomainForService(p_Service, true, serviceDomain)) && // eg. MyESP._http._tcp.local - (p_RRHeader.m_Domain == serviceDomain)) + if ((DNS_RRCLASS_IN == p_RRHeader.m_Attributes.m_u16Class) + || (DNS_RRCLASS_ANY == p_RRHeader.m_Attributes.m_u16Class)) { + stcMDNS_RRDomain DNSSDDomain; + if ((_buildDomainForDNSSD(DNSSDDomain)) && // _services._dns-sd._udp.local + (p_RRHeader.m_Domain == DNSSDDomain) + && ((DNS_RRTYPE_PTR == p_RRHeader.m_Attributes.m_u16Type) + || (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type))) + { + // Common service info requested + u8ReplyMask |= ContentFlag_PTR_TYPE; + } - (p_pbFullNameMatch ? (*p_pbFullNameMatch = true) : (0)); - - if ((DNS_RRTYPE_SRV == p_RRHeader.m_Attributes.m_u16Type) || - (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) + stcMDNS_RRDomain serviceDomain; + if ((_buildDomainForService(p_Service, false, serviceDomain)) + && // eg. _http._tcp.local + (p_RRHeader.m_Domain == serviceDomain) + && ((DNS_RRTYPE_PTR == p_RRHeader.m_Attributes.m_u16Type) + || (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type))) { - // Instance info SRV requested - u8ReplyMask |= ContentFlag_SRV; + // Special service info requested + u8ReplyMask |= ContentFlag_PTR_NAME; } - if ((DNS_RRTYPE_TXT == p_RRHeader.m_Attributes.m_u16Type) || - (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) + + if ((_buildDomainForService(p_Service, true, serviceDomain)) + && // eg. MyESP._http._tcp.local + (p_RRHeader.m_Domain == serviceDomain)) { - // Instance info TXT requested - u8ReplyMask |= ContentFlag_TXT; + (p_pbFullNameMatch ? (*p_pbFullNameMatch = true) : (0)); + + if ((DNS_RRTYPE_SRV == p_RRHeader.m_Attributes.m_u16Type) + || (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) + { + // Instance info SRV requested + u8ReplyMask |= ContentFlag_SRV; + } + if ((DNS_RRTYPE_TXT == p_RRHeader.m_Attributes.m_u16Type) + || (DNS_RRTYPE_ANY == p_RRHeader.m_Attributes.m_u16Type)) + { + // Instance info TXT requested + u8ReplyMask |= ContentFlag_TXT; + } } } + else + { + // DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _replyMaskForService: + // INVALID RR-class (0x%04X)!\n"), p_RRHeader.m_Attributes.m_u16Class);); + } + DEBUG_EX_INFO(if (u8ReplyMask) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _replyMaskForService(%s.%s.%s): 0x%X\n"), + p_Service.m_pcName, p_Service.m_pcService, p_Service.m_pcProtocol, + u8ReplyMask); + }); + return u8ReplyMask; } - else - { - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _replyMaskForService: INVALID RR-class (0x%04X)!\n"), p_RRHeader.m_Attributes.m_u16Class);); - } - DEBUG_EX_INFO(if (u8ReplyMask) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _replyMaskForService(%s.%s.%s): 0x%X\n"), p_Service.m_pcName, p_Service.m_pcService, p_Service.m_pcProtocol, u8ReplyMask); - }); - return u8ReplyMask; -} -} // namespace MDNSImplementation +} // namespace MDNSImplementation -} // namespace esp8266 +} // namespace esp8266 diff --git a/libraries/ESP8266mDNS/src/LEAmDNS_Helpers.cpp b/libraries/ESP8266mDNS/src/LEAmDNS_Helpers.cpp index 48f4e4c5ee..2d12f9042e 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS_Helpers.cpp +++ b/libraries/ESP8266mDNS/src/LEAmDNS_Helpers.cpp @@ -23,7 +23,7 @@ */ #include -#include // strrstr() +#include // strrstr() #include "ESP8266mDNS.h" #include "LEAmDNS_lwIPdefs.h" @@ -38,53 +38,79 @@ namespace esp8266 namespace MDNSImplementation { -/** - HELPERS -*/ - -/* - MDNSResponder::indexDomain (static) + /** + HELPERS + */ - Updates the given domain 'p_rpcHostname' by appending a delimiter and an index number. + /* + MDNSResponder::indexDomain (static) - If the given domain already hasa numeric index (after the given delimiter), this index - incremented. If not, the delimiter and index '2' is added. + Updates the given domain 'p_rpcHostname' by appending a delimiter and an index number. - If 'p_rpcHostname' is empty (==0), the given default name 'p_pcDefaultHostname' is used, - if no default is given, 'esp8266' is used. + If the given domain already hasa numeric index (after the given delimiter), this index + incremented. If not, the delimiter and index '2' is added. -*/ -/*static*/ bool MDNSResponder::indexDomain(char*& p_rpcDomain, - const char* p_pcDivider /*= "-"*/, - const char* p_pcDefaultDomain /*= 0*/) -{ + If 'p_rpcHostname' is empty (==0), the given default name 'p_pcDefaultHostname' is used, + if no default is given, 'esp8266' is used. - bool bResult = false; + */ + /*static*/ bool MDNSResponder::indexDomain(char*& p_rpcDomain, + const char* p_pcDivider /*= "-"*/, + const char* p_pcDefaultDomain /*= 0*/) + { + bool bResult = false; - // Ensure a divider exists; use '-' as default - const char* pcDivider = (p_pcDivider ? : "-"); + // Ensure a divider exists; use '-' as default + const char* pcDivider = (p_pcDivider ?: "-"); - if (p_rpcDomain) - { - const char* pFoundDivider = strrstr(p_rpcDomain, pcDivider); - if (pFoundDivider) // maybe already extended + if (p_rpcDomain) { - char* pEnd = 0; - unsigned long ulIndex = strtoul((pFoundDivider + strlen(pcDivider)), &pEnd, 10); - if ((ulIndex) && - ((pEnd - p_rpcDomain) == (ptrdiff_t)strlen(p_rpcDomain)) && - (!*pEnd)) // Valid (old) index found + const char* pFoundDivider = strrstr(p_rpcDomain, pcDivider); + if (pFoundDivider) // maybe already extended { + char* pEnd = 0; + unsigned long ulIndex = strtoul((pFoundDivider + strlen(pcDivider)), &pEnd, 10); + if ((ulIndex) && ((pEnd - p_rpcDomain) == (ptrdiff_t)strlen(p_rpcDomain)) + && (!*pEnd)) // Valid (old) index found + { + char acIndexBuffer[16]; + sprintf(acIndexBuffer, "%lu", (++ulIndex)); + size_t stLength = ((pFoundDivider - p_rpcDomain + strlen(pcDivider)) + + strlen(acIndexBuffer) + 1); + char* pNewHostname = new char[stLength]; + if (pNewHostname) + { + memcpy(pNewHostname, p_rpcDomain, + (pFoundDivider - p_rpcDomain + strlen(pcDivider))); + pNewHostname[pFoundDivider - p_rpcDomain + strlen(pcDivider)] = 0; + strcat(pNewHostname, acIndexBuffer); + + delete[] p_rpcDomain; + p_rpcDomain = pNewHostname; + + bResult = true; + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.println( + F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!"));); + } + } + else + { + pFoundDivider = 0; // Flag the need to (base) extend the hostname + } + } - char acIndexBuffer[16]; - sprintf(acIndexBuffer, "%lu", (++ulIndex)); - size_t stLength = ((pFoundDivider - p_rpcDomain + strlen(pcDivider)) + strlen(acIndexBuffer) + 1); - char* pNewHostname = new char[stLength]; + if (!pFoundDivider) // not yet extended (or failed to increment extension) -> start + // indexing + { + size_t stLength = strlen(p_rpcDomain) + + (strlen(pcDivider) + 1 + 1); // Name + Divider + '2' + '\0' + char* pNewHostname = new char[stLength]; if (pNewHostname) { - memcpy(pNewHostname, p_rpcDomain, (pFoundDivider - p_rpcDomain + strlen(pcDivider))); - pNewHostname[pFoundDivider - p_rpcDomain + strlen(pcDivider)] = 0; - strcat(pNewHostname, acIndexBuffer); + sprintf(pNewHostname, "%s%s2", p_rpcDomain, pcDivider); delete[] p_rpcDomain; p_rpcDomain = pNewHostname; @@ -93,709 +119,659 @@ namespace MDNSImplementation } else { - DEBUG_EX_ERR(DEBUG_OUTPUT.println(F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!"));); + DEBUG_EX_ERR(DEBUG_OUTPUT.println( + F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!"));); } } - else - { - pFoundDivider = 0; // Flag the need to (base) extend the hostname - } } - - if (!pFoundDivider) // not yet extended (or failed to increment extension) -> start indexing + else { - size_t stLength = strlen(p_rpcDomain) + (strlen(pcDivider) + 1 + 1); // Name + Divider + '2' + '\0' - char* pNewHostname = new char[stLength]; - if (pNewHostname) - { - sprintf(pNewHostname, "%s%s2", p_rpcDomain, pcDivider); - - delete[] p_rpcDomain; - p_rpcDomain = pNewHostname; + // No given host domain, use base or default + const char* cpcDefaultName = (p_pcDefaultDomain ?: "esp8266"); + size_t stLength = strlen(cpcDefaultName) + 1; // '\0' + p_rpcDomain = new char[stLength]; + if (p_rpcDomain) + { + strncpy(p_rpcDomain, cpcDefaultName, stLength); bResult = true; } else { - DEBUG_EX_ERR(DEBUG_OUTPUT.println(F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!"));); + DEBUG_EX_ERR(DEBUG_OUTPUT.println( + F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!"));); } } + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] indexDomain: %s\n"), p_rpcDomain);); + return bResult; } - else - { - // No given host domain, use base or default - const char* cpcDefaultName = (p_pcDefaultDomain ? : "esp8266"); - - size_t stLength = strlen(cpcDefaultName) + 1; // '\0' - p_rpcDomain = new char[stLength]; - if (p_rpcDomain) - { - strncpy(p_rpcDomain, cpcDefaultName, stLength); - bResult = true; - } - else - { - DEBUG_EX_ERR(DEBUG_OUTPUT.println(F("[MDNSResponder] indexDomain: FAILED to alloc new hostname!"));); - } - } - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] indexDomain: %s\n"), p_rpcDomain);); - return bResult; -} + /* + UDP CONTEXT + */ -/* - UDP CONTEXT -*/ + bool MDNSResponder::_callProcess(void) + { + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf("[MDNSResponder] _callProcess (%lu, triggered by: %s)\n", millis(), + IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str());); -bool MDNSResponder::_callProcess(void) -{ - DEBUG_EX_INFO(DEBUG_OUTPUT.printf("[MDNSResponder] _callProcess (%lu, triggered by: %s)\n", millis(), IPAddress(m_pUDPContext->getRemoteAddress()).toString().c_str());); + return _process(false); + } - return _process(false); -} + /* + MDNSResponder::_allocUDPContext -/* - MDNSResponder::_allocUDPContext + (Re-)Creates the one-and-only UDP context for the MDNS responder. + The context is added to the 'multicast'-group and listens to the MDNS port (5353). + The travel-distance for multicast messages is set to 1 (local, via MDNS_MULTICAST_TTL). + Messages are received via the MDNSResponder '_update' function. CAUTION: This function + is called from the WiFi stack side of the ESP stack system. - (Re-)Creates the one-and-only UDP context for the MDNS responder. - The context is added to the 'multicast'-group and listens to the MDNS port (5353). - The travel-distance for multicast messages is set to 1 (local, via MDNS_MULTICAST_TTL). - Messages are received via the MDNSResponder '_update' function. CAUTION: This function - is called from the WiFi stack side of the ESP stack system. + */ + bool MDNSResponder::_allocUDPContext(void) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.println("[MDNSResponder] _allocUDPContext");); -*/ -bool MDNSResponder::_allocUDPContext(void) -{ - DEBUG_EX_INFO(DEBUG_OUTPUT.println("[MDNSResponder] _allocUDPContext");); + _releaseUDPContext(); + _joinMulticastGroups(); - _releaseUDPContext(); - _joinMulticastGroups(); + m_pUDPContext = new UdpContext; + m_pUDPContext->ref(); - m_pUDPContext = new UdpContext; - m_pUDPContext->ref(); + if (m_pUDPContext->listen(IP4_ADDR_ANY, DNS_MQUERY_PORT)) + { + m_pUDPContext->setMulticastTTL(MDNS_MULTICAST_TTL); + m_pUDPContext->onRx(std::bind(&MDNSResponder::_callProcess, this)); + } + else + { + return false; + } - if (m_pUDPContext->listen(IP4_ADDR_ANY, DNS_MQUERY_PORT)) - { - m_pUDPContext->setMulticastTTL(MDNS_MULTICAST_TTL); - m_pUDPContext->onRx(std::bind(&MDNSResponder::_callProcess, this)); + return true; } - else - { - return false; - } - - return true; -} -/* - MDNSResponder::_releaseUDPContext -*/ -bool MDNSResponder::_releaseUDPContext(void) -{ - - if (m_pUDPContext) + /* + MDNSResponder::_releaseUDPContext + */ + bool MDNSResponder::_releaseUDPContext(void) { - m_pUDPContext->unref(); - m_pUDPContext = 0; - _leaveMulticastGroups(); + if (m_pUDPContext) + { + m_pUDPContext->unref(); + m_pUDPContext = 0; + _leaveMulticastGroups(); + } + return true; } - return true; -} + /* + SERVICE QUERY + */ -/* - SERVICE QUERY -*/ - -/* - MDNSResponder::_allocServiceQuery -*/ -MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_allocServiceQuery(void) -{ - - stcMDNSServiceQuery* pServiceQuery = new stcMDNSServiceQuery; - if (pServiceQuery) + /* + MDNSResponder::_allocServiceQuery + */ + MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_allocServiceQuery(void) { - // Link to query list - pServiceQuery->m_pNext = m_pServiceQueries; - m_pServiceQueries = pServiceQuery; + stcMDNSServiceQuery* pServiceQuery = new stcMDNSServiceQuery; + if (pServiceQuery) + { + // Link to query list + pServiceQuery->m_pNext = m_pServiceQueries; + m_pServiceQueries = pServiceQuery; + } + return m_pServiceQueries; } - return m_pServiceQueries; -} - -/* - MDNSResponder::_removeServiceQuery -*/ -bool MDNSResponder::_removeServiceQuery(MDNSResponder::stcMDNSServiceQuery* p_pServiceQuery) -{ - bool bResult = false; - - if (p_pServiceQuery) + /* + MDNSResponder::_removeServiceQuery + */ + bool MDNSResponder::_removeServiceQuery(MDNSResponder::stcMDNSServiceQuery* p_pServiceQuery) { - stcMDNSServiceQuery* pPred = m_pServiceQueries; - while ((pPred) && - (pPred->m_pNext != p_pServiceQuery)) - { - pPred = pPred->m_pNext; - } - if (pPred) - { - pPred->m_pNext = p_pServiceQuery->m_pNext; - delete p_pServiceQuery; - bResult = true; - } - else // No predecesor + bool bResult = false; + + if (p_pServiceQuery) { - if (m_pServiceQueries == p_pServiceQuery) + stcMDNSServiceQuery* pPred = m_pServiceQueries; + while ((pPred) && (pPred->m_pNext != p_pServiceQuery)) { - m_pServiceQueries = p_pServiceQuery->m_pNext; + pPred = pPred->m_pNext; + } + if (pPred) + { + pPred->m_pNext = p_pServiceQuery->m_pNext; delete p_pServiceQuery; bResult = true; } - else + else // No predecessor { - DEBUG_EX_ERR(DEBUG_OUTPUT.println("[MDNSResponder] _releaseServiceQuery: INVALID service query!");); + if (m_pServiceQueries == p_pServiceQuery) + { + m_pServiceQueries = p_pServiceQuery->m_pNext; + delete p_pServiceQuery; + bResult = true; + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.println( + "[MDNSResponder] _releaseServiceQuery: INVALID service query!");); + } } } + return bResult; } - return bResult; -} -/* - MDNSResponder::_removeLegacyServiceQuery -*/ -bool MDNSResponder::_removeLegacyServiceQuery(void) -{ - - stcMDNSServiceQuery* pLegacyServiceQuery = _findLegacyServiceQuery(); - return (pLegacyServiceQuery ? _removeServiceQuery(pLegacyServiceQuery) : true); -} - -/* - MDNSResponder::_findServiceQuery + /* + MDNSResponder::_removeLegacyServiceQuery + */ + bool MDNSResponder::_removeLegacyServiceQuery(void) + { + stcMDNSServiceQuery* pLegacyServiceQuery = _findLegacyServiceQuery(); + return (pLegacyServiceQuery ? _removeServiceQuery(pLegacyServiceQuery) : true); + } - 'Convert' hMDNSServiceQuery to stcMDNSServiceQuery* (ensure existance) + /* + MDNSResponder::_findServiceQuery -*/ -MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findServiceQuery(MDNSResponder::hMDNSServiceQuery p_hServiceQuery) -{ + 'Convert' hMDNSServiceQuery to stcMDNSServiceQuery* (ensure existence) - stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; - while (pServiceQuery) + */ + MDNSResponder::stcMDNSServiceQuery* + MDNSResponder::_findServiceQuery(MDNSResponder::hMDNSServiceQuery p_hServiceQuery) { - if ((hMDNSServiceQuery)pServiceQuery == p_hServiceQuery) + stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; + while (pServiceQuery) { - break; + if ((hMDNSServiceQuery)pServiceQuery == p_hServiceQuery) + { + break; + } + pServiceQuery = pServiceQuery->m_pNext; } - pServiceQuery = pServiceQuery->m_pNext; + return pServiceQuery; } - return pServiceQuery; -} -/* - MDNSResponder::_findLegacyServiceQuery -*/ -MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findLegacyServiceQuery(void) -{ - - stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; - while (pServiceQuery) + /* + MDNSResponder::_findLegacyServiceQuery + */ + MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findLegacyServiceQuery(void) { - if (pServiceQuery->m_bLegacyQuery) + stcMDNSServiceQuery* pServiceQuery = m_pServiceQueries; + while (pServiceQuery) { - break; + if (pServiceQuery->m_bLegacyQuery) + { + break; + } + pServiceQuery = pServiceQuery->m_pNext; } - pServiceQuery = pServiceQuery->m_pNext; + return pServiceQuery; } - return pServiceQuery; -} -/* - MDNSResponder::_releaseServiceQueries -*/ -bool MDNSResponder::_releaseServiceQueries(void) -{ - while (m_pServiceQueries) + /* + MDNSResponder::_releaseServiceQueries + */ + bool MDNSResponder::_releaseServiceQueries(void) { - stcMDNSServiceQuery* pNext = m_pServiceQueries->m_pNext; - delete m_pServiceQueries; - m_pServiceQueries = pNext; + while (m_pServiceQueries) + { + stcMDNSServiceQuery* pNext = m_pServiceQueries->m_pNext; + delete m_pServiceQueries; + m_pServiceQueries = pNext; + } + return true; } - return true; -} - -/* - MDNSResponder::_findNextServiceQueryByServiceType -*/ -MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findNextServiceQueryByServiceType(const stcMDNS_RRDomain& p_ServiceTypeDomain, - const stcMDNSServiceQuery* p_pPrevServiceQuery) -{ - stcMDNSServiceQuery* pMatchingServiceQuery = 0; - stcMDNSServiceQuery* pServiceQuery = (p_pPrevServiceQuery ? p_pPrevServiceQuery->m_pNext : m_pServiceQueries); - while (pServiceQuery) + /* + MDNSResponder::_findNextServiceQueryByServiceType + */ + MDNSResponder::stcMDNSServiceQuery* MDNSResponder::_findNextServiceQueryByServiceType( + const stcMDNS_RRDomain& p_ServiceTypeDomain, const stcMDNSServiceQuery* p_pPrevServiceQuery) { - if (p_ServiceTypeDomain == pServiceQuery->m_ServiceTypeDomain) + stcMDNSServiceQuery* pMatchingServiceQuery = 0; + + stcMDNSServiceQuery* pServiceQuery + = (p_pPrevServiceQuery ? p_pPrevServiceQuery->m_pNext : m_pServiceQueries); + while (pServiceQuery) { - pMatchingServiceQuery = pServiceQuery; - break; + if (p_ServiceTypeDomain == pServiceQuery->m_ServiceTypeDomain) + { + pMatchingServiceQuery = pServiceQuery; + break; + } + pServiceQuery = pServiceQuery->m_pNext; } - pServiceQuery = pServiceQuery->m_pNext; + return pMatchingServiceQuery; } - return pMatchingServiceQuery; -} + /* + HOSTNAME + */ -/* - HOSTNAME -*/ - -/* - MDNSResponder::_setHostname -*/ -bool MDNSResponder::_setHostname(const char* p_pcHostname) -{ - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _allocHostname (%s)\n"), p_pcHostname);); + /* + MDNSResponder::_setHostname + */ + bool MDNSResponder::_setHostname(const char* p_pcHostname) + { + // DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _allocHostname (%s)\n"), + // p_pcHostname);); - bool bResult = false; + bool bResult = false; - _releaseHostname(); + _releaseHostname(); - size_t stLength = 0; - if ((p_pcHostname) && - (MDNS_DOMAIN_LABEL_MAXLENGTH >= (stLength = strlen(p_pcHostname)))) // char max size for a single label - { - // Copy in hostname characters as lowercase - if ((bResult = (0 != (m_pcHostname = new char[stLength + 1])))) + size_t stLength = 0; + if ((p_pcHostname) + && (MDNS_DOMAIN_LABEL_MAXLENGTH + >= (stLength = strlen(p_pcHostname)))) // char max size for a single label { -#ifdef MDNS_FORCE_LOWERCASE_HOSTNAME - size_t i = 0; - for (; i < stLength; ++i) + // Copy in hostname characters as lowercase + if ((bResult = (0 != (m_pcHostname = new char[stLength + 1])))) { - m_pcHostname[i] = (isupper(p_pcHostname[i]) ? tolower(p_pcHostname[i]) : p_pcHostname[i]); - } - m_pcHostname[i] = 0; +#ifdef MDNS_FORCE_LOWERCASE_HOSTNAME + size_t i = 0; + for (; i < stLength; ++i) + { + m_pcHostname[i] + = (isupper(p_pcHostname[i]) ? tolower(p_pcHostname[i]) : p_pcHostname[i]); + } + m_pcHostname[i] = 0; #else - strncpy(m_pcHostname, p_pcHostname, (stLength + 1)); + strncpy(m_pcHostname, p_pcHostname, (stLength + 1)); #endif + } } + return bResult; } - return bResult; -} -/* - MDNSResponder::_releaseHostname -*/ -bool MDNSResponder::_releaseHostname(void) -{ - - if (m_pcHostname) + /* + MDNSResponder::_releaseHostname + */ + bool MDNSResponder::_releaseHostname(void) { - delete[] m_pcHostname; - m_pcHostname = 0; + if (m_pcHostname) + { + delete[] m_pcHostname; + m_pcHostname = 0; + } + return true; } - return true; -} - - -/* - SERVICE -*/ - -/* - MDNSResponder::_allocService -*/ -MDNSResponder::stcMDNSService* MDNSResponder::_allocService(const char* p_pcName, - const char* p_pcService, - const char* p_pcProtocol, - uint16_t p_u16Port) -{ - stcMDNSService* pService = 0; - if (((!p_pcName) || - (MDNS_DOMAIN_LABEL_MAXLENGTH >= strlen(p_pcName))) && - (p_pcService) && - (MDNS_SERVICE_NAME_LENGTH >= strlen(p_pcService)) && - (p_pcProtocol) && - (MDNS_SERVICE_PROTOCOL_LENGTH >= strlen(p_pcProtocol)) && - (p_u16Port) && - (0 != (pService = new stcMDNSService)) && - (pService->setName(p_pcName ? : m_pcHostname)) && - (pService->setService(p_pcService)) && - (pService->setProtocol(p_pcProtocol))) + /* + SERVICE + */ + + /* + MDNSResponder::_allocService + */ + MDNSResponder::stcMDNSService* MDNSResponder::_allocService(const char* p_pcName, + const char* p_pcService, + const char* p_pcProtocol, + uint16_t p_u16Port) { + stcMDNSService* pService = 0; + if (((!p_pcName) || (MDNS_DOMAIN_LABEL_MAXLENGTH >= strlen(p_pcName))) && (p_pcService) + && (MDNS_SERVICE_NAME_LENGTH >= strlen(p_pcService)) && (p_pcProtocol) + && (MDNS_SERVICE_PROTOCOL_LENGTH >= strlen(p_pcProtocol)) && (p_u16Port) + && (0 != (pService = new stcMDNSService)) + && (pService->setName(p_pcName ?: m_pcHostname)) && (pService->setService(p_pcService)) + && (pService->setProtocol(p_pcProtocol))) + { + pService->m_bAutoName = (0 == p_pcName); + pService->m_u16Port = p_u16Port; - pService->m_bAutoName = (0 == p_pcName); - pService->m_u16Port = p_u16Port; - - // Add to list (or start list) - pService->m_pNext = m_pServices; - m_pServices = pService; + // Add to list (or start list) + pService->m_pNext = m_pServices; + m_pServices = pService; + } + return pService; } - return pService; -} -/* - MDNSResponder::_releaseService -*/ -bool MDNSResponder::_releaseService(MDNSResponder::stcMDNSService* p_pService) -{ - - bool bResult = false; - - if (p_pService) + /* + MDNSResponder::_releaseService + */ + bool MDNSResponder::_releaseService(MDNSResponder::stcMDNSService* p_pService) { - stcMDNSService* pPred = m_pServices; - while ((pPred) && - (pPred->m_pNext != p_pService)) - { - pPred = pPred->m_pNext; - } - if (pPred) - { - pPred->m_pNext = p_pService->m_pNext; - delete p_pService; - bResult = true; - } - else // No predecesor + bool bResult = false; + + if (p_pService) { - if (m_pServices == p_pService) + stcMDNSService* pPred = m_pServices; + while ((pPred) && (pPred->m_pNext != p_pService)) { - m_pServices = p_pService->m_pNext; + pPred = pPred->m_pNext; + } + if (pPred) + { + pPred->m_pNext = p_pService->m_pNext; delete p_pService; bResult = true; } - else + else // No predecessor { - DEBUG_EX_ERR(DEBUG_OUTPUT.println("[MDNSResponder] _releaseService: INVALID service!");); + if (m_pServices == p_pService) + { + m_pServices = p_pService->m_pNext; + delete p_pService; + bResult = true; + } + else + { + DEBUG_EX_ERR( + DEBUG_OUTPUT.println("[MDNSResponder] _releaseService: INVALID service!");); + } } } + return bResult; } - return bResult; -} -/* - MDNSResponder::_releaseServices -*/ -bool MDNSResponder::_releaseServices(void) -{ - - stcMDNSService* pService = m_pServices; - while (pService) + /* + MDNSResponder::_releaseServices + */ + bool MDNSResponder::_releaseServices(void) { - _releaseService(pService); - pService = m_pServices; + stcMDNSService* pService = m_pServices; + while (pService) + { + _releaseService(pService); + pService = m_pServices; + } + return true; } - return true; -} - -/* - MDNSResponder::_findService -*/ -MDNSResponder::stcMDNSService* MDNSResponder::_findService(const char* p_pcName, - const char* p_pcService, - const char* p_pcProtocol) -{ - stcMDNSService* pService = m_pServices; - while (pService) + /* + MDNSResponder::_findService + */ + MDNSResponder::stcMDNSService* MDNSResponder::_findService(const char* p_pcName, + const char* p_pcService, + const char* p_pcProtocol) { - if ((0 == strcmp(pService->m_pcName, p_pcName)) && - (0 == strcmp(pService->m_pcService, p_pcService)) && - (0 == strcmp(pService->m_pcProtocol, p_pcProtocol))) + stcMDNSService* pService = m_pServices; + while (pService) { - - break; + if ((0 == strcmp(pService->m_pcName, p_pcName)) + && (0 == strcmp(pService->m_pcService, p_pcService)) + && (0 == strcmp(pService->m_pcProtocol, p_pcProtocol))) + { + break; + } + pService = pService->m_pNext; } - pService = pService->m_pNext; + return pService; } - return pService; -} -/* - MDNSResponder::_findService -*/ -MDNSResponder::stcMDNSService* MDNSResponder::_findService(const MDNSResponder::hMDNSService p_hService) -{ - - stcMDNSService* pService = m_pServices; - while (pService) + /* + MDNSResponder::_findService + */ + MDNSResponder::stcMDNSService* + MDNSResponder::_findService(const MDNSResponder::hMDNSService p_hService) { - if (p_hService == (hMDNSService)pService) + stcMDNSService* pService = m_pServices; + while (pService) { - break; + if (p_hService == (hMDNSService)pService) + { + break; + } + pService = pService->m_pNext; } - pService = pService->m_pNext; + return pService; } - return pService; -} - - -/* - SERVICE TXT -*/ - -/* - MDNSResponder::_allocServiceTxt -*/ -MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_allocServiceTxt(MDNSResponder::stcMDNSService* p_pService, - const char* p_pcKey, - const char* p_pcValue, - bool p_bTemp) -{ - stcMDNSServiceTxt* pTxt = 0; + /* + SERVICE TXT + */ - if ((p_pService) && - (p_pcKey) && - (MDNS_SERVICE_TXT_MAXLENGTH > (p_pService->m_Txts.length() + - 1 + // Length byte - (p_pcKey ? strlen(p_pcKey) : 0) + - 1 + // '=' - (p_pcValue ? strlen(p_pcValue) : 0)))) + /* + MDNSResponder::_allocServiceTxt + */ + MDNSResponder::stcMDNSServiceTxt* + MDNSResponder::_allocServiceTxt(MDNSResponder::stcMDNSService* p_pService, const char* p_pcKey, + const char* p_pcValue, bool p_bTemp) { + stcMDNSServiceTxt* pTxt = 0; - pTxt = new stcMDNSServiceTxt; - if (pTxt) + if ((p_pService) && (p_pcKey) + && (MDNS_SERVICE_TXT_MAXLENGTH > (p_pService->m_Txts.length() + 1 + // Length byte + (p_pcKey ? strlen(p_pcKey) : 0) + 1 + // '=' + (p_pcValue ? strlen(p_pcValue) : 0)))) { - size_t stLength = (p_pcKey ? strlen(p_pcKey) : 0); - pTxt->m_pcKey = new char[stLength + 1]; - if (pTxt->m_pcKey) + pTxt = new stcMDNSServiceTxt; + if (pTxt) { - strncpy(pTxt->m_pcKey, p_pcKey, stLength); pTxt->m_pcKey[stLength] = 0; - } + size_t stLength = (p_pcKey ? strlen(p_pcKey) : 0); + pTxt->m_pcKey = new char[stLength + 1]; + if (pTxt->m_pcKey) + { + strncpy(pTxt->m_pcKey, p_pcKey, stLength); + pTxt->m_pcKey[stLength] = 0; + } - if (p_pcValue) - { - stLength = (p_pcValue ? strlen(p_pcValue) : 0); - pTxt->m_pcValue = new char[stLength + 1]; - if (pTxt->m_pcValue) + if (p_pcValue) { - strncpy(pTxt->m_pcValue, p_pcValue, stLength); pTxt->m_pcValue[stLength] = 0; + stLength = (p_pcValue ? strlen(p_pcValue) : 0); + pTxt->m_pcValue = new char[stLength + 1]; + if (pTxt->m_pcValue) + { + strncpy(pTxt->m_pcValue, p_pcValue, stLength); + pTxt->m_pcValue[stLength] = 0; + } } - } - pTxt->m_bTemp = p_bTemp; + pTxt->m_bTemp = p_bTemp; - // Add to list (or start list) - p_pService->m_Txts.add(pTxt); + // Add to list (or start list) + p_pService->m_Txts.add(pTxt); + } } + return pTxt; } - return pTxt; -} -/* - MDNSResponder::_releaseServiceTxt -*/ -bool MDNSResponder::_releaseServiceTxt(MDNSResponder::stcMDNSService* p_pService, - MDNSResponder::stcMDNSServiceTxt* p_pTxt) -{ - - return ((p_pService) && - (p_pTxt) && - (p_pService->m_Txts.remove(p_pTxt))); -} - -/* - MDNSResponder::_updateServiceTxt -*/ -MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_updateServiceTxt(MDNSResponder::stcMDNSService* p_pService, - MDNSResponder::stcMDNSServiceTxt* p_pTxt, - const char* p_pcValue, - bool p_bTemp) -{ - - if ((p_pService) && - (p_pTxt) && - (MDNS_SERVICE_TXT_MAXLENGTH > (p_pService->m_Txts.length() - - (p_pTxt->m_pcValue ? strlen(p_pTxt->m_pcValue) : 0) + - (p_pcValue ? strlen(p_pcValue) : 0)))) + /* + MDNSResponder::_releaseServiceTxt + */ + bool MDNSResponder::_releaseServiceTxt(MDNSResponder::stcMDNSService* p_pService, + MDNSResponder::stcMDNSServiceTxt* p_pTxt) { - p_pTxt->update(p_pcValue); - p_pTxt->m_bTemp = p_bTemp; + return ((p_pService) && (p_pTxt) && (p_pService->m_Txts.remove(p_pTxt))); } - return p_pTxt; -} -/* - MDNSResponder::_findServiceTxt -*/ -MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_findServiceTxt(MDNSResponder::stcMDNSService* p_pService, - const char* p_pcKey) -{ + /* + MDNSResponder::_updateServiceTxt + */ + MDNSResponder::stcMDNSServiceTxt* + MDNSResponder::_updateServiceTxt(MDNSResponder::stcMDNSService* p_pService, + MDNSResponder::stcMDNSServiceTxt* p_pTxt, + const char* p_pcValue, bool p_bTemp) + { + if ((p_pService) && (p_pTxt) + && (MDNS_SERVICE_TXT_MAXLENGTH + > (p_pService->m_Txts.length() - (p_pTxt->m_pcValue ? strlen(p_pTxt->m_pcValue) : 0) + + (p_pcValue ? strlen(p_pcValue) : 0)))) + { + p_pTxt->update(p_pcValue); + p_pTxt->m_bTemp = p_bTemp; + } + return p_pTxt; + } - return (p_pService ? p_pService->m_Txts.find(p_pcKey) : 0); -} + /* + MDNSResponder::_findServiceTxt + */ + MDNSResponder::stcMDNSServiceTxt* + MDNSResponder::_findServiceTxt(MDNSResponder::stcMDNSService* p_pService, const char* p_pcKey) + { + return (p_pService ? p_pService->m_Txts.find(p_pcKey) : 0); + } -/* - MDNSResponder::_findServiceTxt -*/ -MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_findServiceTxt(MDNSResponder::stcMDNSService* p_pService, - const hMDNSTxt p_hTxt) -{ + /* + MDNSResponder::_findServiceTxt + */ + MDNSResponder::stcMDNSServiceTxt* + MDNSResponder::_findServiceTxt(MDNSResponder::stcMDNSService* p_pService, const hMDNSTxt p_hTxt) + { + return (((p_pService) && (p_hTxt)) ? p_pService->m_Txts.find((stcMDNSServiceTxt*)p_hTxt) + : 0); + } - return (((p_pService) && (p_hTxt)) ? p_pService->m_Txts.find((stcMDNSServiceTxt*)p_hTxt) : 0); -} + /* + MDNSResponder::_addServiceTxt + */ + MDNSResponder::stcMDNSServiceTxt* + MDNSResponder::_addServiceTxt(MDNSResponder::stcMDNSService* p_pService, const char* p_pcKey, + const char* p_pcValue, bool p_bTemp) + { + stcMDNSServiceTxt* pResult = 0; -/* - MDNSResponder::_addServiceTxt -*/ -MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_addServiceTxt(MDNSResponder::stcMDNSService* p_pService, - const char* p_pcKey, - const char* p_pcValue, - bool p_bTemp) -{ - stcMDNSServiceTxt* pResult = 0; + if ((p_pService) && (p_pcKey) && (strlen(p_pcKey))) + { + stcMDNSServiceTxt* pTxt = p_pService->m_Txts.find(p_pcKey); + if (pTxt) + { + pResult = _updateServiceTxt(p_pService, pTxt, p_pcValue, p_bTemp); + } + else + { + pResult = _allocServiceTxt(p_pService, p_pcKey, p_pcValue, p_bTemp); + } + } + return pResult; + } - if ((p_pService) && - (p_pcKey) && - (strlen(p_pcKey))) + MDNSResponder::stcMDNSServiceTxt* + MDNSResponder::_answerKeyValue(const hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex) { + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + stcMDNSServiceQuery::stcAnswer* pSQAnswer + = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); + // Fill m_pcTxts (if not already done) + return (pSQAnswer) ? pSQAnswer->m_Txts.m_pTxts : 0; + } - stcMDNSServiceTxt* pTxt = p_pService->m_Txts.find(p_pcKey); - if (pTxt) + /* + MDNSResponder::_collectServiceTxts + */ + bool MDNSResponder::_collectServiceTxts(MDNSResponder::stcMDNSService& p_rService) + { + // Call Dynamic service callbacks + if (m_fnServiceTxtCallback) { - pResult = _updateServiceTxt(p_pService, pTxt, p_pcValue, p_bTemp); + m_fnServiceTxtCallback((hMDNSService)&p_rService); } - else + if (p_rService.m_fnTxtCallback) { - pResult = _allocServiceTxt(p_pService, p_pcKey, p_pcValue, p_bTemp); + p_rService.m_fnTxtCallback((hMDNSService)&p_rService); } + return true; } - return pResult; -} -MDNSResponder::stcMDNSServiceTxt* MDNSResponder::_answerKeyValue(const hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex) -{ - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - // Fill m_pcTxts (if not already done) - return (pSQAnswer) ? pSQAnswer->m_Txts.m_pTxts : 0; -} - -/* - MDNSResponder::_collectServiceTxts -*/ -bool MDNSResponder::_collectServiceTxts(MDNSResponder::stcMDNSService& p_rService) -{ - - // Call Dynamic service callbacks - if (m_fnServiceTxtCallback) + /* + MDNSResponder::_releaseTempServiceTxts + */ + bool MDNSResponder::_releaseTempServiceTxts(MDNSResponder::stcMDNSService& p_rService) { - m_fnServiceTxtCallback((hMDNSService)&p_rService); + return (p_rService.m_Txts.removeTempTxts()); } - if (p_rService.m_fnTxtCallback) - { - p_rService.m_fnTxtCallback((hMDNSService)&p_rService); - } - return true; -} - -/* - MDNSResponder::_releaseTempServiceTxts -*/ -bool MDNSResponder::_releaseTempServiceTxts(MDNSResponder::stcMDNSService& p_rService) -{ - - return (p_rService.m_Txts.removeTempTxts()); -} - -/* - MISC -*/ + /* + MISC + */ #ifdef DEBUG_ESP_MDNS_RESPONDER -/* - MDNSResponder::_printRRDomain -*/ -bool MDNSResponder::_printRRDomain(const MDNSResponder::stcMDNS_RRDomain& p_RRDomain) const -{ - - //DEBUG_OUTPUT.printf_P(PSTR("Domain: ")); - - const char* pCursor = p_RRDomain.m_acName; - uint8_t u8Length = *pCursor++; - if (u8Length) + /* + MDNSResponder::_printRRDomain + */ + bool MDNSResponder::_printRRDomain(const MDNSResponder::stcMDNS_RRDomain& p_RRDomain) const { - while (u8Length) + // DEBUG_OUTPUT.printf_P(PSTR("Domain: ")); + + const char* pCursor = p_RRDomain.m_acName; + uint8_t u8Length = *pCursor++; + if (u8Length) { - for (uint8_t u = 0; u < u8Length; ++u) - { - DEBUG_OUTPUT.printf_P(PSTR("%c"), *(pCursor++)); - } - u8Length = *pCursor++; - if (u8Length) + while (u8Length) { - DEBUG_OUTPUT.printf_P(PSTR(".")); + for (uint8_t u = 0; u < u8Length; ++u) + { + DEBUG_OUTPUT.printf_P(PSTR("%c"), *(pCursor++)); + } + u8Length = *pCursor++; + if (u8Length) + { + DEBUG_OUTPUT.printf_P(PSTR(".")); + } } } - } - else // empty domain - { - DEBUG_OUTPUT.printf_P(PSTR("-empty-")); - } - //DEBUG_OUTPUT.printf_P(PSTR("\n")); - - return true; -} + else // empty domain + { + DEBUG_OUTPUT.printf_P(PSTR("-empty-")); + } + // DEBUG_OUTPUT.printf_P(PSTR("\n")); -/* - MDNSResponder::_printRRAnswer -*/ -bool MDNSResponder::_printRRAnswer(const MDNSResponder::stcMDNS_RRAnswer& p_RRAnswer) const -{ + return true; + } - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] RRAnswer: ")); - _printRRDomain(p_RRAnswer.m_Header.m_Domain); - DEBUG_OUTPUT.printf_P(PSTR(" Type:0x%04X Class:0x%04X TTL:%u, "), p_RRAnswer.m_Header.m_Attributes.m_u16Type, p_RRAnswer.m_Header.m_Attributes.m_u16Class, p_RRAnswer.m_u32TTL); - switch (p_RRAnswer.m_Header.m_Attributes.m_u16Type & (~0x8000)) // Topmost bit might carry 'cache flush' flag + /* + MDNSResponder::_printRRAnswer + */ + bool MDNSResponder::_printRRAnswer(const MDNSResponder::stcMDNS_RRAnswer& p_RRAnswer) const { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] RRAnswer: ")); + _printRRDomain(p_RRAnswer.m_Header.m_Domain); + DEBUG_OUTPUT.printf_P(PSTR(" Type:0x%04X Class:0x%04X TTL:%u, "), + p_RRAnswer.m_Header.m_Attributes.m_u16Type, + p_RRAnswer.m_Header.m_Attributes.m_u16Class, p_RRAnswer.m_u32TTL); + switch (p_RRAnswer.m_Header.m_Attributes.m_u16Type + & (~0x8000)) // Topmost bit might carry 'cache flush' flag + { #ifdef MDNS_IP4_SUPPORT - case DNS_RRTYPE_A: - DEBUG_OUTPUT.printf_P(PSTR("A IP:%s"), ((const stcMDNS_RRAnswerA*)&p_RRAnswer)->m_IPAddress.toString().c_str()); - break; + case DNS_RRTYPE_A: + DEBUG_OUTPUT.printf_P( + PSTR("A IP:%s"), + ((const stcMDNS_RRAnswerA*)&p_RRAnswer)->m_IPAddress.toString().c_str()); + break; #endif - case DNS_RRTYPE_PTR: - DEBUG_OUTPUT.printf_P(PSTR("PTR ")); - _printRRDomain(((const stcMDNS_RRAnswerPTR*)&p_RRAnswer)->m_PTRDomain); - break; - case DNS_RRTYPE_TXT: - { - size_t stTxtLength = ((const stcMDNS_RRAnswerTXT*)&p_RRAnswer)->m_Txts.c_strLength(); - char* pTxts = new char[stTxtLength]; - if (pTxts) + case DNS_RRTYPE_PTR: + DEBUG_OUTPUT.printf_P(PSTR("PTR ")); + _printRRDomain(((const stcMDNS_RRAnswerPTR*)&p_RRAnswer)->m_PTRDomain); + break; + case DNS_RRTYPE_TXT: { - ((/*const c_str()!!*/stcMDNS_RRAnswerTXT*)&p_RRAnswer)->m_Txts.c_str(pTxts); - DEBUG_OUTPUT.printf_P(PSTR("TXT(%u) %s"), stTxtLength, pTxts); - delete[] pTxts; + size_t stTxtLength = ((const stcMDNS_RRAnswerTXT*)&p_RRAnswer)->m_Txts.c_strLength(); + char* pTxts = new char[stTxtLength]; + if (pTxts) + { + ((/*const c_str()!!*/ stcMDNS_RRAnswerTXT*)&p_RRAnswer)->m_Txts.c_str(pTxts); + DEBUG_OUTPUT.printf_P(PSTR("TXT(%zu) %s"), stTxtLength, pTxts); + delete[] pTxts; + } + break; } - break; - } #ifdef MDNS_IP6_SUPPORT - case DNS_RRTYPE_AAAA: - DEBUG_OUTPUT.printf_P(PSTR("AAAA IP:%s"), ((stcMDNS_RRAnswerA*&)p_rpRRAnswer)->m_IPAddress.toString().c_str()); - break; + case DNS_RRTYPE_AAAA: + DEBUG_OUTPUT.printf_P( + PSTR("AAAA IP:%s"), + ((stcMDNS_RRAnswerA*&)p_rpRRAnswer)->m_IPAddress.toString().c_str()); + break; #endif - case DNS_RRTYPE_SRV: - DEBUG_OUTPUT.printf_P(PSTR("SRV Port:%u "), ((const stcMDNS_RRAnswerSRV*)&p_RRAnswer)->m_u16Port); - _printRRDomain(((const stcMDNS_RRAnswerSRV*)&p_RRAnswer)->m_SRVDomain); - break; - default: - DEBUG_OUTPUT.printf_P(PSTR("generic ")); - break; - } - DEBUG_OUTPUT.printf_P(PSTR("\n")); + case DNS_RRTYPE_SRV: + DEBUG_OUTPUT.printf_P(PSTR("SRV Port:%u "), + ((const stcMDNS_RRAnswerSRV*)&p_RRAnswer)->m_u16Port); + _printRRDomain(((const stcMDNS_RRAnswerSRV*)&p_RRAnswer)->m_SRVDomain); + break; + default: + DEBUG_OUTPUT.printf_P(PSTR("generic ")); + break; + } + DEBUG_OUTPUT.printf_P(PSTR("\n")); - return true; -} + return true; + } #endif -} // namespace MDNSImplementation - -} // namespace esp8266 - - - +} // namespace MDNSImplementation +} // namespace esp8266 diff --git a/libraries/ESP8266mDNS/src/LEAmDNS_Priv.h b/libraries/ESP8266mDNS/src/LEAmDNS_Priv.h index a7d8a8f998..9b89141285 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS_Priv.h +++ b/libraries/ESP8266mDNS/src/LEAmDNS_Priv.h @@ -37,7 +37,7 @@ namespace MDNSImplementation // Enable class debug functions #define ESP_8266_MDNS_INCLUDE -//#define DEBUG_ESP_MDNS_RESPONDER + //#define DEBUG_ESP_MDNS_RESPONDER #if !defined(DEBUG_ESP_MDNS_RESPONDER) && defined(DEBUG_ESP_MDNS) #define DEBUG_ESP_MDNS_RESPONDER @@ -48,8 +48,9 @@ namespace MDNSImplementation #endif // -// If ENABLE_ESP_MDNS_RESPONDER_PASSIV_MODE is defined, the mDNS responder ignores a successful probing -// This allows to drive the responder in a environment, where 'update()' isn't called in the loop +// If ENABLE_ESP_MDNS_RESPONDER_PASSIV_MODE is defined, the mDNS responder ignores a successful +// probing This allows to drive the responder in a environment, where 'update()' isn't called in the +// loop //#define ENABLE_ESP_MDNS_RESPONDER_PASSIV_MODE // Enable/disable debug trace macros @@ -62,7 +63,7 @@ namespace MDNSImplementation #ifdef DEBUG_ESP_MDNS_RESPONDER #ifdef DEBUG_ESP_MDNS_INFO -#define DEBUG_EX_INFO(A) A +#define DEBUG_EX_INFO(A) A #else #define DEBUG_EX_INFO(A) #endif @@ -72,12 +73,12 @@ namespace MDNSImplementation #define DEBUG_EX_ERR(A) #endif #ifdef DEBUG_ESP_MDNS_TX -#define DEBUG_EX_TX(A) A +#define DEBUG_EX_TX(A) A #else #define DEBUG_EX_TX(A) #endif #ifdef DEBUG_ESP_MDNS_RX -#define DEBUG_EX_RX(A) A +#define DEBUG_EX_RX(A) A #else #define DEBUG_EX_RX(A) #endif @@ -88,19 +89,34 @@ namespace MDNSImplementation #define DEBUG_OUTPUT Serial #endif #else -#define DEBUG_EX_INFO(A) do { (void)0; } while (0) -#define DEBUG_EX_ERR(A) do { (void)0; } while (0) -#define DEBUG_EX_TX(A) do { (void)0; } while (0) -#define DEBUG_EX_RX(A) do { (void)0; } while (0) +#define DEBUG_EX_INFO(A) \ + do \ + { \ + (void)0; \ + } while (0) +#define DEBUG_EX_ERR(A) \ + do \ + { \ + (void)0; \ + } while (0) +#define DEBUG_EX_TX(A) \ + do \ + { \ + (void)0; \ + } while (0) +#define DEBUG_EX_RX(A) \ + do \ + { \ + (void)0; \ + } while (0) #endif /* already defined in lwIP ('lwip/prot/dns.h') #ifdef MDNS_IP4_SUPPORT - #define DNS_MQUERY_IPV4_GROUP_INIT (IPAddress(224, 0, 0, 251)) // ip_addr_t v4group = DNS_MQUERY_IPV4_GROUP_INIT - #endif - #ifdef MDNS_IP6_SUPPORT - #define DNS_MQUERY_IPV6_GROUP_INIT IPADDR6_INIT_HOST(0xFF020000,0,0,0xFB) // ip_addr_t v6group = DNS_MQUERY_IPV6_GROUP_INIT - #endif*/ + #define DNS_MQUERY_IPV4_GROUP_INIT (IPAddress(224, 0, 0, 251)) // ip_addr_t + v4group = DNS_MQUERY_IPV4_GROUP_INIT #endif #ifdef MDNS_IP6_SUPPORT #define + DNS_MQUERY_IPV6_GROUP_INIT IPADDR6_INIT_HOST(0xFF020000,0,0,0xFB) // ip_addr_t v6group = + DNS_MQUERY_IPV6_GROUP_INIT #endif*/ //#define MDNS_MULTICAST_PORT 5353 /* @@ -111,44 +127,44 @@ namespace MDNSImplementation However, RFC 3171 seems to force 255 instead */ -#define MDNS_MULTICAST_TTL 255/*1*/ +#define MDNS_MULTICAST_TTL 255 /*1*/ /* This is the MDNS record TTL Host level records are set to 2min (120s) service level records are set to 75min (4500s) */ -#define MDNS_HOST_TTL 120 -#define MDNS_SERVICE_TTL 4500 +#define MDNS_HOST_TTL 120 +#define MDNS_SERVICE_TTL 4500 /* - Compressed labels are flaged by the two topmost bits of the length byte being set + Compressed labels are flagged by the two topmost bits of the length byte being set */ -#define MDNS_DOMAIN_COMPRESS_MARK 0xC0 +#define MDNS_DOMAIN_COMPRESS_MARK 0xC0 /* Avoid endless recursion because of malformed compressed labels */ -#define MDNS_DOMAIN_MAX_REDIRCTION 6 +#define MDNS_DOMAIN_MAX_REDIRCTION 6 /* Default service priority and weight in SRV answers */ -#define MDNS_SRV_PRIORITY 0 -#define MDNS_SRV_WEIGHT 0 +#define MDNS_SRV_PRIORITY 0 +#define MDNS_SRV_WEIGHT 0 /* Delay between and number of probes for host and service domains Delay between and number of announces for host and service domains - Delay between and number of service queries; the delay is multiplied by the resent number in '_checkServiceQueryCache' + Delay between and number of service queries; the delay is multiplied by the resent number in + '_checkServiceQueryCache' */ -#define MDNS_PROBE_DELAY 250 -#define MDNS_PROBE_COUNT 3 -#define MDNS_ANNOUNCE_DELAY 1000 -#define MDNS_ANNOUNCE_COUNT 8 +#define MDNS_PROBE_DELAY 250 +#define MDNS_PROBE_COUNT 3 +#define MDNS_ANNOUNCE_DELAY 1000 +#define MDNS_ANNOUNCE_COUNT 8 #define MDNS_DYNAMIC_QUERY_RESEND_COUNT 5 #define MDNS_DYNAMIC_QUERY_RESEND_DELAY 5000 - /* Force host domain to use only lowercase letters */ @@ -156,7 +172,7 @@ namespace MDNSImplementation /* Enable/disable the usage of the F() macro in debug trace printf calls. - There needs to be an PGM comptible printf function to use this. + There needs to be an PGM compatible printf function to use this. USE_PGM_PRINTF and F */ @@ -167,15 +183,14 @@ namespace MDNSImplementation #ifdef F #undef F #endif -#define F(A) A +#define F(A) A #endif -} // namespace MDNSImplementation +} // namespace MDNSImplementation -} // namespace esp8266 +} // namespace esp8266 // Include the main header, so the submodlues only need to include this header #include "LEAmDNS.h" - #endif // MDNS_PRIV_H diff --git a/libraries/ESP8266mDNS/src/LEAmDNS_Structs.cpp b/libraries/ESP8266mDNS/src/LEAmDNS_Structs.cpp index 0e128aea8a..637f62869a 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS_Structs.cpp +++ b/libraries/ESP8266mDNS/src/LEAmDNS_Structs.cpp @@ -35,2456 +35,2253 @@ namespace esp8266 namespace MDNSImplementation { -/** - STRUCTS -*/ - -/** - MDNSResponder::stcMDNSServiceTxt - - One MDNS TXT item. - m_pcValue may be '\0'. - Objects can be chained together (list, m_pNext). - A 'm_bTemp' flag differentiates between static and dynamic items. - Output as byte array 'c#=1' is supported. -*/ - -/* - MDNSResponder::stcMDNSServiceTxt::stcMDNSServiceTxt constructor -*/ -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) -{ + /** + STRUCTS + */ - setKey(p_pcKey); - setValue(p_pcValue); -} + /** + MDNSResponder::stcMDNSServiceTxt -/* - MDNSResponder::stcMDNSServiceTxt::stcMDNSServiceTxt copy-constructor -*/ -MDNSResponder::stcMDNSServiceTxt::stcMDNSServiceTxt(const MDNSResponder::stcMDNSServiceTxt& p_Other) - : m_pNext(0), - m_pcKey(0), - m_pcValue(0), - m_bTemp(false) -{ - - operator=(p_Other); -} - -/* - MDNSResponder::stcMDNSServiceTxt::~stcMDNSServiceTxt destructor -*/ -MDNSResponder::stcMDNSServiceTxt::~stcMDNSServiceTxt(void) -{ + One MDNS TXT item. + m_pcValue may be '\0'. + Objects can be chained together (list, m_pNext). + A 'm_bTemp' flag differentiates between static and dynamic items. + Output as byte array 'c#=1' is supported. + */ - clear(); -} + /* + MDNSResponder::stcMDNSServiceTxt::stcMDNSServiceTxt constructor + */ + 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) + { + setKey(p_pcKey); + setValue(p_pcValue); + } -/* - MDNSResponder::stcMDNSServiceTxt::operator= -*/ -MDNSResponder::stcMDNSServiceTxt& MDNSResponder::stcMDNSServiceTxt::operator=(const MDNSResponder::stcMDNSServiceTxt& p_Other) -{ + /* + MDNSResponder::stcMDNSServiceTxt::stcMDNSServiceTxt copy-constructor + */ + MDNSResponder::stcMDNSServiceTxt::stcMDNSServiceTxt( + const MDNSResponder::stcMDNSServiceTxt& p_Other) : + m_pNext(0), m_pcKey(0), m_pcValue(0), m_bTemp(false) + { + operator=(p_Other); + } - if (&p_Other != this) + /* + MDNSResponder::stcMDNSServiceTxt::~stcMDNSServiceTxt destructor + */ + MDNSResponder::stcMDNSServiceTxt::~stcMDNSServiceTxt(void) { clear(); - set(p_Other.m_pcKey, p_Other.m_pcValue, p_Other.m_bTemp); } - return *this; -} - -/* - MDNSResponder::stcMDNSServiceTxt::clear -*/ -bool MDNSResponder::stcMDNSServiceTxt::clear(void) -{ - - releaseKey(); - releaseValue(); - return true; -} - -/* - MDNSResponder::stcMDNSServiceTxt::allocKey -*/ -char* MDNSResponder::stcMDNSServiceTxt::allocKey(size_t p_stLength) -{ - releaseKey(); - if (p_stLength) + /* + MDNSResponder::stcMDNSServiceTxt::operator= + */ + MDNSResponder::stcMDNSServiceTxt& + MDNSResponder::stcMDNSServiceTxt::operator=(const MDNSResponder::stcMDNSServiceTxt& p_Other) { - m_pcKey = new char[p_stLength + 1]; + if (&p_Other != this) + { + clear(); + set(p_Other.m_pcKey, p_Other.m_pcValue, p_Other.m_bTemp); + } + return *this; } - return m_pcKey; -} - -/* - MDNSResponder::stcMDNSServiceTxt::setKey -*/ -bool MDNSResponder::stcMDNSServiceTxt::setKey(const char* p_pcKey, - size_t p_stLength) -{ - bool bResult = false; + /* + MDNSResponder::stcMDNSServiceTxt::clear + */ + bool MDNSResponder::stcMDNSServiceTxt::clear(void) + { + releaseKey(); + releaseValue(); + return true; + } - releaseKey(); - if (p_stLength) + /* + MDNSResponder::stcMDNSServiceTxt::allocKey + */ + char* MDNSResponder::stcMDNSServiceTxt::allocKey(size_t p_stLength) { - if (allocKey(p_stLength)) + releaseKey(); + if (p_stLength) { - strncpy(m_pcKey, p_pcKey, p_stLength); - m_pcKey[p_stLength] = 0; - bResult = true; + m_pcKey = new char[p_stLength + 1]; } + return m_pcKey; } - return bResult; -} - -/* - MDNSResponder::stcMDNSServiceTxt::setKey -*/ -bool MDNSResponder::stcMDNSServiceTxt::setKey(const char* p_pcKey) -{ - return setKey(p_pcKey, (p_pcKey ? strlen(p_pcKey) : 0)); -} + /* + MDNSResponder::stcMDNSServiceTxt::setKey + */ + bool MDNSResponder::stcMDNSServiceTxt::setKey(const char* p_pcKey, size_t p_stLength) + { + bool bResult = false; -/* - MDNSResponder::stcMDNSServiceTxt::releaseKey -*/ -bool MDNSResponder::stcMDNSServiceTxt::releaseKey(void) -{ + releaseKey(); + if (p_stLength) + { + if (allocKey(p_stLength)) + { + strncpy(m_pcKey, p_pcKey, p_stLength); + m_pcKey[p_stLength] = 0; + bResult = true; + } + } + return bResult; + } - if (m_pcKey) + /* + MDNSResponder::stcMDNSServiceTxt::setKey + */ + bool MDNSResponder::stcMDNSServiceTxt::setKey(const char* p_pcKey) { - delete[] m_pcKey; - m_pcKey = 0; + return setKey(p_pcKey, (p_pcKey ? strlen(p_pcKey) : 0)); } - return true; -} - -/* - MDNSResponder::stcMDNSServiceTxt::allocValue -*/ -char* MDNSResponder::stcMDNSServiceTxt::allocValue(size_t p_stLength) -{ - releaseValue(); - if (p_stLength) + /* + MDNSResponder::stcMDNSServiceTxt::releaseKey + */ + bool MDNSResponder::stcMDNSServiceTxt::releaseKey(void) { - m_pcValue = new char[p_stLength + 1]; + if (m_pcKey) + { + delete[] m_pcKey; + m_pcKey = 0; + } + return true; } - return m_pcValue; -} - -/* - MDNSResponder::stcMDNSServiceTxt::setValue -*/ -bool MDNSResponder::stcMDNSServiceTxt::setValue(const char* p_pcValue, - size_t p_stLength) -{ - bool bResult = false; + /* + MDNSResponder::stcMDNSServiceTxt::allocValue + */ + char* MDNSResponder::stcMDNSServiceTxt::allocValue(size_t p_stLength) + { + releaseValue(); + if (p_stLength) + { + m_pcValue = new char[p_stLength + 1]; + } + return m_pcValue; + } - releaseValue(); - if (p_stLength) + /* + MDNSResponder::stcMDNSServiceTxt::setValue + */ + bool MDNSResponder::stcMDNSServiceTxt::setValue(const char* p_pcValue, size_t p_stLength) { - if (allocValue(p_stLength)) + bool bResult = false; + + releaseValue(); + if (p_stLength) + { + if (allocValue(p_stLength)) + { + strncpy(m_pcValue, p_pcValue, p_stLength); + m_pcValue[p_stLength] = 0; + bResult = true; + } + } + else // No value -> also OK { - strncpy(m_pcValue, p_pcValue, p_stLength); - m_pcValue[p_stLength] = 0; bResult = true; } + return bResult; } - else // No value -> also OK + + /* + MDNSResponder::stcMDNSServiceTxt::setValue + */ + bool MDNSResponder::stcMDNSServiceTxt::setValue(const char* p_pcValue) { - bResult = true; + return setValue(p_pcValue, (p_pcValue ? strlen(p_pcValue) : 0)); } - return bResult; -} - -/* - MDNSResponder::stcMDNSServiceTxt::setValue -*/ -bool MDNSResponder::stcMDNSServiceTxt::setValue(const char* p_pcValue) -{ - - return setValue(p_pcValue, (p_pcValue ? strlen(p_pcValue) : 0)); -} - -/* - MDNSResponder::stcMDNSServiceTxt::releaseValue -*/ -bool MDNSResponder::stcMDNSServiceTxt::releaseValue(void) -{ - if (m_pcValue) + /* + MDNSResponder::stcMDNSServiceTxt::releaseValue + */ + bool MDNSResponder::stcMDNSServiceTxt::releaseValue(void) { - delete[] m_pcValue; - m_pcValue = 0; + if (m_pcValue) + { + delete[] m_pcValue; + m_pcValue = 0; + } + return true; } - return true; -} - -/* - MDNSResponder::stcMDNSServiceTxt::set -*/ -bool MDNSResponder::stcMDNSServiceTxt::set(const char* p_pcKey, - const char* p_pcValue, - bool p_bTemp /*= false*/) -{ - - m_bTemp = p_bTemp; - return ((setKey(p_pcKey)) && - (setValue(p_pcValue))); -} - -/* - MDNSResponder::stcMDNSServiceTxt::update -*/ -bool MDNSResponder::stcMDNSServiceTxt::update(const char* p_pcValue) -{ - - return setValue(p_pcValue); -} - -/* - MDNSResponder::stcMDNSServiceTxt::length - - length of eg. 'c#=1' without any closing '\0' -*/ -size_t MDNSResponder::stcMDNSServiceTxt::length(void) const -{ - size_t stLength = 0; - if (m_pcKey) + /* + MDNSResponder::stcMDNSServiceTxt::set + */ + bool MDNSResponder::stcMDNSServiceTxt::set(const char* p_pcKey, const char* p_pcValue, + bool p_bTemp /*= false*/) { - stLength += strlen(m_pcKey); // Key - stLength += 1; // '=' - stLength += (m_pcValue ? strlen(m_pcValue) : 0); // Value + m_bTemp = p_bTemp; + return ((setKey(p_pcKey)) && (setValue(p_pcValue))); } - return stLength; -} - - -/** - MDNSResponder::stcMDNSServiceTxts - - A list of zero or more MDNS TXT items. - Dynamic TXT items can be removed by 'removeTempTxts'. - A TXT item can be looke up by its 'key' member. - Export as ';'-separated byte array is supported. - Export as 'length byte coded' byte array is supported. - Comparision ((all A TXT items in B and equal) AND (all B TXT items in A and equal)) is supported. - -*/ - -/* - MDNSResponder::stcMDNSServiceTxts::stcMDNSServiceTxts contructor -*/ -MDNSResponder::stcMDNSServiceTxts::stcMDNSServiceTxts(void) - : m_pTxts(0) -{ - -} - -/* - MDNSResponder::stcMDNSServiceTxts::stcMDNSServiceTxts copy-constructor -*/ -MDNSResponder::stcMDNSServiceTxts::stcMDNSServiceTxts(const stcMDNSServiceTxts& p_Other) - : m_pTxts(0) -{ - - operator=(p_Other); -} - -/* - MDNSResponder::stcMDNSServiceTxts::~stcMDNSServiceTxts destructor -*/ -MDNSResponder::stcMDNSServiceTxts::~stcMDNSServiceTxts(void) -{ - clear(); -} + /* + MDNSResponder::stcMDNSServiceTxt::update + */ + bool MDNSResponder::stcMDNSServiceTxt::update(const char* p_pcValue) + { + return setValue(p_pcValue); + } -/* - MDNSResponder::stcMDNSServiceTxts::operator= -*/ -MDNSResponder::stcMDNSServiceTxts& MDNSResponder::stcMDNSServiceTxts::operator=(const stcMDNSServiceTxts& p_Other) -{ + /* + MDNSResponder::stcMDNSServiceTxt::length - if (this != &p_Other) + length of eg. 'c#=1' without any closing '\0' + */ + size_t MDNSResponder::stcMDNSServiceTxt::length(void) const { - clear(); - - for (stcMDNSServiceTxt* pOtherTxt = p_Other.m_pTxts; pOtherTxt; pOtherTxt = pOtherTxt->m_pNext) + size_t stLength = 0; + if (m_pcKey) { - add(new stcMDNSServiceTxt(*pOtherTxt)); + stLength += strlen(m_pcKey); // Key + stLength += 1; // '=' + stLength += (m_pcValue ? strlen(m_pcValue) : 0); // Value } + return stLength; } - return *this; -} -/* - MDNSResponder::stcMDNSServiceTxts::clear -*/ -bool MDNSResponder::stcMDNSServiceTxts::clear(void) -{ + /** + MDNSResponder::stcMDNSServiceTxts - while (m_pTxts) - { - stcMDNSServiceTxt* pNext = m_pTxts->m_pNext; - delete m_pTxts; - m_pTxts = pNext; - } - return true; -} + A list of zero or more MDNS TXT items. + Dynamic TXT items can be removed by 'removeTempTxts'. + A TXT item can be looke up by its 'key' member. + Export as ';'-separated byte array is supported. + Export as 'length byte coded' byte array is supported. + Comparison ((all A TXT items in B and equal) AND (all B TXT items in A and equal)) is + supported. -/* - MDNSResponder::stcMDNSServiceTxts::add -*/ -bool MDNSResponder::stcMDNSServiceTxts::add(MDNSResponder::stcMDNSServiceTxt* p_pTxt) -{ + */ - bool bResult = false; + /* + MDNSResponder::stcMDNSServiceTxts::stcMDNSServiceTxts constructor + */ + MDNSResponder::stcMDNSServiceTxts::stcMDNSServiceTxts(void) : m_pTxts(0) { } - if (p_pTxt) + /* + MDNSResponder::stcMDNSServiceTxts::stcMDNSServiceTxts copy-constructor + */ + MDNSResponder::stcMDNSServiceTxts::stcMDNSServiceTxts(const stcMDNSServiceTxts& p_Other) : + m_pTxts(0) { - p_pTxt->m_pNext = m_pTxts; - m_pTxts = p_pTxt; - bResult = true; + operator=(p_Other); } - return bResult; -} - -/* - MDNSResponder::stcMDNSServiceTxts::remove -*/ -bool MDNSResponder::stcMDNSServiceTxts::remove(stcMDNSServiceTxt* p_pTxt) -{ - bool bResult = false; + /* + MDNSResponder::stcMDNSServiceTxts::~stcMDNSServiceTxts destructor + */ + MDNSResponder::stcMDNSServiceTxts::~stcMDNSServiceTxts(void) + { + clear(); + } - if (p_pTxt) + /* + MDNSResponder::stcMDNSServiceTxts::operator= + */ + MDNSResponder::stcMDNSServiceTxts& + MDNSResponder::stcMDNSServiceTxts::operator=(const stcMDNSServiceTxts& p_Other) { - stcMDNSServiceTxt* pPred = m_pTxts; - while ((pPred) && - (pPred->m_pNext != p_pTxt)) - { - pPred = pPred->m_pNext; - } - if (pPred) + if (this != &p_Other) { - pPred->m_pNext = p_pTxt->m_pNext; - delete p_pTxt; - bResult = true; + clear(); + + for (stcMDNSServiceTxt* pOtherTxt = p_Other.m_pTxts; pOtherTxt; + pOtherTxt = pOtherTxt->m_pNext) + { + add(new stcMDNSServiceTxt(*pOtherTxt)); + } } - else if (m_pTxts == p_pTxt) // No predecesor, but first item + return *this; + } + + /* + MDNSResponder::stcMDNSServiceTxts::clear + */ + bool MDNSResponder::stcMDNSServiceTxts::clear(void) + { + while (m_pTxts) { - m_pTxts = p_pTxt->m_pNext; - delete p_pTxt; - bResult = true; + stcMDNSServiceTxt* pNext = m_pTxts->m_pNext; + delete m_pTxts; + m_pTxts = pNext; } + return true; } - return bResult; -} - -/* - MDNSResponder::stcMDNSServiceTxts::removeTempTxts -*/ -bool MDNSResponder::stcMDNSServiceTxts::removeTempTxts(void) -{ - - bool bResult = true; - stcMDNSServiceTxt* pTxt = m_pTxts; - while ((bResult) && - (pTxt)) + /* + MDNSResponder::stcMDNSServiceTxts::add + */ + bool MDNSResponder::stcMDNSServiceTxts::add(MDNSResponder::stcMDNSServiceTxt* p_pTxt) { - stcMDNSServiceTxt* pNext = pTxt->m_pNext; - if (pTxt->m_bTemp) + bool bResult = false; + + if (p_pTxt) { - bResult = remove(pTxt); + p_pTxt->m_pNext = m_pTxts; + m_pTxts = p_pTxt; + bResult = true; } - pTxt = pNext; + return bResult; } - return bResult; -} -/* - MDNSResponder::stcMDNSServiceTxts::find -*/ -MDNSResponder::stcMDNSServiceTxt* MDNSResponder::stcMDNSServiceTxts::find(const char* p_pcKey) -{ + /* + MDNSResponder::stcMDNSServiceTxts::remove + */ + bool MDNSResponder::stcMDNSServiceTxts::remove(stcMDNSServiceTxt* p_pTxt) + { + bool bResult = false; - stcMDNSServiceTxt* pResult = 0; + if (p_pTxt) + { + stcMDNSServiceTxt* pPred = m_pTxts; + while ((pPred) && (pPred->m_pNext != p_pTxt)) + { + pPred = pPred->m_pNext; + } + if (pPred) + { + pPred->m_pNext = p_pTxt->m_pNext; + delete p_pTxt; + bResult = true; + } + else if (m_pTxts == p_pTxt) // No predecessor, but first item + { + m_pTxts = p_pTxt->m_pNext; + delete p_pTxt; + bResult = true; + } + } + return bResult; + } - for (stcMDNSServiceTxt* pTxt = m_pTxts; pTxt; pTxt = pTxt->m_pNext) + /* + MDNSResponder::stcMDNSServiceTxts::removeTempTxts + */ + bool MDNSResponder::stcMDNSServiceTxts::removeTempTxts(void) { - if ((p_pcKey) && - (0 == strcmp(pTxt->m_pcKey, p_pcKey))) + bool bResult = true; + + stcMDNSServiceTxt* pTxt = m_pTxts; + while ((bResult) && (pTxt)) { - pResult = pTxt; - break; + stcMDNSServiceTxt* pNext = pTxt->m_pNext; + if (pTxt->m_bTemp) + { + bResult = remove(pTxt); + } + pTxt = pNext; } + return bResult; } - return pResult; -} -/* - MDNSResponder::stcMDNSServiceTxts::find -*/ -const MDNSResponder::stcMDNSServiceTxt* MDNSResponder::stcMDNSServiceTxts::find(const char* p_pcKey) const -{ + /* + MDNSResponder::stcMDNSServiceTxts::find + */ + MDNSResponder::stcMDNSServiceTxt* MDNSResponder::stcMDNSServiceTxts::find(const char* p_pcKey) + { + stcMDNSServiceTxt* pResult = 0; - const stcMDNSServiceTxt* pResult = 0; + for (stcMDNSServiceTxt* pTxt = m_pTxts; pTxt; pTxt = pTxt->m_pNext) + { + if ((p_pcKey) && (0 == strcmp(pTxt->m_pcKey, p_pcKey))) + { + pResult = pTxt; + break; + } + } + return pResult; + } - for (const stcMDNSServiceTxt* pTxt = m_pTxts; pTxt; pTxt = pTxt->m_pNext) + /* + MDNSResponder::stcMDNSServiceTxts::find + */ + const MDNSResponder::stcMDNSServiceTxt* + MDNSResponder::stcMDNSServiceTxts::find(const char* p_pcKey) const { - if ((p_pcKey) && - (0 == strcmp(pTxt->m_pcKey, p_pcKey))) - { + const stcMDNSServiceTxt* pResult = 0; - pResult = pTxt; - break; + for (const stcMDNSServiceTxt* pTxt = m_pTxts; pTxt; pTxt = pTxt->m_pNext) + { + if ((p_pcKey) && (0 == strcmp(pTxt->m_pcKey, p_pcKey))) + { + pResult = pTxt; + break; + } } + return pResult; } - return pResult; -} -/* - MDNSResponder::stcMDNSServiceTxts::find -*/ -MDNSResponder::stcMDNSServiceTxt* MDNSResponder::stcMDNSServiceTxts::find(const stcMDNSServiceTxt* p_pTxt) -{ + /* + MDNSResponder::stcMDNSServiceTxts::find + */ + MDNSResponder::stcMDNSServiceTxt* + MDNSResponder::stcMDNSServiceTxts::find(const stcMDNSServiceTxt* p_pTxt) + { + stcMDNSServiceTxt* pResult = 0; - stcMDNSServiceTxt* pResult = 0; + for (stcMDNSServiceTxt* pTxt = m_pTxts; pTxt; pTxt = pTxt->m_pNext) + { + if (p_pTxt == pTxt) + { + pResult = pTxt; + break; + } + } + return pResult; + } - for (stcMDNSServiceTxt* pTxt = m_pTxts; pTxt; pTxt = pTxt->m_pNext) + /* + MDNSResponder::stcMDNSServiceTxts::length + */ + uint16_t MDNSResponder::stcMDNSServiceTxts::length(void) const { - if (p_pTxt == pTxt) + uint16_t u16Length = 0; + + stcMDNSServiceTxt* pTxt = m_pTxts; + while (pTxt) { - pResult = pTxt; - break; + u16Length += 1; // Length byte + u16Length += pTxt->length(); // Text + pTxt = pTxt->m_pNext; } + return u16Length; } - return pResult; -} - -/* - MDNSResponder::stcMDNSServiceTxts::length -*/ -uint16_t MDNSResponder::stcMDNSServiceTxts::length(void) const -{ - uint16_t u16Length = 0; + /* + MDNSResponder::stcMDNSServiceTxts::c_strLength - stcMDNSServiceTxt* pTxt = m_pTxts; - while (pTxt) + (incl. closing '\0'). Length bytes place is used for delimiting ';' and closing '\0' + */ + size_t MDNSResponder::stcMDNSServiceTxts::c_strLength(void) const { - u16Length += 1; // Length byte - u16Length += pTxt->length(); // Text - pTxt = pTxt->m_pNext; + return length(); } - return u16Length; -} - -/* - MDNSResponder::stcMDNSServiceTxts::c_strLength - - (incl. closing '\0'). Length bytes place is used for delimiting ';' and closing '\0' -*/ -size_t MDNSResponder::stcMDNSServiceTxts::c_strLength(void) const -{ - - return length(); -} - -/* - MDNSResponder::stcMDNSServiceTxts::c_str -*/ -bool MDNSResponder::stcMDNSServiceTxts::c_str(char* p_pcBuffer) -{ - - bool bResult = false; - if (p_pcBuffer) + /* + MDNSResponder::stcMDNSServiceTxts::c_str + */ + bool MDNSResponder::stcMDNSServiceTxts::c_str(char* p_pcBuffer) { - bResult = true; + bool bResult = false; - *p_pcBuffer = 0; - for (stcMDNSServiceTxt* pTxt = m_pTxts; ((bResult) && (pTxt)); pTxt = pTxt->m_pNext) + if (p_pcBuffer) { - size_t stLength; - if ((bResult = (0 != (stLength = (pTxt->m_pcKey ? strlen(pTxt->m_pcKey) : 0))))) + bResult = true; + + *p_pcBuffer = 0; + for (stcMDNSServiceTxt* pTxt = m_pTxts; ((bResult) && (pTxt)); pTxt = pTxt->m_pNext) { - if (pTxt != m_pTxts) - { - *p_pcBuffer++ = ';'; - } - strncpy(p_pcBuffer, pTxt->m_pcKey, stLength); p_pcBuffer[stLength] = 0; - p_pcBuffer += stLength; - *p_pcBuffer++ = '='; - if ((stLength = (pTxt->m_pcValue ? strlen(pTxt->m_pcValue) : 0))) + size_t stLength; + if ((bResult = (0 != (stLength = (pTxt->m_pcKey ? strlen(pTxt->m_pcKey) : 0))))) { - strncpy(p_pcBuffer, pTxt->m_pcValue, stLength); p_pcBuffer[stLength] = 0; + if (pTxt != m_pTxts) + { + *p_pcBuffer++ = ';'; + } + strncpy(p_pcBuffer, pTxt->m_pcKey, stLength); + p_pcBuffer[stLength] = 0; p_pcBuffer += stLength; + *p_pcBuffer++ = '='; + if ((stLength = (pTxt->m_pcValue ? strlen(pTxt->m_pcValue) : 0))) + { + strncpy(p_pcBuffer, pTxt->m_pcValue, stLength); + p_pcBuffer[stLength] = 0; + p_pcBuffer += stLength; + } } } + *p_pcBuffer++ = 0; } - *p_pcBuffer++ = 0; + return bResult; } - return bResult; -} - -/* - MDNSResponder::stcMDNSServiceTxts::bufferLength - - (incl. closing '\0'). -*/ -size_t MDNSResponder::stcMDNSServiceTxts::bufferLength(void) const -{ - - return (length() + 1); -} -/* - MDNSResponder::stcMDNSServiceTxts::toBuffer -*/ -bool MDNSResponder::stcMDNSServiceTxts::buffer(char* p_pcBuffer) -{ + /* + MDNSResponder::stcMDNSServiceTxts::bufferLength - bool bResult = false; + (incl. closing '\0'). + */ + size_t MDNSResponder::stcMDNSServiceTxts::bufferLength(void) const + { + return (length() + 1); + } - if (p_pcBuffer) + /* + MDNSResponder::stcMDNSServiceTxts::toBuffer + */ + bool MDNSResponder::stcMDNSServiceTxts::buffer(char* p_pcBuffer) { - bResult = true; + bool bResult = false; - *p_pcBuffer = 0; - for (stcMDNSServiceTxt* pTxt = m_pTxts; ((bResult) && (pTxt)); pTxt = pTxt->m_pNext) + if (p_pcBuffer) { - *(unsigned char*)p_pcBuffer++ = pTxt->length(); - size_t stLength; - if ((bResult = (0 != (stLength = (pTxt->m_pcKey ? strlen(pTxt->m_pcKey) : 0))))) + bResult = true; + + *p_pcBuffer = 0; + for (stcMDNSServiceTxt* pTxt = m_pTxts; ((bResult) && (pTxt)); pTxt = pTxt->m_pNext) { - memcpy(p_pcBuffer, pTxt->m_pcKey, stLength); - p_pcBuffer += stLength; - *p_pcBuffer++ = '='; - if ((stLength = (pTxt->m_pcValue ? strlen(pTxt->m_pcValue) : 0))) + *(unsigned char*)p_pcBuffer++ = pTxt->length(); + size_t stLength; + if ((bResult = (0 != (stLength = (pTxt->m_pcKey ? strlen(pTxt->m_pcKey) : 0))))) { - memcpy(p_pcBuffer, pTxt->m_pcValue, stLength); + memcpy(p_pcBuffer, pTxt->m_pcKey, stLength); p_pcBuffer += stLength; + *p_pcBuffer++ = '='; + if ((stLength = (pTxt->m_pcValue ? strlen(pTxt->m_pcValue) : 0))) + { + memcpy(p_pcBuffer, pTxt->m_pcValue, stLength); + p_pcBuffer += stLength; + } } } + *p_pcBuffer++ = 0; } - *p_pcBuffer++ = 0; + return bResult; } - return bResult; -} - -/* - MDNSResponder::stcMDNSServiceTxts::compare -*/ -bool MDNSResponder::stcMDNSServiceTxts::compare(const MDNSResponder::stcMDNSServiceTxts& p_Other) const -{ - - bool bResult = false; - if ((bResult = (length() == p_Other.length()))) + /* + MDNSResponder::stcMDNSServiceTxts::compare + */ + bool MDNSResponder::stcMDNSServiceTxts::compare( + const MDNSResponder::stcMDNSServiceTxts& p_Other) const { - // Compare A->B - for (const stcMDNSServiceTxt* pTxt = m_pTxts; ((bResult) && (pTxt)); pTxt = pTxt->m_pNext) - { - const stcMDNSServiceTxt* pOtherTxt = p_Other.find(pTxt->m_pcKey); - bResult = ((pOtherTxt) && - (pTxt->m_pcValue) && - (pOtherTxt->m_pcValue) && - (strlen(pTxt->m_pcValue) == strlen(pOtherTxt->m_pcValue)) && - (0 == strcmp(pTxt->m_pcValue, pOtherTxt->m_pcValue))); - } - // Compare B->A - for (const stcMDNSServiceTxt* pOtherTxt = p_Other.m_pTxts; ((bResult) && (pOtherTxt)); pOtherTxt = pOtherTxt->m_pNext) + bool bResult = false; + + if ((bResult = (length() == p_Other.length()))) { - const stcMDNSServiceTxt* pTxt = find(pOtherTxt->m_pcKey); - bResult = ((pTxt) && - (pOtherTxt->m_pcValue) && - (pTxt->m_pcValue) && - (strlen(pOtherTxt->m_pcValue) == strlen(pTxt->m_pcValue)) && - (0 == strcmp(pOtherTxt->m_pcValue, pTxt->m_pcValue))); + // Compare A->B + for (const stcMDNSServiceTxt* pTxt = m_pTxts; ((bResult) && (pTxt)); + pTxt = pTxt->m_pNext) + { + const stcMDNSServiceTxt* pOtherTxt = p_Other.find(pTxt->m_pcKey); + bResult = ((pOtherTxt) && (pTxt->m_pcValue) && (pOtherTxt->m_pcValue) + && (strlen(pTxt->m_pcValue) == strlen(pOtherTxt->m_pcValue)) + && (0 == strcmp(pTxt->m_pcValue, pOtherTxt->m_pcValue))); + } + // Compare B->A + for (const stcMDNSServiceTxt* pOtherTxt = p_Other.m_pTxts; ((bResult) && (pOtherTxt)); + pOtherTxt = pOtherTxt->m_pNext) + { + const stcMDNSServiceTxt* pTxt = find(pOtherTxt->m_pcKey); + bResult = ((pTxt) && (pOtherTxt->m_pcValue) && (pTxt->m_pcValue) + && (strlen(pOtherTxt->m_pcValue) == strlen(pTxt->m_pcValue)) + && (0 == strcmp(pOtherTxt->m_pcValue, pTxt->m_pcValue))); + } } + return bResult; } - return bResult; -} - -/* - MDNSResponder::stcMDNSServiceTxts::operator== -*/ -bool MDNSResponder::stcMDNSServiceTxts::operator==(const stcMDNSServiceTxts& p_Other) const -{ - return compare(p_Other); -} + /* + MDNSResponder::stcMDNSServiceTxts::operator== + */ + bool MDNSResponder::stcMDNSServiceTxts::operator==(const stcMDNSServiceTxts& p_Other) const + { + return compare(p_Other); + } -/* - MDNSResponder::stcMDNSServiceTxts::operator!= -*/ -bool MDNSResponder::stcMDNSServiceTxts::operator!=(const stcMDNSServiceTxts& p_Other) const -{ + /* + MDNSResponder::stcMDNSServiceTxts::operator!= + */ + bool MDNSResponder::stcMDNSServiceTxts::operator!=(const stcMDNSServiceTxts& p_Other) const + { + return !compare(p_Other); + } - return !compare(p_Other); -} + /** + MDNSResponder::stcMDNS_MsgHeader + A MDNS message header. -/** - MDNSResponder::stcMDNS_MsgHeader + */ - A MDNS message haeder. - -*/ - -/* - MDNSResponder::stcMDNS_MsgHeader::stcMDNS_MsgHeader -*/ -MDNSResponder::stcMDNS_MsgHeader::stcMDNS_MsgHeader(uint16_t p_u16ID /*= 0*/, - bool p_bQR /*= false*/, - unsigned char p_ucOpcode /*= 0*/, - bool p_bAA /*= false*/, - bool p_bTC /*= false*/, - bool p_bRD /*= false*/, - 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_u16ANCount(p_u16ANCount), - m_u16NSCount(p_u16NSCount), - m_u16ARCount(p_u16ARCount) -{ - -} - - -/** - MDNSResponder::stcMDNS_RRDomain - - A MDNS domain object. - The labels of the domain are stored (DNS-like encoded) in 'm_acName': - [length byte]varlength label[length byte]varlength label[0] - 'm_u16NameLength' stores the used length of 'm_acName'. - Dynamic label addition is supported. - Comparison is supported. - Export as byte array 'esp8266.local' is supported. - -*/ - -/* - MDNSResponder::stcMDNS_RRDomain::stcMDNS_RRDomain constructor -*/ -MDNSResponder::stcMDNS_RRDomain::stcMDNS_RRDomain(void) - : m_u16NameLength(0) -{ - - clear(); -} + /* + MDNSResponder::stcMDNS_MsgHeader::stcMDNS_MsgHeader + */ + MDNSResponder::stcMDNS_MsgHeader::stcMDNS_MsgHeader( + uint16_t p_u16ID /*= 0*/, bool p_bQR /*= false*/, unsigned char p_ucOpcode /*= 0*/, + bool p_bAA /*= false*/, bool p_bTC /*= false*/, bool p_bRD /*= false*/, + 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_u16ANCount(p_u16ANCount), m_u16NSCount(p_u16NSCount), m_u16ARCount(p_u16ARCount) + { + } -/* - MDNSResponder::stcMDNS_RRDomain::stcMDNS_RRDomain copy-constructor -*/ -MDNSResponder::stcMDNS_RRDomain::stcMDNS_RRDomain(const stcMDNS_RRDomain& p_Other) - : m_u16NameLength(0) -{ + /** + MDNSResponder::stcMDNS_RRDomain - operator=(p_Other); -} + A MDNS domain object. + The labels of the domain are stored (DNS-like encoded) in 'm_acName': + [length byte]varlength label[length byte]varlength label[0] + 'm_u16NameLength' stores the used length of 'm_acName'. + Dynamic label addition is supported. + Comparison is supported. + Export as byte array 'esp8266.local' is supported. -/* - MDNSResponder::stcMDNS_RRDomain::operator = -*/ -MDNSResponder::stcMDNS_RRDomain& MDNSResponder::stcMDNS_RRDomain::operator=(const stcMDNS_RRDomain& p_Other) -{ + */ - if (&p_Other != this) + /* + MDNSResponder::stcMDNS_RRDomain::stcMDNS_RRDomain constructor + */ + MDNSResponder::stcMDNS_RRDomain::stcMDNS_RRDomain(void) : m_u16NameLength(0) { - memcpy(m_acName, p_Other.m_acName, sizeof(m_acName)); - m_u16NameLength = p_Other.m_u16NameLength; + clear(); } - return *this; -} -/* - MDNSResponder::stcMDNS_RRDomain::clear -*/ -bool MDNSResponder::stcMDNS_RRDomain::clear(void) -{ - - memset(m_acName, 0, sizeof(m_acName)); - m_u16NameLength = 0; - return true; -} + /* + MDNSResponder::stcMDNS_RRDomain::stcMDNS_RRDomain copy-constructor + */ + MDNSResponder::stcMDNS_RRDomain::stcMDNS_RRDomain(const stcMDNS_RRDomain& p_Other) : + m_u16NameLength(0) + { + operator=(p_Other); + } -/* - MDNSResponder::stcMDNS_RRDomain::addLabel -*/ -bool MDNSResponder::stcMDNS_RRDomain::addLabel(const char* p_pcLabel, - bool p_bPrependUnderline /*= false*/) -{ + /* + MDNSResponder::stcMDNS_RRDomain::operator = + */ + MDNSResponder::stcMDNS_RRDomain& + MDNSResponder::stcMDNS_RRDomain::operator=(const stcMDNS_RRDomain& p_Other) + { + if (&p_Other != this) + { + memcpy(m_acName, p_Other.m_acName, sizeof(m_acName)); + m_u16NameLength = p_Other.m_u16NameLength; + } + return *this; + } - bool bResult = false; + /* + MDNSResponder::stcMDNS_RRDomain::clear + */ + bool MDNSResponder::stcMDNS_RRDomain::clear(void) + { + memset(m_acName, 0, sizeof(m_acName)); + m_u16NameLength = 0; + return true; + } - size_t stLength = (p_pcLabel - ? (strlen(p_pcLabel) + (p_bPrependUnderline ? 1 : 0)) - : 0); - if ((MDNS_DOMAIN_LABEL_MAXLENGTH >= stLength) && - (MDNS_DOMAIN_MAXLENGTH >= (m_u16NameLength + (1 + stLength)))) + /* + MDNSResponder::stcMDNS_RRDomain::addLabel + */ + bool MDNSResponder::stcMDNS_RRDomain::addLabel(const char* p_pcLabel, + bool p_bPrependUnderline /*= false*/) { - // Length byte - m_acName[m_u16NameLength] = (unsigned char)stLength; // Might be 0! - ++m_u16NameLength; - // Label - if (stLength) + bool bResult = false; + + size_t stLength = (p_pcLabel ? (strlen(p_pcLabel) + (p_bPrependUnderline ? 1 : 0)) : 0); + if ((MDNS_DOMAIN_LABEL_MAXLENGTH >= stLength) + && (MDNS_DOMAIN_MAXLENGTH >= (m_u16NameLength + (1 + stLength)))) { - if (p_bPrependUnderline) + // Length byte + m_acName[m_u16NameLength] = (unsigned char)stLength; // Might be 0! + ++m_u16NameLength; + // Label + if (stLength) { - m_acName[m_u16NameLength++] = '_'; - --stLength; + if (p_bPrependUnderline) + { + m_acName[m_u16NameLength++] = '_'; + --stLength; + } + strncpy(&(m_acName[m_u16NameLength]), p_pcLabel, stLength); + m_acName[m_u16NameLength + stLength] = 0; + m_u16NameLength += stLength; } - strncpy(&(m_acName[m_u16NameLength]), p_pcLabel, stLength); m_acName[m_u16NameLength + stLength] = 0; - m_u16NameLength += stLength; + bResult = true; } - bResult = true; + return bResult; } - return bResult; -} -/* - MDNSResponder::stcMDNS_RRDomain::compare -*/ -bool MDNSResponder::stcMDNS_RRDomain::compare(const stcMDNS_RRDomain& p_Other) const -{ - - bool bResult = false; - - if (m_u16NameLength == p_Other.m_u16NameLength) + /* + MDNSResponder::stcMDNS_RRDomain::compare + */ + bool MDNSResponder::stcMDNS_RRDomain::compare(const stcMDNS_RRDomain& p_Other) const { - const char* pT = m_acName; - const char* pO = p_Other.m_acName; - while ((pT) && - (pO) && - (*((unsigned char*)pT) == *((unsigned char*)pO)) && // Same length AND - (0 == strncasecmp((pT + 1), (pO + 1), *((unsigned char*)pT)))) // Same content + bool bResult = false; + + if (m_u16NameLength == p_Other.m_u16NameLength) { - if (*((unsigned char*)pT)) // Not 0 + const char* pT = m_acName; + const char* pO = p_Other.m_acName; + while ((pT) && (pO) && (*((unsigned char*)pT) == *((unsigned char*)pO)) + && // Same length AND + (0 == strncasecmp((pT + 1), (pO + 1), *((unsigned char*)pT)))) // Same content { - pT += (1 + * ((unsigned char*)pT)); // Shift by length byte and length - pO += (1 + * ((unsigned char*)pO)); - } - else // Is 0 -> Successfully reached the end - { - bResult = true; - break; + if (*((unsigned char*)pT)) // Not 0 + { + pT += (1 + *((unsigned char*)pT)); // Shift by length byte and length + pO += (1 + *((unsigned char*)pO)); + } + else // Is 0 -> Successfully reached the end + { + bResult = true; + break; + } } } + return bResult; } - return bResult; -} -/* - MDNSResponder::stcMDNS_RRDomain::operator == -*/ -bool MDNSResponder::stcMDNS_RRDomain::operator==(const stcMDNS_RRDomain& p_Other) const -{ - - return compare(p_Other); -} - -/* - MDNSResponder::stcMDNS_RRDomain::operator != -*/ -bool MDNSResponder::stcMDNS_RRDomain::operator!=(const stcMDNS_RRDomain& p_Other) const -{ - - return !compare(p_Other); -} - -/* - MDNSResponder::stcMDNS_RRDomain::operator > -*/ -bool MDNSResponder::stcMDNS_RRDomain::operator>(const stcMDNS_RRDomain& p_Other) const -{ - - // TODO: Check, if this is a good idea... - return !compare(p_Other); -} - -/* - MDNSResponder::stcMDNS_RRDomain::c_strLength -*/ -size_t MDNSResponder::stcMDNS_RRDomain::c_strLength(void) const -{ - - size_t stLength = 0; - - unsigned char* pucLabelLength = (unsigned char*)m_acName; - while (*pucLabelLength) + /* + MDNSResponder::stcMDNS_RRDomain::operator == + */ + bool MDNSResponder::stcMDNS_RRDomain::operator==(const stcMDNS_RRDomain& p_Other) const { - stLength += (*pucLabelLength + 1 /* +1 for '.' or '\0'*/); - pucLabelLength += (*pucLabelLength + 1); + return compare(p_Other); } - return stLength; -} -/* - MDNSResponder::stcMDNS_RRDomain::c_str -*/ -bool MDNSResponder::stcMDNS_RRDomain::c_str(char* p_pcBuffer) -{ + /* + MDNSResponder::stcMDNS_RRDomain::operator != + */ + bool MDNSResponder::stcMDNS_RRDomain::operator!=(const stcMDNS_RRDomain& p_Other) const + { + return !compare(p_Other); + } - bool bResult = false; + /* + MDNSResponder::stcMDNS_RRDomain::operator > + */ + bool MDNSResponder::stcMDNS_RRDomain::operator>(const stcMDNS_RRDomain& p_Other) const + { + // TODO: Check, if this is a good idea... + return !compare(p_Other); + } - if (p_pcBuffer) + /* + MDNSResponder::stcMDNS_RRDomain::c_strLength + */ + size_t MDNSResponder::stcMDNS_RRDomain::c_strLength(void) const { - *p_pcBuffer = 0; + size_t stLength = 0; + unsigned char* pucLabelLength = (unsigned char*)m_acName; while (*pucLabelLength) { - memcpy(p_pcBuffer, (const char*)(pucLabelLength + 1), *pucLabelLength); - p_pcBuffer += *pucLabelLength; + stLength += (*pucLabelLength + 1 /* +1 for '.' or '\0'*/); pucLabelLength += (*pucLabelLength + 1); - *p_pcBuffer++ = (*pucLabelLength ? '.' : '\0'); } - bResult = true; + return stLength; } - return bResult; -} - -/** - MDNSResponder::stcMDNS_RRAttributes - - A MDNS attributes object. - -*/ - -/* - MDNSResponder::stcMDNS_RRAttributes::stcMDNS_RRAttributes constructor -*/ -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) -{ + /* + MDNSResponder::stcMDNS_RRDomain::c_str + */ + bool MDNSResponder::stcMDNS_RRDomain::c_str(char* p_pcBuffer) + { + bool bResult = false; -} + if (p_pcBuffer) + { + *p_pcBuffer = 0; + unsigned char* pucLabelLength = (unsigned char*)m_acName; + while (*pucLabelLength) + { + memcpy(p_pcBuffer, (const char*)(pucLabelLength + 1), *pucLabelLength); + p_pcBuffer += *pucLabelLength; + pucLabelLength += (*pucLabelLength + 1); + *p_pcBuffer++ = (*pucLabelLength ? '.' : '\0'); + } + bResult = true; + } + return bResult; + } -/* - MDNSResponder::stcMDNS_RRAttributes::stcMDNS_RRAttributes copy-constructor -*/ -MDNSResponder::stcMDNS_RRAttributes::stcMDNS_RRAttributes(const MDNSResponder::stcMDNS_RRAttributes& p_Other) -{ + /** + MDNSResponder::stcMDNS_RRAttributes - operator=(p_Other); -} + A MDNS attributes object. -/* - MDNSResponder::stcMDNS_RRAttributes::operator = -*/ -MDNSResponder::stcMDNS_RRAttributes& MDNSResponder::stcMDNS_RRAttributes::operator=(const MDNSResponder::stcMDNS_RRAttributes& p_Other) -{ + */ - if (&p_Other != this) + /* + MDNSResponder::stcMDNS_RRAttributes::stcMDNS_RRAttributes constructor + */ + 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_Other.m_u16Type; - m_u16Class = p_Other.m_u16Class; } - return *this; -} - -/** - MDNSResponder::stcMDNS_RRHeader - - A MDNS record header (domain and attributes) object. - -*/ - -/* - MDNSResponder::stcMDNS_RRHeader::stcMDNS_RRHeader constructor -*/ -MDNSResponder::stcMDNS_RRHeader::stcMDNS_RRHeader(void) -{ - -} - -/* - MDNSResponder::stcMDNS_RRHeader::stcMDNS_RRHeader copy-constructor -*/ -MDNSResponder::stcMDNS_RRHeader::stcMDNS_RRHeader(const stcMDNS_RRHeader& p_Other) -{ - - operator=(p_Other); -} - -/* - MDNSResponder::stcMDNS_RRHeader::operator = -*/ -MDNSResponder::stcMDNS_RRHeader& MDNSResponder::stcMDNS_RRHeader::operator=(const MDNSResponder::stcMDNS_RRHeader& p_Other) -{ - - if (&p_Other != this) + /* + MDNSResponder::stcMDNS_RRAttributes::stcMDNS_RRAttributes copy-constructor + */ + MDNSResponder::stcMDNS_RRAttributes::stcMDNS_RRAttributes( + const MDNSResponder::stcMDNS_RRAttributes& p_Other) { - m_Domain = p_Other.m_Domain; - m_Attributes = p_Other.m_Attributes; + operator=(p_Other); } - return *this; -} - -/* - MDNSResponder::stcMDNS_RRHeader::clear -*/ -bool MDNSResponder::stcMDNS_RRHeader::clear(void) -{ - - m_Domain.clear(); - return true; -} - - -/** - MDNSResponder::stcMDNS_RRQuestion - - A MDNS question record object (header + question flags) - -*/ - -/* - MDNSResponder::stcMDNS_RRQuestion::stcMDNS_RRQuestion constructor -*/ -MDNSResponder::stcMDNS_RRQuestion::stcMDNS_RRQuestion(void) - : m_pNext(0), - m_bUnicast(false) -{ - -} - - -/** - MDNSResponder::stcMDNS_RRAnswer - - A MDNS answer record object (header + answer content). - This is a 'virtual' base class for all other MDNS answer classes. - -*/ - -/* - MDNSResponder::stcMDNS_RRAnswer::stcMDNS_RRAnswer constructor -*/ -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) -{ - - // Extract 'cache flush'-bit - m_bCacheFlush = (m_Header.m_Attributes.m_u16Class & 0x8000); - m_Header.m_Attributes.m_u16Class &= (~0x8000); -} - -/* - MDNSResponder::stcMDNS_RRAnswer::~stcMDNS_RRAnswer destructor -*/ -MDNSResponder::stcMDNS_RRAnswer::~stcMDNS_RRAnswer(void) -{ - -} - -/* - MDNSResponder::stcMDNS_RRAnswer::answerType -*/ -MDNSResponder::enuAnswerType MDNSResponder::stcMDNS_RRAnswer::answerType(void) const -{ - - return m_AnswerType; -} - -/* - MDNSResponder::stcMDNS_RRAnswer::clear -*/ -bool MDNSResponder::stcMDNS_RRAnswer::clear(void) -{ - - m_pNext = 0; - m_Header.clear(); - return true; -} - - -/** - MDNSResponder::stcMDNS_RRAnswerA - - A MDNS A answer object. - Extends the base class by an IP4 address member. - -*/ - -#ifdef MDNS_IP4_SUPPORT -/* - MDNSResponder::stcMDNS_RRAnswerA::stcMDNS_RRAnswerA constructor -*/ -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) -{ - -} - -/* - MDNSResponder::stcMDNS_RRAnswerA::stcMDNS_RRAnswerA destructor -*/ -MDNSResponder::stcMDNS_RRAnswerA::~stcMDNS_RRAnswerA(void) -{ - - clear(); -} - -/* - MDNSResponder::stcMDNS_RRAnswerA::clear -*/ -bool MDNSResponder::stcMDNS_RRAnswerA::clear(void) -{ - - m_IPAddress = IPAddress(0, 0, 0, 0); - return true; -} -#endif - - -/** - MDNSResponder::stcMDNS_RRAnswerPTR - - A MDNS PTR answer object. - Extends the base class by a MDNS domain member. - -*/ - -/* - MDNSResponder::stcMDNS_RRAnswerPTR::stcMDNS_RRAnswerPTR constructor -*/ -MDNSResponder::stcMDNS_RRAnswerPTR::stcMDNS_RRAnswerPTR(const MDNSResponder::stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL) - : stcMDNS_RRAnswer(AnswerType_PTR, p_Header, p_u32TTL) -{ - -} - -/* - MDNSResponder::stcMDNS_RRAnswerPTR::~stcMDNS_RRAnswerPTR destructor -*/ -MDNSResponder::stcMDNS_RRAnswerPTR::~stcMDNS_RRAnswerPTR(void) -{ - - clear(); -} - -/* - MDNSResponder::stcMDNS_RRAnswerPTR::clear -*/ -bool MDNSResponder::stcMDNS_RRAnswerPTR::clear(void) -{ - - m_PTRDomain.clear(); - return true; -} - - -/** - MDNSResponder::stcMDNS_RRAnswerTXT - - A MDNS TXT answer object. - Extends the base class by a MDNS TXT items list member. - -*/ - -/* - MDNSResponder::stcMDNS_RRAnswerTXT::stcMDNS_RRAnswerTXT constructor -*/ -MDNSResponder::stcMDNS_RRAnswerTXT::stcMDNS_RRAnswerTXT(const MDNSResponder::stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL) - : stcMDNS_RRAnswer(AnswerType_TXT, p_Header, p_u32TTL) -{ - -} - -/* - MDNSResponder::stcMDNS_RRAnswerTXT::~stcMDNS_RRAnswerTXT destructor -*/ -MDNSResponder::stcMDNS_RRAnswerTXT::~stcMDNS_RRAnswerTXT(void) -{ - - clear(); -} - -/* - MDNSResponder::stcMDNS_RRAnswerTXT::clear -*/ -bool MDNSResponder::stcMDNS_RRAnswerTXT::clear(void) -{ - - m_Txts.clear(); - return true; -} - - -/** - MDNSResponder::stcMDNS_RRAnswerAAAA - - A MDNS AAAA answer object. - (Should) extend the base class by an IP6 address member. - -*/ - -#ifdef MDNS_IP6_SUPPORT -/* - MDNSResponder::stcMDNS_RRAnswerAAAA::stcMDNS_RRAnswerAAAA constructor -*/ -MDNSResponder::stcMDNS_RRAnswerAAAA::stcMDNS_RRAnswerAAAA(const MDNSResponder::stcMDNS_RRHeader& p_Header, - uint32_t p_u32TTL) - : stcMDNS_RRAnswer(AnswerType_AAAA, p_Header, p_u32TTL) -{ - -} - -/* - MDNSResponder::stcMDNS_RRAnswerAAAA::~stcMDNS_RRAnswerAAAA destructor -*/ -MDNSResponder::stcMDNS_RRAnswerAAAA::~stcMDNS_RRAnswerAAAA(void) -{ - - clear(); -} - -/* - MDNSResponder::stcMDNS_RRAnswerAAAA::clear -*/ -bool MDNSResponder::stcMDNS_RRAnswerAAAA::clear(void) -{ - - return true; -} -#endif - - -/** - MDNSResponder::stcMDNS_RRAnswerSRV - - A MDNS SRV answer object. - Extends the base class by a port member. - -*/ - -/* - MDNSResponder::stcMDNS_RRAnswerSRV::stcMDNS_RRAnswerSRV constructor -*/ -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) -{ - -} - -/* - MDNSResponder::stcMDNS_RRAnswerSRV::~stcMDNS_RRAnswerSRV destructor -*/ -MDNSResponder::stcMDNS_RRAnswerSRV::~stcMDNS_RRAnswerSRV(void) -{ - - clear(); -} - -/* - MDNSResponder::stcMDNS_RRAnswerSRV::clear -*/ -bool MDNSResponder::stcMDNS_RRAnswerSRV::clear(void) -{ - - m_u16Priority = 0; - m_u16Weight = 0; - m_u16Port = 0; - m_SRVDomain.clear(); - return true; -} + /* + MDNSResponder::stcMDNS_RRAttributes::operator = + */ + MDNSResponder::stcMDNS_RRAttributes& MDNSResponder::stcMDNS_RRAttributes::operator=( + const MDNSResponder::stcMDNS_RRAttributes& p_Other) + { + if (&p_Other != this) + { + m_u16Type = p_Other.m_u16Type; + m_u16Class = p_Other.m_u16Class; + } + return *this; + } -/** - MDNSResponder::stcMDNS_RRAnswerGeneric + /** + MDNSResponder::stcMDNS_RRHeader - An unknown (generic) MDNS answer object. - Extends the base class by a RDATA buffer member. + A MDNS record header (domain and attributes) object. -*/ + */ -/* - MDNSResponder::stcMDNS_RRAnswerGeneric::stcMDNS_RRAnswerGeneric constructor -*/ -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) -{ + /* + MDNSResponder::stcMDNS_RRHeader::stcMDNS_RRHeader constructor + */ + MDNSResponder::stcMDNS_RRHeader::stcMDNS_RRHeader(void) { } -} - -/* - MDNSResponder::stcMDNS_RRAnswerGeneric::~stcMDNS_RRAnswerGeneric destructor -*/ -MDNSResponder::stcMDNS_RRAnswerGeneric::~stcMDNS_RRAnswerGeneric(void) -{ - - clear(); -} + /* + MDNSResponder::stcMDNS_RRHeader::stcMDNS_RRHeader copy-constructor + */ + MDNSResponder::stcMDNS_RRHeader::stcMDNS_RRHeader(const stcMDNS_RRHeader& p_Other) + { + operator=(p_Other); + } -/* - MDNSResponder::stcMDNS_RRAnswerGeneric::clear -*/ -bool MDNSResponder::stcMDNS_RRAnswerGeneric::clear(void) -{ + /* + MDNSResponder::stcMDNS_RRHeader::operator = + */ + MDNSResponder::stcMDNS_RRHeader& + MDNSResponder::stcMDNS_RRHeader::operator=(const MDNSResponder::stcMDNS_RRHeader& p_Other) + { + if (&p_Other != this) + { + m_Domain = p_Other.m_Domain; + m_Attributes = p_Other.m_Attributes; + } + return *this; + } - if (m_pu8RDData) + /* + MDNSResponder::stcMDNS_RRHeader::clear + */ + bool MDNSResponder::stcMDNS_RRHeader::clear(void) { - delete[] m_pu8RDData; - m_pu8RDData = 0; + m_Domain.clear(); + return true; } - m_u16RDLength = 0; - return true; -} + /** + MDNSResponder::stcMDNS_RRQuestion + A MDNS question record object (header + question flags) -/** - MDNSResponder::stcProbeInformation + */ - Probing status information for a host or service domain + /* + MDNSResponder::stcMDNS_RRQuestion::stcMDNS_RRQuestion constructor + */ + MDNSResponder::stcMDNS_RRQuestion::stcMDNS_RRQuestion(void) : m_pNext(0), m_bUnicast(false) { } -*/ + /** + MDNSResponder::stcMDNS_RRAnswer -/* - MDNSResponder::stcProbeInformation::stcProbeInformation constructor -*/ -MDNSResponder::stcProbeInformation::stcProbeInformation(void) - : m_ProbingStatus(ProbingStatus_WaitingForData), - m_u8SentCount(0), - m_Timeout(esp8266::polledTimeout::oneShotMs::neverExpires), - m_bConflict(false), - m_bTiebreakNeeded(false), - m_fnHostProbeResultCallback(0), - m_fnServiceProbeResultCallback(0) -{ -} + A MDNS answer record object (header + answer content). + This is a 'virtual' base class for all other MDNS answer classes. -/* - MDNSResponder::stcProbeInformation::clear -*/ -bool MDNSResponder::stcProbeInformation::clear(bool p_bClearUserdata /*= false*/) -{ + */ - m_ProbingStatus = ProbingStatus_WaitingForData; - m_u8SentCount = 0; - m_Timeout.resetToNeverExpires(); - m_bConflict = false; - m_bTiebreakNeeded = false; - if (p_bClearUserdata) + /* + MDNSResponder::stcMDNS_RRAnswer::stcMDNS_RRAnswer constructor + */ + 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_fnHostProbeResultCallback = 0; - m_fnServiceProbeResultCallback = 0; + // Extract 'cache flush'-bit + m_bCacheFlush = (m_Header.m_Attributes.m_u16Class & 0x8000); + m_Header.m_Attributes.m_u16Class &= (~0x8000); } - return true; -} -/** - MDNSResponder::stcMDNSService + /* + MDNSResponder::stcMDNS_RRAnswer::~stcMDNS_RRAnswer destructor + */ + MDNSResponder::stcMDNS_RRAnswer::~stcMDNS_RRAnswer(void) { } - A MDNS service object (to be announced by the MDNS responder) - The service instance may be '\0'; in this case the hostname is used - and the flag m_bAutoName is set. If the hostname changes, all 'auto- - named' services are renamed also. - m_u8Replymask is used while preparing a response to a MDNS query. It is - resetted in '_sendMDNSMessage' afterwards. -*/ - -/* - MDNSResponder::stcMDNSService::stcMDNSService constructor -*/ -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_u8ReplyMask(0), - m_fnTxtCallback(0) -{ - - setName(p_pcName); - setService(p_pcService); - setProtocol(p_pcProtocol); -} + /* + MDNSResponder::stcMDNS_RRAnswer::answerType + */ + MDNSResponder::enuAnswerType MDNSResponder::stcMDNS_RRAnswer::answerType(void) const + { + return m_AnswerType; + } -/* - MDNSResponder::stcMDNSService::~stcMDNSService destructor -*/ -MDNSResponder::stcMDNSService::~stcMDNSService(void) -{ + /* + MDNSResponder::stcMDNS_RRAnswer::clear + */ + bool MDNSResponder::stcMDNS_RRAnswer::clear(void) + { + m_pNext = 0; + m_Header.clear(); + return true; + } - releaseName(); - releaseService(); - releaseProtocol(); -} + /** + MDNSResponder::stcMDNS_RRAnswerA -/* - MDNSResponder::stcMDNSService::setName -*/ -bool MDNSResponder::stcMDNSService::setName(const char* p_pcName) -{ + A MDNS A answer object. + Extends the base class by an IP4 address member. - bool bResult = false; + */ - releaseName(); - size_t stLength = (p_pcName ? strlen(p_pcName) : 0); - if (stLength) +#ifdef MDNS_IP4_SUPPORT + /* + MDNSResponder::stcMDNS_RRAnswerA::stcMDNS_RRAnswerA constructor + */ + 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) { - if ((bResult = (0 != (m_pcName = new char[stLength + 1])))) - { - strncpy(m_pcName, p_pcName, stLength); - m_pcName[stLength] = 0; - } } - else + + /* + MDNSResponder::stcMDNS_RRAnswerA::stcMDNS_RRAnswerA destructor + */ + MDNSResponder::stcMDNS_RRAnswerA::~stcMDNS_RRAnswerA(void) { - bResult = true; + clear(); } - return bResult; -} - -/* - MDNSResponder::stcMDNSService::releaseName -*/ -bool MDNSResponder::stcMDNSService::releaseName(void) -{ - if (m_pcName) + /* + MDNSResponder::stcMDNS_RRAnswerA::clear + */ + bool MDNSResponder::stcMDNS_RRAnswerA::clear(void) { - delete[] m_pcName; - m_pcName = 0; + m_IPAddress = IPAddress(0, 0, 0, 0); + return true; } - return true; -} - -/* - MDNSResponder::stcMDNSService::setService -*/ -bool MDNSResponder::stcMDNSService::setService(const char* p_pcService) -{ +#endif - bool bResult = false; + /** + MDNSResponder::stcMDNS_RRAnswerPTR - releaseService(); - size_t stLength = (p_pcService ? strlen(p_pcService) : 0); - if (stLength) + A MDNS PTR answer object. + Extends the base class by a MDNS domain member. + + */ + + /* + MDNSResponder::stcMDNS_RRAnswerPTR::stcMDNS_RRAnswerPTR constructor + */ + MDNSResponder::stcMDNS_RRAnswerPTR::stcMDNS_RRAnswerPTR( + const MDNSResponder::stcMDNS_RRHeader& p_Header, uint32_t p_u32TTL) : + stcMDNS_RRAnswer(AnswerType_PTR, p_Header, p_u32TTL) { - if ((bResult = (0 != (m_pcService = new char[stLength + 1])))) - { - strncpy(m_pcService, p_pcService, stLength); - m_pcService[stLength] = 0; - } } - else + + /* + MDNSResponder::stcMDNS_RRAnswerPTR::~stcMDNS_RRAnswerPTR destructor + */ + MDNSResponder::stcMDNS_RRAnswerPTR::~stcMDNS_RRAnswerPTR(void) { - bResult = true; + clear(); } - return bResult; -} - -/* - MDNSResponder::stcMDNSService::releaseService -*/ -bool MDNSResponder::stcMDNSService::releaseService(void) -{ - if (m_pcService) + /* + MDNSResponder::stcMDNS_RRAnswerPTR::clear + */ + bool MDNSResponder::stcMDNS_RRAnswerPTR::clear(void) { - delete[] m_pcService; - m_pcService = 0; + m_PTRDomain.clear(); + return true; } - return true; -} -/* - MDNSResponder::stcMDNSService::setProtocol -*/ -bool MDNSResponder::stcMDNSService::setProtocol(const char* p_pcProtocol) -{ + /** + MDNSResponder::stcMDNS_RRAnswerTXT + + A MDNS TXT answer object. + Extends the base class by a MDNS TXT items list member. - bool bResult = false; + */ - releaseProtocol(); - size_t stLength = (p_pcProtocol ? strlen(p_pcProtocol) : 0); - if (stLength) + /* + MDNSResponder::stcMDNS_RRAnswerTXT::stcMDNS_RRAnswerTXT constructor + */ + MDNSResponder::stcMDNS_RRAnswerTXT::stcMDNS_RRAnswerTXT( + const MDNSResponder::stcMDNS_RRHeader& p_Header, uint32_t p_u32TTL) : + stcMDNS_RRAnswer(AnswerType_TXT, p_Header, p_u32TTL) { - if ((bResult = (0 != (m_pcProtocol = new char[stLength + 1])))) - { - strncpy(m_pcProtocol, p_pcProtocol, stLength); - m_pcProtocol[stLength] = 0; - } } - else + + /* + MDNSResponder::stcMDNS_RRAnswerTXT::~stcMDNS_RRAnswerTXT destructor + */ + MDNSResponder::stcMDNS_RRAnswerTXT::~stcMDNS_RRAnswerTXT(void) { - bResult = true; + clear(); } - return bResult; -} - -/* - MDNSResponder::stcMDNSService::releaseProtocol -*/ -bool MDNSResponder::stcMDNSService::releaseProtocol(void) -{ - if (m_pcProtocol) + /* + MDNSResponder::stcMDNS_RRAnswerTXT::clear + */ + bool MDNSResponder::stcMDNS_RRAnswerTXT::clear(void) { - delete[] m_pcProtocol; - m_pcProtocol = 0; + m_Txts.clear(); + return true; } - return true; -} + /** + MDNSResponder::stcMDNS_RRAnswerAAAA -/** - MDNSResponder::stcMDNSServiceQuery + A MDNS AAAA answer object. + (Should) extend the base class by an IP6 address member. - A MDNS service query object. - Service queries may be static or dynamic. - As the static service query is processed in the blocking function 'queryService', - only one static service service may exist. The processing of the answers is done - on the WiFi-stack side of the ESP stack structure (via 'UDPContext.onRx(_update)'). - -*/ + */ -/** - MDNSResponder::stcMDNSServiceQuery::stcAnswer - - One answer for a service query. - Every answer must contain - - a service instance entry (pivot), - and may contain - - a host domain, - - a port - - an IP4 address - (- an IP6 address) - - a MDNS TXTs - The existance of a component is flaged in 'm_u32ContentFlags'. - For every answer component a TTL value is maintained. - Answer objects can be connected to a linked list. - - For the host domain, service domain and TXTs components, a char array - representation can be retrieved (which is created on demand). +#ifdef MDNS_IP6_SUPPORT + /* + MDNSResponder::stcMDNS_RRAnswerAAAA::stcMDNS_RRAnswerAAAA constructor + */ + MDNSResponder::stcMDNS_RRAnswerAAAA::stcMDNS_RRAnswerAAAA( + const MDNSResponder::stcMDNS_RRHeader& p_Header, uint32_t p_u32TTL) : + stcMDNS_RRAnswer(AnswerType_AAAA, p_Header, p_u32TTL) + { + } -*/ + /* + MDNSResponder::stcMDNS_RRAnswerAAAA::~stcMDNS_RRAnswerAAAA destructor + */ + MDNSResponder::stcMDNS_RRAnswerAAAA::~stcMDNS_RRAnswerAAAA(void) + { + clear(); + } -/** - MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL + /* + MDNSResponder::stcMDNS_RRAnswerAAAA::clear + */ + bool MDNSResponder::stcMDNS_RRAnswerAAAA::clear(void) + { + return true; + } +#endif - The TTL (Time-To-Live) for an specific answer content. - The 80% and outdated states are calculated based on the current time (millis) - and the 'set' time (also millis). - If the answer is scheduled for an update, the corresponding flag should be set. + /** + MDNSResponder::stcMDNS_RRAnswerSRV - / + A MDNS SRV answer object. + Extends the base class by a port member. - / * - MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL constructor - / - MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL(uint32_t p_u32TTL / *= 0* /) - : m_bUpdateScheduled(false) { + */ - set(p_u32TTL * 1000); + /* + MDNSResponder::stcMDNS_RRAnswerSRV::stcMDNS_RRAnswerSRV constructor + */ + 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) + { } - / * - MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set - / - bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set(uint32_t p_u32TTL) { - - m_TTLTimeFlag.restart(p_u32TTL * 1000); - m_bUpdateScheduled = false; - - return true; + /* + MDNSResponder::stcMDNS_RRAnswerSRV::~stcMDNS_RRAnswerSRV destructor + */ + MDNSResponder::stcMDNS_RRAnswerSRV::~stcMDNS_RRAnswerSRV(void) + { + clear(); } - / * - MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::has80Percent - / - bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::has80Percent(void) const { - - return ((m_TTLTimeFlag.getTimeout()) && - (!m_bUpdateScheduled) && - (m_TTLTimeFlag.hypotheticalTimeout((m_TTLTimeFlag.getTimeout() * 800) / 1000))); + /* + MDNSResponder::stcMDNS_RRAnswerSRV::clear + */ + bool MDNSResponder::stcMDNS_RRAnswerSRV::clear(void) + { + m_u16Priority = 0; + m_u16Weight = 0; + m_u16Port = 0; + m_SRVDomain.clear(); + return true; } - / * - MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::isOutdated - / - bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::isOutdated(void) const { + /** + MDNSResponder::stcMDNS_RRAnswerGeneric - return ((m_TTLTimeFlag.getTimeout()) && - (m_TTLTimeFlag.flagged())); - }*/ + An unknown (generic) MDNS answer object. + Extends the base class by a RDATA buffer member. + */ -/** - MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL + /* + MDNSResponder::stcMDNS_RRAnswerGeneric::stcMDNS_RRAnswerGeneric constructor + */ + 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) + { + } - The TTL (Time-To-Live) for an specific answer content. - The 80% and outdated states are calculated based on the current time (millis) - and the 'set' time (also millis). - If the answer is scheduled for an update, the corresponding flag should be set. + /* + MDNSResponder::stcMDNS_RRAnswerGeneric::~stcMDNS_RRAnswerGeneric destructor + */ + MDNSResponder::stcMDNS_RRAnswerGeneric::~stcMDNS_RRAnswerGeneric(void) + { + clear(); + } -*/ + /* + MDNSResponder::stcMDNS_RRAnswerGeneric::clear + */ + bool MDNSResponder::stcMDNS_RRAnswerGeneric::clear(void) + { + if (m_pu8RDData) + { + delete[] m_pu8RDData; + m_pu8RDData = 0; + } + m_u16RDLength = 0; -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL constructor -*/ -MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL(void) - : m_u32TTL(0), - m_TTLTimeout(esp8266::polledTimeout::oneShotMs::neverExpires), - m_timeoutLevel(TIMEOUTLEVEL_UNSET) -{ + return true; + } -} + /** + MDNSResponder::stcProbeInformation -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set -*/ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set(uint32_t p_u32TTL) -{ + Probing status information for a host or service domain + + */ - m_u32TTL = p_u32TTL; - if (m_u32TTL) + /* + MDNSResponder::stcProbeInformation::stcProbeInformation constructor + */ + MDNSResponder::stcProbeInformation::stcProbeInformation(void) : + m_ProbingStatus(ProbingStatus_WaitingForData), m_u8SentCount(0), + m_Timeout(esp8266::polledTimeout::oneShotMs::neverExpires), m_bConflict(false), + m_bTiebreakNeeded(false), m_fnHostProbeResultCallback(0), m_fnServiceProbeResultCallback(0) { - m_timeoutLevel = TIMEOUTLEVEL_BASE; // Set to 80% - m_TTLTimeout.reset(timeout()); } - else + + /* + MDNSResponder::stcProbeInformation::clear + */ + bool MDNSResponder::stcProbeInformation::clear(bool p_bClearUserdata /*= false*/) { - m_timeoutLevel = TIMEOUTLEVEL_UNSET; // undef - m_TTLTimeout.resetToNeverExpires(); + m_ProbingStatus = ProbingStatus_WaitingForData; + m_u8SentCount = 0; + m_Timeout.resetToNeverExpires(); + m_bConflict = false; + m_bTiebreakNeeded = false; + if (p_bClearUserdata) + { + m_fnHostProbeResultCallback = 0; + m_fnServiceProbeResultCallback = 0; + } + return true; } - return true; -} -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::flagged -*/ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::flagged(void) -{ + /** + MDNSResponder::stcMDNSService - return ((m_u32TTL) && - (TIMEOUTLEVEL_UNSET != m_timeoutLevel) && - (m_TTLTimeout.expired())); -} + A MDNS service object (to be announced by the MDNS responder) + The service instance may be '\0'; in this case the hostname is used + and the flag m_bAutoName is set. If the hostname changes, all 'auto- + named' services are renamed also. + m_u8Replymask is used while preparing a response to a MDNS query. It is + reset in '_sendMDNSMessage' afterwards. + */ -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::restart -*/ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::restart(void) -{ + /* + MDNSResponder::stcMDNSService::stcMDNSService constructor + */ + 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_u8ReplyMask(0), m_fnTxtCallback(0) + { + setName(p_pcName); + setService(p_pcService); + setProtocol(p_pcProtocol); + } - bool bResult = true; + /* + MDNSResponder::stcMDNSService::~stcMDNSService destructor + */ + MDNSResponder::stcMDNSService::~stcMDNSService(void) + { + releaseName(); + releaseService(); + releaseProtocol(); + } - if ((TIMEOUTLEVEL_BASE <= m_timeoutLevel) && // >= 80% AND - (TIMEOUTLEVEL_FINAL > m_timeoutLevel)) // < 100% + /* + MDNSResponder::stcMDNSService::setName + */ + bool MDNSResponder::stcMDNSService::setName(const char* p_pcName) { + bool bResult = false; - m_timeoutLevel += TIMEOUTLEVEL_INTERVAL; // increment by 5% - m_TTLTimeout.reset(timeout()); + releaseName(); + size_t stLength = (p_pcName ? strlen(p_pcName) : 0); + if (stLength) + { + if ((bResult = (0 != (m_pcName = new char[stLength + 1])))) + { + strncpy(m_pcName, p_pcName, stLength); + m_pcName[stLength] = 0; + } + } + else + { + bResult = true; + } + return bResult; } - else + + /* + MDNSResponder::stcMDNSService::releaseName + */ + bool MDNSResponder::stcMDNSService::releaseName(void) { - bResult = false; - m_TTLTimeout.resetToNeverExpires(); - m_timeoutLevel = TIMEOUTLEVEL_UNSET; + if (m_pcName) + { + delete[] m_pcName; + m_pcName = 0; + } + return true; } - return bResult; -} - -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::prepareDeletion -*/ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::prepareDeletion(void) -{ - m_timeoutLevel = TIMEOUTLEVEL_FINAL; - m_TTLTimeout.reset(1 * 1000); // See RFC 6762, 10.1 - - return true; -} + /* + MDNSResponder::stcMDNSService::setService + */ + bool MDNSResponder::stcMDNSService::setService(const char* p_pcService) + { + bool bResult = false; -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::finalTimeoutLevel -*/ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::finalTimeoutLevel(void) const -{ + releaseService(); + size_t stLength = (p_pcService ? strlen(p_pcService) : 0); + if (stLength) + { + if ((bResult = (0 != (m_pcService = new char[stLength + 1])))) + { + strncpy(m_pcService, p_pcService, stLength); + m_pcService[stLength] = 0; + } + } + else + { + bResult = true; + } + return bResult; + } - return (TIMEOUTLEVEL_FINAL == m_timeoutLevel); -} + /* + MDNSResponder::stcMDNSService::releaseService + */ + bool MDNSResponder::stcMDNSService::releaseService(void) + { + if (m_pcService) + { + delete[] m_pcService; + m_pcService = 0; + } + return true; + } -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::timeout -*/ -unsigned long MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::timeout(void) const -{ + /* + MDNSResponder::stcMDNSService::setProtocol + */ + bool MDNSResponder::stcMDNSService::setProtocol(const char* p_pcProtocol) + { + bool bResult = false; - uint32_t u32Timeout = esp8266::polledTimeout::oneShotMs::neverExpires; + releaseProtocol(); + size_t stLength = (p_pcProtocol ? strlen(p_pcProtocol) : 0); + if (stLength) + { + if ((bResult = (0 != (m_pcProtocol = new char[stLength + 1])))) + { + strncpy(m_pcProtocol, p_pcProtocol, stLength); + m_pcProtocol[stLength] = 0; + } + } + else + { + bResult = true; + } + return bResult; + } - if (TIMEOUTLEVEL_BASE == m_timeoutLevel) // 80% + /* + MDNSResponder::stcMDNSService::releaseProtocol + */ + bool MDNSResponder::stcMDNSService::releaseProtocol(void) { - u32Timeout = (m_u32TTL * 800); // to milliseconds + if (m_pcProtocol) + { + delete[] m_pcProtocol; + m_pcProtocol = 0; + } + return true; } - else if ((TIMEOUTLEVEL_BASE < m_timeoutLevel) && // >80% AND - (TIMEOUTLEVEL_FINAL >= m_timeoutLevel)) // <= 100% - { - u32Timeout = (m_u32TTL * 50); - } // else: invalid - return u32Timeout; -} + /** + MDNSResponder::stcMDNSServiceQuery + A MDNS service query object. + Service queries may be static or dynamic. + As the static service query is processed in the blocking function 'queryService', + only one static service service may exist. The processing of the answers is done + on the WiFi-stack side of the ESP stack structure (via 'UDPContext.onRx(_update)'). -#ifdef MDNS_IP4_SUPPORT -/** - MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address + */ -*/ + /** + MDNSResponder::stcMDNSServiceQuery::stcAnswer -/* - 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) -{ + One answer for a service query. + Every answer must contain + - a service instance entry (pivot), + and may contain + - a host domain, + - a port + - an IP4 address + (- an IP6 address) + - a MDNS TXTs + The existence of a component is flagged in 'm_u32ContentFlags'. + For every answer component a TTL value is maintained. + Answer objects can be connected to a linked list. - m_TTL.set(p_u32TTL); -} -#endif + For the host domain, service domain and TXTs components, a char array + representation can be retrieved (which is created on demand). + */ -/** - MDNSResponder::stcMDNSServiceQuery::stcAnswer -*/ + /** + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcAnswer constructor -*/ -MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcAnswer(void) - : m_pNext(0), - m_pcServiceDomain(0), - m_pcHostDomain(0), - m_u16Port(0), - m_pcTxts(0), -#ifdef MDNS_IP4_SUPPORT - m_pIP4Addresses(0), -#endif -#ifdef MDNS_IP6_SUPPORT - m_pIP6Addresses(0), -#endif - m_u32ContentFlags(0) -{ -} + The TTL (Time-To-Live) for an specific answer content. + The 80% and outdated states are calculated based on the current time (millis) + and the 'set' time (also millis). + If the answer is scheduled for an update, the corresponding flag should be set. -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::~stcAnswer destructor -*/ -MDNSResponder::stcMDNSServiceQuery::stcAnswer::~stcAnswer(void) -{ + / - clear(); -} + / * + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL constructor + / + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL(uint32_t p_u32TTL / *= 0* /) + : m_bUpdateScheduled(false) { -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::clear -*/ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::clear(void) -{ + set(p_u32TTL * 1000); + } - return ((releaseTxts()) && -#ifdef MDNS_IP4_SUPPORT - (releaseIP4Addresses()) && -#endif -#ifdef MDNS_IP6_SUPPORT - (releaseIP6Addresses()) -#endif - (releaseHostDomain()) && - (releaseServiceDomain())); -} + / * + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set + / + bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set(uint32_t p_u32TTL) { -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocServiceDomain + m_TTLTimeFlag.restart(p_u32TTL * 1000); + m_bUpdateScheduled = false; - Alloc memory for the char array representation of the service domain. + return true; + } -*/ -char* MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocServiceDomain(size_t p_stLength) -{ + / * + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::has80Percent + / + bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::has80Percent(void) const { - releaseServiceDomain(); - if (p_stLength) - { - m_pcServiceDomain = new char[p_stLength]; - } - return m_pcServiceDomain; -} + return ((m_TTLTimeFlag.getTimeout()) && + (!m_bUpdateScheduled) && + (m_TTLTimeFlag.hypotheticalTimeout((m_TTLTimeFlag.getTimeout() * 800) / 1000))); + } -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseServiceDomain -*/ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseServiceDomain(void) -{ + / * + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::isOutdated + / + bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::isOutdated(void) const { - if (m_pcServiceDomain) - { - delete[] m_pcServiceDomain; - m_pcServiceDomain = 0; - } - return true; -} + return ((m_TTLTimeFlag.getTimeout()) && + (m_TTLTimeFlag.flagged())); + }*/ -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocHostDomain + /** + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL - Alloc memory for the char array representation of the host domain. + The TTL (Time-To-Live) for an specific answer content. + The 80% and outdated states are calculated based on the current time (millis) + and the 'set' time (also millis). + If the answer is scheduled for an update, the corresponding flag should be set. -*/ -char* MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocHostDomain(size_t p_stLength) -{ + */ - releaseHostDomain(); - if (p_stLength) + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL constructor + */ + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::stcTTL(void) : + m_u32TTL(0), m_TTLTimeout(esp8266::polledTimeout::oneShotMs::neverExpires), + m_timeoutLevel(TIMEOUTLEVEL_UNSET) { - m_pcHostDomain = new char[p_stLength]; } - return m_pcHostDomain; -} - -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseHostDomain -*/ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseHostDomain(void) -{ - if (m_pcHostDomain) + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set + */ + bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::set(uint32_t p_u32TTL) { - delete[] m_pcHostDomain; - m_pcHostDomain = 0; + m_u32TTL = p_u32TTL; + if (m_u32TTL) + { + m_timeoutLevel = TIMEOUTLEVEL_BASE; // Set to 80% + m_TTLTimeout.reset(timeout()); + } + else + { + m_timeoutLevel = TIMEOUTLEVEL_UNSET; // undef + m_TTLTimeout.resetToNeverExpires(); + } + return true; } - return true; -} - -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocTxts - Alloc memory for the char array representation of the TXT items. - -*/ -char* MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocTxts(size_t p_stLength) -{ - - releaseTxts(); - if (p_stLength) + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::flagged + */ + bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::flagged(void) { - m_pcTxts = new char[p_stLength]; + return ((m_u32TTL) && (TIMEOUTLEVEL_UNSET != m_timeoutLevel) && (m_TTLTimeout.expired())); } - return m_pcTxts; -} - -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseTxts -*/ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseTxts(void) -{ - if (m_pcTxts) + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::restart + */ + bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::restart(void) { - delete[] m_pcTxts; - m_pcTxts = 0; - } - return true; -} - -#ifdef MDNS_IP4_SUPPORT -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseIP4Addresses -*/ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseIP4Addresses(void) -{ + bool bResult = true; - while (m_pIP4Addresses) - { - stcIP4Address* pNext = m_pIP4Addresses->m_pNext; - delete m_pIP4Addresses; - m_pIP4Addresses = pNext; + if ((TIMEOUTLEVEL_BASE <= m_timeoutLevel) && // >= 80% AND + (TIMEOUTLEVEL_FINAL > m_timeoutLevel)) // < 100% + { + m_timeoutLevel += TIMEOUTLEVEL_INTERVAL; // increment by 5% + m_TTLTimeout.reset(timeout()); + } + else + { + bResult = false; + m_TTLTimeout.resetToNeverExpires(); + m_timeoutLevel = TIMEOUTLEVEL_UNSET; + } + return bResult; } - return true; -} -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::addIP4Address -*/ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::addIP4Address(MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* p_pIP4Address) -{ + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::prepareDeletion + */ + bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::prepareDeletion(void) + { + m_timeoutLevel = TIMEOUTLEVEL_FINAL; + m_TTLTimeout.reset(1 * 1000); // See RFC 6762, 10.1 - bool bResult = false; + return true; + } - if (p_pIP4Address) + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::finalTimeoutLevel + */ + bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::finalTimeoutLevel(void) const { - p_pIP4Address->m_pNext = m_pIP4Addresses; - m_pIP4Addresses = p_pIP4Address; - bResult = true; + return (TIMEOUTLEVEL_FINAL == m_timeoutLevel); } - return bResult; -} -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::removeIP4Address -*/ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::removeIP4Address(MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* p_pIP4Address) -{ - - bool bResult = false; - - if (p_pIP4Address) + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::timeout + */ + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::timeoutBase::timeType + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::timeout(void) const { - stcIP4Address* pPred = m_pIP4Addresses; - while ((pPred) && - (pPred->m_pNext != p_pIP4Address)) - { - pPred = pPred->m_pNext; - } - if (pPred) + if (TIMEOUTLEVEL_BASE == m_timeoutLevel) // 80% { - pPred->m_pNext = p_pIP4Address->m_pNext; - delete p_pIP4Address; - bResult = true; + return (m_u32TTL * 800L); // to milliseconds } - else if (m_pIP4Addresses == p_pIP4Address) // No predecesor, but first item + else if ((TIMEOUTLEVEL_BASE < m_timeoutLevel) && // >80% AND + (TIMEOUTLEVEL_FINAL >= m_timeoutLevel)) // <= 100% { - m_pIP4Addresses = p_pIP4Address->m_pNext; - delete p_pIP4Address; - bResult = true; - } + return (m_u32TTL * 50L); + } // else: invalid + return MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcTTL::timeoutBase::neverExpires; } - return bResult; -} - -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address (const) -*/ -const MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address(const IPAddress& p_IPAddress) const -{ - return (stcIP4Address*)(((const stcAnswer*)this)->findIP4Address(p_IPAddress)); -} +#ifdef MDNS_IP4_SUPPORT + /** + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address -*/ -MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address(const IPAddress& p_IPAddress) -{ + */ - stcIP4Address* pIP4Address = m_pIP4Addresses; - while (pIP4Address) + /* + 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) { - if (pIP4Address->m_IPAddress == p_IPAddress) - { - break; - } - pIP4Address = pIP4Address->m_pNext; + m_TTL.set(p_u32TTL); } - return pIP4Address; -} - -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressCount -*/ -uint32_t MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressCount(void) const -{ +#endif - uint32_t u32Count = 0; + /** + MDNSResponder::stcMDNSServiceQuery::stcAnswer + */ - stcIP4Address* pIP4Address = m_pIP4Addresses; - while (pIP4Address) + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcAnswer constructor + */ + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcAnswer(void) : + m_pNext(0), m_pcServiceDomain(0), m_pcHostDomain(0), m_u16Port(0), m_pcTxts(0), +#ifdef MDNS_IP4_SUPPORT + m_pIP4Addresses(0), +#endif +#ifdef MDNS_IP6_SUPPORT + m_pIP6Addresses(0), +#endif + m_u32ContentFlags(0) { - ++u32Count; - pIP4Address = pIP4Address->m_pNext; } - return u32Count; -} -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressAtIndex -*/ -MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressAtIndex(uint32_t p_u32Index) -{ + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::~stcAnswer destructor + */ + MDNSResponder::stcMDNSServiceQuery::stcAnswer::~stcAnswer(void) + { + clear(); + } - return (stcIP4Address*)(((const stcAnswer*)this)->IP4AddressAtIndex(p_u32Index)); -} + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::clear + */ + bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::clear(void) + { + return ((releaseTxts()) && +#ifdef MDNS_IP4_SUPPORT + (releaseIP4Addresses()) && +#endif +#ifdef MDNS_IP6_SUPPORT + (releaseIP6Addresses()) +#endif + (releaseHostDomain()) + && (releaseServiceDomain())); + } -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressAtIndex (const) -*/ -const MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressAtIndex(uint32_t p_u32Index) const -{ + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocServiceDomain - const stcIP4Address* pIP4Address = 0; + Alloc memory for the char array representation of the service domain. - if (((uint32_t)(-1) != p_u32Index) && - (m_pIP4Addresses)) + */ + char* MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocServiceDomain(size_t p_stLength) { - - uint32_t u32Index; - for (pIP4Address = m_pIP4Addresses, u32Index = 0; ((pIP4Address) && (u32Index < p_u32Index)); pIP4Address = pIP4Address->m_pNext, ++u32Index); + releaseServiceDomain(); + if (p_stLength) + { + m_pcServiceDomain = new char[p_stLength]; + } + return m_pcServiceDomain; } - return pIP4Address; -} -#endif - -#ifdef MDNS_IP6_SUPPORT -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseIP6Addresses -*/ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseIP6Addresses(void) -{ - while (m_pIP6Addresses) + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseServiceDomain + */ + bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseServiceDomain(void) { - stcIP6Address* pNext = m_pIP6Addresses->m_pNext; - delete m_pIP6Addresses; - m_pIP6Addresses = pNext; + if (m_pcServiceDomain) + { + delete[] m_pcServiceDomain; + m_pcServiceDomain = 0; + } + return true; } - return true; -} -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::addIP6Address -*/ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::addIP6Address(MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* p_pIP6Address) -{ + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocHostDomain - bool bResult = false; + Alloc memory for the char array representation of the host domain. - if (p_pIP6Address) + */ + char* MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocHostDomain(size_t p_stLength) { - p_pIP6Address->m_pNext = m_pIP6Addresses; - m_pIP6Addresses = p_pIP6Address; - bResult = true; + releaseHostDomain(); + if (p_stLength) + { + m_pcHostDomain = new char[p_stLength]; + } + return m_pcHostDomain; } - return bResult; -} -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::removeIP6Address -*/ -bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::removeIP6Address(MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* p_pIP6Address) -{ + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseHostDomain + */ + bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseHostDomain(void) + { + if (m_pcHostDomain) + { + delete[] m_pcHostDomain; + m_pcHostDomain = 0; + } + return true; + } - bool bResult = false; + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocTxts - if (p_pIP6Address) + Alloc memory for the char array representation of the TXT items. + + */ + char* MDNSResponder::stcMDNSServiceQuery::stcAnswer::allocTxts(size_t p_stLength) { - stcIP6Address* pPred = m_pIP6Addresses; - while ((pPred) && - (pPred->m_pNext != p_pIP6Address)) + releaseTxts(); + if (p_stLength) { - pPred = pPred->m_pNext; + m_pcTxts = new char[p_stLength]; } - if (pPred) + return m_pcTxts; + } + + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseTxts + */ + bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseTxts(void) + { + if (m_pcTxts) { - pPred->m_pNext = p_pIP6Address->m_pNext; - delete p_pIP6Address; - bResult = true; + delete[] m_pcTxts; + m_pcTxts = 0; } - else if (m_pIP6Addresses == p_pIP6Address) // No predecesor, but first item + return true; + } + +#ifdef MDNS_IP4_SUPPORT + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseIP4Addresses + */ + bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseIP4Addresses(void) + { + while (m_pIP4Addresses) { - m_pIP6Addresses = p_pIP6Address->m_pNext; - delete p_pIP6Address; - bResult = true; + stcIP4Address* pNext = m_pIP4Addresses->m_pNext; + delete m_pIP4Addresses; + m_pIP4Addresses = pNext; } + return true; } - return bResult; -} - -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP6Address -*/ -MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP6Address(const IP6Address& p_IPAddress) -{ - - return (stcIP6Address*)(((const stcAnswer*)this)->findIP6Address(p_IPAddress)); -} - -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP6Address (const) -*/ -const MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP6Address(const IPAddress& p_IPAddress) const -{ - const stcIP6Address* pIP6Address = m_pIP6Addresses; - while (pIP6Address) + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::addIP4Address + */ + bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::addIP4Address( + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* p_pIP4Address) { - if (p_IP6Address->m_IPAddress == p_IPAddress) + bool bResult = false; + + if (p_pIP4Address) { - break; + p_pIP4Address->m_pNext = m_pIP4Addresses; + m_pIP4Addresses = p_pIP4Address; + bResult = true; } - pIP6Address = pIP6Address->m_pNext; + return bResult; } - return pIP6Address; -} -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressCount -*/ -uint32_t MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressCount(void) const -{ + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::removeIP4Address + */ + bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::removeIP4Address( + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* p_pIP4Address) + { + bool bResult = false; - uint32_t u32Count = 0; + if (p_pIP4Address) + { + stcIP4Address* pPred = m_pIP4Addresses; + while ((pPred) && (pPred->m_pNext != p_pIP4Address)) + { + pPred = pPred->m_pNext; + } + if (pPred) + { + pPred->m_pNext = p_pIP4Address->m_pNext; + delete p_pIP4Address; + bResult = true; + } + else if (m_pIP4Addresses == p_pIP4Address) // No predecessor, but first item + { + m_pIP4Addresses = p_pIP4Address->m_pNext; + delete p_pIP4Address; + bResult = true; + } + } + return bResult; + } - stcIP6Address* pIP6Address = m_pIP6Addresses; - while (pIP6Address) + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address (const) + */ + const MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address( + const IPAddress& p_IPAddress) const { - ++u32Count; - pIP6Address = pIP6Address->m_pNext; + return (stcIP4Address*)(((const stcAnswer*)this)->findIP4Address(p_IPAddress)); } - return u32Count; -} -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressAtIndex (const) -*/ -const MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressAtIndex(uint32_t p_u32Index) const -{ + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address + */ + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP4Address(const IPAddress& p_IPAddress) + { + stcIP4Address* pIP4Address = m_pIP4Addresses; + while (pIP4Address) + { + if (pIP4Address->m_IPAddress == p_IPAddress) + { + break; + } + pIP4Address = pIP4Address->m_pNext; + } + return pIP4Address; + } - return (stcIP6Address*)(((const stcAnswer*)this)->IP6AddressAtIndex(p_u32Index)); -} + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressCount + */ + uint32_t MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressCount(void) const + { + uint32_t u32Count = 0; -/* - MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressAtIndex -*/ -MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressAtIndex(uint32_t p_u32Index) -{ + stcIP4Address* pIP4Address = m_pIP4Addresses; + while (pIP4Address) + { + ++u32Count; + pIP4Address = pIP4Address->m_pNext; + } + return u32Count; + } - stcIP6Address* pIP6Address = 0; + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressAtIndex + */ + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressAtIndex(uint32_t p_u32Index) + { + return (stcIP4Address*)(((const stcAnswer*)this)->IP4AddressAtIndex(p_u32Index)); + } - if (((uint32_t)(-1) != p_u32Index) && - (m_pIP6Addresses)) + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressAtIndex (const) + */ + const MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP4Address* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP4AddressAtIndex(uint32_t p_u32Index) const { + const stcIP4Address* pIP4Address = 0; - uint32_t u32Index; - for (pIP6Address = m_pIP6Addresses, u32Index = 0; ((pIP6Address) && (u32Index < p_u32Index)); pIP6Address = pIP6Address->m_pNext, ++u32Index); + if (((uint32_t)(-1) != p_u32Index) && (m_pIP4Addresses)) + { + uint32_t u32Index; + for (pIP4Address = m_pIP4Addresses, u32Index = 0; + ((pIP4Address) && (u32Index < p_u32Index)); + pIP4Address = pIP4Address->m_pNext, ++u32Index) + ; + } + return pIP4Address; } - return pIP6Address; -} #endif +#ifdef MDNS_IP6_SUPPORT + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseIP6Addresses + */ + bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::releaseIP6Addresses(void) + { + while (m_pIP6Addresses) + { + stcIP6Address* pNext = m_pIP6Addresses->m_pNext; + delete m_pIP6Addresses; + m_pIP6Addresses = pNext; + } + return true; + } -/** - MDNSResponder::stcMDNSServiceQuery - - A service query object. - A static query is flaged via 'm_bLegacyQuery'; while the function 'queryService' - is waiting for answers, the internal flag 'm_bAwaitingAnswers' is set. When the - timeout is reached, the flag is removed. These two flags are only used for static - service queries. - All answers to the service query are stored in 'm_pAnswers' list. - Individual answers may be addressed by index (in the list of answers). - Every time a answer component is added (or changes) in a dynamic service query, - the callback 'm_fnCallback' is called. - The answer list may be searched by service and host domain. - - Service query object may be connected to a linked list. -*/ - -/* - MDNSResponder::stcMDNSServiceQuery::stcMDNSServiceQuery constructor -*/ -MDNSResponder::stcMDNSServiceQuery::stcMDNSServiceQuery(void) - : m_pNext(0), - m_fnCallback(0), - m_bLegacyQuery(false), - m_u8SentCount(0), - m_ResendTimeout(esp8266::polledTimeout::oneShotMs::neverExpires), - m_bAwaitingAnswers(true), - m_pAnswers(0) -{ - - clear(); -} + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::addIP6Address + */ + bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::addIP6Address( + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* p_pIP6Address) + { + bool bResult = false; -/* - MDNSResponder::stcMDNSServiceQuery::~stcMDNSServiceQuery destructor -*/ -MDNSResponder::stcMDNSServiceQuery::~stcMDNSServiceQuery(void) -{ + if (p_pIP6Address) + { + p_pIP6Address->m_pNext = m_pIP6Addresses; + m_pIP6Addresses = p_pIP6Address; + bResult = true; + } + return bResult; + } - clear(); -} + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::removeIP6Address + */ + bool MDNSResponder::stcMDNSServiceQuery::stcAnswer::removeIP6Address( + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* p_pIP6Address) + { + bool bResult = false; -/* - MDNSResponder::stcMDNSServiceQuery::clear -*/ -bool MDNSResponder::stcMDNSServiceQuery::clear(void) -{ + if (p_pIP6Address) + { + stcIP6Address* pPred = m_pIP6Addresses; + while ((pPred) && (pPred->m_pNext != p_pIP6Address)) + { + pPred = pPred->m_pNext; + } + if (pPred) + { + pPred->m_pNext = p_pIP6Address->m_pNext; + delete p_pIP6Address; + bResult = true; + } + else if (m_pIP6Addresses == p_pIP6Address) // No predecessor, but first item + { + m_pIP6Addresses = p_pIP6Address->m_pNext; + delete p_pIP6Address; + bResult = true; + } + } + return bResult; + } - m_fnCallback = 0; - m_bLegacyQuery = false; - m_u8SentCount = 0; - m_ResendTimeout.resetToNeverExpires(); - m_bAwaitingAnswers = true; - while (m_pAnswers) + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP6Address + */ + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP6Address(const IP6Address& p_IPAddress) { - stcAnswer* pNext = m_pAnswers->m_pNext; - delete m_pAnswers; - m_pAnswers = pNext; + return (stcIP6Address*)(((const stcAnswer*)this)->findIP6Address(p_IPAddress)); } - return true; -} - -/* - MDNSResponder::stcMDNSServiceQuery::answerCount -*/ -uint32_t MDNSResponder::stcMDNSServiceQuery::answerCount(void) const -{ - uint32_t u32Count = 0; - - stcAnswer* pAnswer = m_pAnswers; - while (pAnswer) + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP6Address (const) + */ + const MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::findIP6Address( + const IPAddress& p_IPAddress) const { - ++u32Count; - pAnswer = pAnswer->m_pNext; + const stcIP6Address* pIP6Address = m_pIP6Addresses; + while (pIP6Address) + { + if (p_IP6Address->m_IPAddress == p_IPAddress) + { + break; + } + pIP6Address = pIP6Address->m_pNext; + } + return pIP6Address; } - return u32Count; -} -/* - MDNSResponder::stcMDNSServiceQuery::answerAtIndex -*/ -const MDNSResponder::stcMDNSServiceQuery::stcAnswer* MDNSResponder::stcMDNSServiceQuery::answerAtIndex(uint32_t p_u32Index) const -{ + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressCount + */ + uint32_t MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressCount(void) const + { + uint32_t u32Count = 0; - const stcAnswer* pAnswer = 0; + stcIP6Address* pIP6Address = m_pIP6Addresses; + while (pIP6Address) + { + ++u32Count; + pIP6Address = pIP6Address->m_pNext; + } + return u32Count; + } - if (((uint32_t)(-1) != p_u32Index) && - (m_pAnswers)) + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressAtIndex (const) + */ + const MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressAtIndex(uint32_t p_u32Index) const { - - uint32_t u32Index; - for (pAnswer = m_pAnswers, u32Index = 0; ((pAnswer) && (u32Index < p_u32Index)); pAnswer = pAnswer->m_pNext, ++u32Index); + return (stcIP6Address*)(((const stcAnswer*)this)->IP6AddressAtIndex(p_u32Index)); } - return pAnswer; -} -/* - MDNSResponder::stcMDNSServiceQuery::answerAtIndex -*/ -MDNSResponder::stcMDNSServiceQuery::stcAnswer* MDNSResponder::stcMDNSServiceQuery::answerAtIndex(uint32_t p_u32Index) -{ + /* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressAtIndex + */ + MDNSResponder::stcMDNSServiceQuery::stcAnswer::stcIP6Address* + MDNSResponder::stcMDNSServiceQuery::stcAnswer::IP6AddressAtIndex(uint32_t p_u32Index) + { + stcIP6Address* pIP6Address = 0; - return (stcAnswer*)(((const stcMDNSServiceQuery*)this)->answerAtIndex(p_u32Index)); -} + if (((uint32_t)(-1) != p_u32Index) && (m_pIP6Addresses)) + { + uint32_t u32Index; + for (pIP6Address = m_pIP6Addresses, u32Index = 0; + ((pIP6Address) && (u32Index < p_u32Index)); + pIP6Address = pIP6Address->m_pNext, ++u32Index) + ; + } + return pIP6Address; + } +#endif -/* - MDNSResponder::stcMDNSServiceQuery::indexOfAnswer -*/ -uint32_t MDNSResponder::stcMDNSServiceQuery::indexOfAnswer(const MDNSResponder::stcMDNSServiceQuery::stcAnswer* p_pAnswer) const -{ + /** + MDNSResponder::stcMDNSServiceQuery + + A service query object. + A static query is flagged via 'm_bLegacyQuery'; while the function 'queryService' + is waiting for answers, the internal flag 'm_bAwaitingAnswers' is set. When the + timeout is reached, the flag is removed. These two flags are only used for static + service queries. + All answers to the service query are stored in 'm_pAnswers' list. + Individual answers may be addressed by index (in the list of answers). + Every time a answer component is added (or changes) in a dynamic service query, + the callback 'm_fnCallback' is called. + The answer list may be searched by service and host domain. + + Service query object may be connected to a linked list. + */ + + /* + MDNSResponder::stcMDNSServiceQuery::stcMDNSServiceQuery constructor + */ + MDNSResponder::stcMDNSServiceQuery::stcMDNSServiceQuery(void) : + m_pNext(0), m_fnCallback(0), m_bLegacyQuery(false), m_u8SentCount(0), + m_ResendTimeout(esp8266::polledTimeout::oneShotMs::neverExpires), m_bAwaitingAnswers(true), + m_pAnswers(0) + { + clear(); + } - uint32_t u32Index = 0; + /* + MDNSResponder::stcMDNSServiceQuery::~stcMDNSServiceQuery destructor + */ + MDNSResponder::stcMDNSServiceQuery::~stcMDNSServiceQuery(void) + { + clear(); + } - for (const stcAnswer* pAnswer = m_pAnswers; pAnswer; pAnswer = pAnswer->m_pNext, ++u32Index) + /* + MDNSResponder::stcMDNSServiceQuery::clear + */ + bool MDNSResponder::stcMDNSServiceQuery::clear(void) { - if (pAnswer == p_pAnswer) + m_fnCallback = 0; + m_bLegacyQuery = false; + m_u8SentCount = 0; + m_ResendTimeout.resetToNeverExpires(); + m_bAwaitingAnswers = true; + while (m_pAnswers) { - return u32Index; + stcAnswer* pNext = m_pAnswers->m_pNext; + delete m_pAnswers; + m_pAnswers = pNext; } + return true; } - return ((uint32_t)(-1)); -} -/* - MDNSResponder::stcMDNSServiceQuery::addAnswer -*/ -bool MDNSResponder::stcMDNSServiceQuery::addAnswer(MDNSResponder::stcMDNSServiceQuery::stcAnswer* p_pAnswer) -{ + /* + MDNSResponder::stcMDNSServiceQuery::answerCount + */ + uint32_t MDNSResponder::stcMDNSServiceQuery::answerCount(void) const + { + uint32_t u32Count = 0; - bool bResult = false; + stcAnswer* pAnswer = m_pAnswers; + while (pAnswer) + { + ++u32Count; + pAnswer = pAnswer->m_pNext; + } + return u32Count; + } - if (p_pAnswer) + /* + MDNSResponder::stcMDNSServiceQuery::answerAtIndex + */ + const MDNSResponder::stcMDNSServiceQuery::stcAnswer* + MDNSResponder::stcMDNSServiceQuery::answerAtIndex(uint32_t p_u32Index) const { - p_pAnswer->m_pNext = m_pAnswers; - m_pAnswers = p_pAnswer; - bResult = true; - } - return bResult; -} + const stcAnswer* pAnswer = 0; -/* - MDNSResponder::stcMDNSServiceQuery::removeAnswer -*/ -bool MDNSResponder::stcMDNSServiceQuery::removeAnswer(MDNSResponder::stcMDNSServiceQuery::stcAnswer* p_pAnswer) -{ + if (((uint32_t)(-1) != p_u32Index) && (m_pAnswers)) + { + uint32_t u32Index; + for (pAnswer = m_pAnswers, u32Index = 0; ((pAnswer) && (u32Index < p_u32Index)); + pAnswer = pAnswer->m_pNext, ++u32Index) + ; + } + return pAnswer; + } - bool bResult = false; + /* + MDNSResponder::stcMDNSServiceQuery::answerAtIndex + */ + MDNSResponder::stcMDNSServiceQuery::stcAnswer* + MDNSResponder::stcMDNSServiceQuery::answerAtIndex(uint32_t p_u32Index) + { + return (stcAnswer*)(((const stcMDNSServiceQuery*)this)->answerAtIndex(p_u32Index)); + } - if (p_pAnswer) + /* + MDNSResponder::stcMDNSServiceQuery::indexOfAnswer + */ + uint32_t MDNSResponder::stcMDNSServiceQuery::indexOfAnswer( + const MDNSResponder::stcMDNSServiceQuery::stcAnswer* p_pAnswer) const { - stcAnswer* pPred = m_pAnswers; - while ((pPred) && - (pPred->m_pNext != p_pAnswer)) + uint32_t u32Index = 0; + + for (const stcAnswer* pAnswer = m_pAnswers; pAnswer; pAnswer = pAnswer->m_pNext, ++u32Index) { - pPred = pPred->m_pNext; + if (pAnswer == p_pAnswer) + { + return u32Index; + } } - if (pPred) + return ((uint32_t)(-1)); + } + + /* + MDNSResponder::stcMDNSServiceQuery::addAnswer + */ + bool MDNSResponder::stcMDNSServiceQuery::addAnswer( + MDNSResponder::stcMDNSServiceQuery::stcAnswer* p_pAnswer) + { + bool bResult = false; + + if (p_pAnswer) { - pPred->m_pNext = p_pAnswer->m_pNext; - delete p_pAnswer; - bResult = true; + p_pAnswer->m_pNext = m_pAnswers; + m_pAnswers = p_pAnswer; + bResult = true; } - else if (m_pAnswers == p_pAnswer) // No predecesor, but first item + return bResult; + } + + /* + MDNSResponder::stcMDNSServiceQuery::removeAnswer + */ + bool MDNSResponder::stcMDNSServiceQuery::removeAnswer( + MDNSResponder::stcMDNSServiceQuery::stcAnswer* p_pAnswer) + { + bool bResult = false; + + if (p_pAnswer) { - m_pAnswers = p_pAnswer->m_pNext; - delete p_pAnswer; - bResult = true; + stcAnswer* pPred = m_pAnswers; + while ((pPred) && (pPred->m_pNext != p_pAnswer)) + { + pPred = pPred->m_pNext; + } + if (pPred) + { + pPred->m_pNext = p_pAnswer->m_pNext; + delete p_pAnswer; + bResult = true; + } + else if (m_pAnswers == p_pAnswer) // No predecessor, but first item + { + m_pAnswers = p_pAnswer->m_pNext; + delete p_pAnswer; + bResult = true; + } } + return bResult; } - return bResult; -} - -/* - MDNSResponder::stcMDNSServiceQuery::findAnswerForServiceDomain -*/ -MDNSResponder::stcMDNSServiceQuery::stcAnswer* MDNSResponder::stcMDNSServiceQuery::findAnswerForServiceDomain(const MDNSResponder::stcMDNS_RRDomain& p_ServiceDomain) -{ - stcAnswer* pAnswer = m_pAnswers; - while (pAnswer) + /* + MDNSResponder::stcMDNSServiceQuery::findAnswerForServiceDomain + */ + MDNSResponder::stcMDNSServiceQuery::stcAnswer* + MDNSResponder::stcMDNSServiceQuery::findAnswerForServiceDomain( + const MDNSResponder::stcMDNS_RRDomain& p_ServiceDomain) { - if (pAnswer->m_ServiceDomain == p_ServiceDomain) + stcAnswer* pAnswer = m_pAnswers; + while (pAnswer) { - break; + if (pAnswer->m_ServiceDomain == p_ServiceDomain) + { + break; + } + pAnswer = pAnswer->m_pNext; } - pAnswer = pAnswer->m_pNext; + return pAnswer; } - return pAnswer; -} - -/* - MDNSResponder::stcMDNSServiceQuery::findAnswerForHostDomain -*/ -MDNSResponder::stcMDNSServiceQuery::stcAnswer* MDNSResponder::stcMDNSServiceQuery::findAnswerForHostDomain(const MDNSResponder::stcMDNS_RRDomain& p_HostDomain) -{ - stcAnswer* pAnswer = m_pAnswers; - while (pAnswer) + /* + MDNSResponder::stcMDNSServiceQuery::findAnswerForHostDomain + */ + MDNSResponder::stcMDNSServiceQuery::stcAnswer* + MDNSResponder::stcMDNSServiceQuery::findAnswerForHostDomain( + const MDNSResponder::stcMDNS_RRDomain& p_HostDomain) { - if (pAnswer->m_HostDomain == p_HostDomain) + stcAnswer* pAnswer = m_pAnswers; + while (pAnswer) { - break; + if (pAnswer->m_HostDomain == p_HostDomain) + { + break; + } + pAnswer = pAnswer->m_pNext; } - pAnswer = pAnswer->m_pNext; + return pAnswer; } - return pAnswer; -} - - -/** - MDNSResponder::stcMDNSSendParameter - - A 'collection' of properties and flags for one MDNS query or response. - Mainly managed by the 'Control' functions. - The current offset in the UPD output buffer is tracked to be able to do - a simple host or service domain compression. - -*/ - -/** - MDNSResponder::stcMDNSSendParameter::stcDomainCacheItem - - A cached host or service domain, incl. the offset in the UDP output buffer. - -*/ - -/* - MDNSResponder::stcMDNSSendParameter::stcDomainCacheItem::stcDomainCacheItem constructor -*/ -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) -{ - -} - -/** - MDNSResponder::stcMDNSSendParameter -*/ - -/* - MDNSResponder::stcMDNSSendParameter::stcMDNSSendParameter constructor -*/ -MDNSResponder::stcMDNSSendParameter::stcMDNSSendParameter(void) - : m_pQuestions(0), - m_pDomainCacheItems(0) -{ - - clear(); -} -/* - MDNSResponder::stcMDNSSendParameter::~stcMDNSSendParameter destructor -*/ -MDNSResponder::stcMDNSSendParameter::~stcMDNSSendParameter(void) -{ + /** + MDNSResponder::stcMDNSSendParameter - clear(); -} + A 'collection' of properties and flags for one MDNS query or response. + Mainly managed by the 'Control' functions. + The current offset in the UPD output buffer is tracked to be able to do + a simple host or service domain compression. -/* - MDNSResponder::stcMDNSSendParameter::clear -*/ -bool MDNSResponder::stcMDNSSendParameter::clear(void) -{ + */ - m_u16ID = 0; - m_u8HostReplyMask = 0; - m_u16Offset = 0; + /** + MDNSResponder::stcMDNSSendParameter::stcDomainCacheItem - m_bLegacyQuery = false; - m_bResponse = false; - m_bAuthorative = false; - m_bUnicast = false; - m_bUnannounce = false; + A cached host or service domain, incl. the offset in the UDP output buffer. - m_bCacheFlush = true; + */ - while (m_pQuestions) + /* + MDNSResponder::stcMDNSSendParameter::stcDomainCacheItem::stcDomainCacheItem constructor + */ + 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) { - stcMDNS_RRQuestion* pNext = m_pQuestions->m_pNext; - delete m_pQuestions; - m_pQuestions = pNext; } - return clearCachedNames();; -} -/* - MDNSResponder::stcMDNSSendParameter::clear cached names -*/ -bool MDNSResponder::stcMDNSSendParameter::clearCachedNames(void) -{ - - m_u16Offset = 0; + /** + MDNSResponder::stcMDNSSendParameter + */ - while (m_pDomainCacheItems) + /* + MDNSResponder::stcMDNSSendParameter::stcMDNSSendParameter constructor + */ + MDNSResponder::stcMDNSSendParameter::stcMDNSSendParameter(void) : + m_pQuestions(0), m_pDomainCacheItems(0) { - stcDomainCacheItem* pNext = m_pDomainCacheItems->m_pNext; - delete m_pDomainCacheItems; - m_pDomainCacheItems = pNext; + clear(); } - m_pDomainCacheItems = nullptr; - return true; -} + /* + MDNSResponder::stcMDNSSendParameter::~stcMDNSSendParameter destructor + */ + MDNSResponder::stcMDNSSendParameter::~stcMDNSSendParameter(void) + { + clear(); + } -/* - MDNSResponder::stcMDNSSendParameter::shiftOffset -*/ -bool MDNSResponder::stcMDNSSendParameter::shiftOffset(uint16_t p_u16Shift) -{ + /* + MDNSResponder::stcMDNSSendParameter::clear + */ + bool MDNSResponder::stcMDNSSendParameter::clear(void) + { + m_u16ID = 0; + m_u8HostReplyMask = 0; + m_u16Offset = 0; - m_u16Offset += p_u16Shift; - return true; -} + m_bLegacyQuery = false; + m_bResponse = false; + m_bAuthorative = false; + m_bUnicast = false; + m_bUnannounce = false; -/* - MDNSResponder::stcMDNSSendParameter::addDomainCacheItem -*/ -bool MDNSResponder::stcMDNSSendParameter::addDomainCacheItem(const void* p_pHostnameOrService, - bool p_bAdditionalData, - uint16_t p_u16Offset) -{ + m_bCacheFlush = true; - bool bResult = false; + while (m_pQuestions) + { + stcMDNS_RRQuestion* pNext = m_pQuestions->m_pNext; + delete m_pQuestions; + m_pQuestions = pNext; + } - stcDomainCacheItem* pNewItem = 0; - if ((p_pHostnameOrService) && - (p_u16Offset) && - ((pNewItem = new stcDomainCacheItem(p_pHostnameOrService, p_bAdditionalData, p_u16Offset)))) + return clearCachedNames(); + ; + } + /* + MDNSResponder::stcMDNSSendParameter::clear cached names + */ + bool MDNSResponder::stcMDNSSendParameter::clearCachedNames(void) { + m_u16Offset = 0; - pNewItem->m_pNext = m_pDomainCacheItems; - bResult = ((m_pDomainCacheItems = pNewItem)); - } - return bResult; -} + while (m_pDomainCacheItems) + { + stcDomainCacheItem* pNext = m_pDomainCacheItems->m_pNext; + delete m_pDomainCacheItems; + m_pDomainCacheItems = pNext; + } + m_pDomainCacheItems = nullptr; -/* - MDNSResponder::stcMDNSSendParameter::findCachedDomainOffset -*/ -uint16_t MDNSResponder::stcMDNSSendParameter::findCachedDomainOffset(const void* p_pHostnameOrService, - bool p_bAdditionalData) const -{ + return true; + } - const stcDomainCacheItem* pCacheItem = m_pDomainCacheItems; + /* + MDNSResponder::stcMDNSSendParameter::shiftOffset + */ + bool MDNSResponder::stcMDNSSendParameter::shiftOffset(uint16_t p_u16Shift) + { + m_u16Offset += p_u16Shift; + return true; + } - for (; pCacheItem; pCacheItem = pCacheItem->m_pNext) + /* + MDNSResponder::stcMDNSSendParameter::addDomainCacheItem + */ + bool MDNSResponder::stcMDNSSendParameter::addDomainCacheItem(const void* p_pHostnameOrService, + bool p_bAdditionalData, + uint16_t p_u16Offset) { - if ((pCacheItem->m_pHostnameOrService == p_pHostnameOrService) && - (pCacheItem->m_bAdditionalData == p_bAdditionalData)) // Found cache item + bool bResult = false; + + stcDomainCacheItem* pNewItem = 0; + if ((p_pHostnameOrService) && (p_u16Offset) + && ((pNewItem + = new stcDomainCacheItem(p_pHostnameOrService, p_bAdditionalData, p_u16Offset)))) { - break; + pNewItem->m_pNext = m_pDomainCacheItems; + bResult = ((m_pDomainCacheItems = pNewItem)); } + return bResult; } - return (pCacheItem ? pCacheItem->m_u16Offset : 0); -} -} // namespace MDNSImplementation - -} // namespace esp8266 + /* + MDNSResponder::stcMDNSSendParameter::findCachedDomainOffset + */ + uint16_t + MDNSResponder::stcMDNSSendParameter::findCachedDomainOffset(const void* p_pHostnameOrService, + bool p_bAdditionalData) const + { + const stcDomainCacheItem* pCacheItem = m_pDomainCacheItems; + for (; pCacheItem; pCacheItem = pCacheItem->m_pNext) + { + if ((pCacheItem->m_pHostnameOrService == p_pHostnameOrService) + && (pCacheItem->m_bAdditionalData == p_bAdditionalData)) // Found cache item + { + break; + } + } + return (pCacheItem ? pCacheItem->m_u16Offset : 0); + } +} // namespace MDNSImplementation +} // namespace esp8266 diff --git a/libraries/ESP8266mDNS/src/LEAmDNS_Transfer.cpp b/libraries/ESP8266mDNS/src/LEAmDNS_Transfer.cpp index 1b8e337895..ed58f1b86f 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS_Transfer.cpp +++ b/libraries/ESP8266mDNS/src/LEAmDNS_Transfer.cpp @@ -22,7 +22,8 @@ */ -extern "C" { +extern "C" +{ #include "user_interface.h" } @@ -30,7 +31,6 @@ extern "C" { #include "LEAmDNS_lwIPdefs.h" #include "LEAmDNS_Priv.h" - namespace esp8266 { @@ -40,1749 +40,1845 @@ namespace esp8266 namespace MDNSImplementation { -/** - CONST STRINGS -*/ -static const char* scpcLocal = "local"; -static const char* scpcServices = "services"; -static const char* scpcDNSSD = "dns-sd"; -static const char* scpcUDP = "udp"; -//static const char* scpcTCP = "tcp"; + /** + CONST STRINGS + */ + static const char* scpcLocal = "local"; + static const char* scpcServices = "services"; + static const char* scpcDNSSD = "dns-sd"; + static const char* scpcUDP = "udp"; + // static const char* scpcTCP = "tcp"; #ifdef MDNS_IP4_SUPPORT -static const char* scpcReverseIP4Domain = "in-addr"; + static const char* scpcReverseIP4Domain = "in-addr"; #endif #ifdef MDNS_IP6_SUPPORT -static const char* scpcReverseIP6Domain = "ip6"; + static const char* scpcReverseIP6Domain = "ip6"; #endif -static const char* scpcReverseTopDomain = "arpa"; + static const char* scpcReverseTopDomain = "arpa"; -/** - TRANSFER -*/ + /** + TRANSFER + */ + /** + SENDING + */ -/** - SENDING -*/ + /* + MDNSResponder::_sendMDNSMessage -/* - MDNSResponder::_sendMDNSMessage + Unicast responses are prepared and sent directly to the querier. + Multicast responses or queries are transferred to _sendMDNSMessage_Multicast - Unicast responses are prepared and sent directly to the querier. - Multicast responses or queries are transferred to _sendMDNSMessage_Multicast + Any reply flags in installed services are removed at the end! - Any reply flags in installed services are removed at the end! + */ + bool MDNSResponder::_sendMDNSMessage(MDNSResponder::stcMDNSSendParameter& p_rSendParameter) + { + bool bResult = true; -*/ -bool MDNSResponder::_sendMDNSMessage(MDNSResponder::stcMDNSSendParameter& p_rSendParameter) -{ + if (p_rSendParameter.m_bResponse + && p_rSendParameter.m_bUnicast) // Unicast response -> Send to querier + { + DEBUG_EX_ERR(if (!m_pUDPContext->getRemoteAddress()) { + DEBUG_OUTPUT.printf_P(PSTR( + "[MDNSResponder] _sendMDNSMessage: MISSING remote address for response!\n")); + }); + IPAddress ipRemote; + ipRemote = m_pUDPContext->getRemoteAddress(); + bResult + = ((_prepareMDNSMessage(p_rSendParameter, m_pUDPContext->getInputNetif()->ip_addr)) + && (m_pUDPContext->sendTimeout(ipRemote, m_pUDPContext->getRemotePort(), + MDNS_UDPCONTEXT_TIMEOUT))); + } + else // Multicast response + { + bResult = _sendMDNSMessage_Multicast(p_rSendParameter); + } - bool bResult = true; + // Finally clear service reply masks + for (stcMDNSService* pService = m_pServices; pService; pService = pService->m_pNext) + { + pService->m_u8ReplyMask = 0; + } - if (p_rSendParameter.m_bResponse && - p_rSendParameter.m_bUnicast) // Unicast response -> Send to querier - { - DEBUG_EX_ERR(if (!m_pUDPContext->getRemoteAddress()) - { - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendMDNSMessage: MISSING remote address for response!\n")); + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendMDNSMessage: FAILED!\n")); }); - IPAddress ipRemote; - ipRemote = m_pUDPContext->getRemoteAddress(); - bResult = ((_prepareMDNSMessage(p_rSendParameter, m_pUDPContext->getInputNetif()->ip_addr)) && - (m_pUDPContext->sendTimeout(ipRemote, m_pUDPContext->getRemotePort(), MDNS_UDPCONTEXT_TIMEOUT))); - } - else // Multicast response - { - bResult = _sendMDNSMessage_Multicast(p_rSendParameter); - } - - // Finally clear service reply masks - for (stcMDNSService* pService = m_pServices; pService; pService = pService->m_pNext) - { - pService->m_u8ReplyMask = 0; + return bResult; } - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendMDNSMessage: FAILED!\n")); - }); - return bResult; -} - -/* - MDNSResponder::_sendMDNSMessage_Multicast - - Fills the UDP output buffer (via _prepareMDNSMessage) and sends the buffer - via the selected WiFi interface (Station or AP) -*/ -bool MDNSResponder::_sendMDNSMessage_Multicast(MDNSResponder::stcMDNSSendParameter& p_rSendParameter) -{ - - bool bResult = false; + /* + MDNSResponder::_sendMDNSMessage_Multicast - for (netif* pNetIf = netif_list; pNetIf; pNetIf = pNetIf->next) + Fills the UDP output buffer (via _prepareMDNSMessage) and sends the buffer + via the selected WiFi interface (Station or AP) + */ + bool + MDNSResponder::_sendMDNSMessage_Multicast(MDNSResponder::stcMDNSSendParameter& p_rSendParameter) { - if (netif_is_up(pNetIf)) + bool bResult = false; + + for (netif* pNetIf = netif_list; pNetIf; pNetIf = pNetIf->next) { - IPAddress fromIPAddress; - //fromIPAddress = _getResponseMulticastInterface(); - fromIPAddress = pNetIf->ip_addr; - m_pUDPContext->setMulticastInterface(fromIPAddress); + if (netif_is_up(pNetIf) && IPAddress(pNetIf->ip_addr).isSet()) + { + IPAddress fromIPAddress; + // fromIPAddress = _getResponseMulticastInterface(); + fromIPAddress = pNetIf->ip_addr; + m_pUDPContext->setMulticastInterface(fromIPAddress); #ifdef MDNS_IP4_SUPPORT - IPAddress toMulticastAddress(DNS_MQUERY_IPV4_GROUP_INIT); + IPAddress toMulticastAddress(DNS_MQUERY_IPV4_GROUP_INIT); #endif #ifdef MDNS_IP6_SUPPORT - //TODO: set multicast address - IPAddress toMulticastAddress(DNS_MQUERY_IPV6_GROUP_INIT); + // TODO: set multicast address + IPAddress toMulticastAddress(DNS_MQUERY_IPV6_GROUP_INIT); #endif - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendMDNSMessage_Multicast: Will send to '%s'.\n"), toMulticastAddress.toString().c_str());); - bResult = ((_prepareMDNSMessage(p_rSendParameter, fromIPAddress)) && - (m_pUDPContext->sendTimeout(toMulticastAddress, DNS_MQUERY_PORT, MDNS_UDPCONTEXT_TIMEOUT))); - - DEBUG_EX_ERR(if (!bResult) - { - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendMDNSMessage_Multicast: FAILED!\n")); - }); + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _sendMDNSMessage_Multicast: Will send to '%s'.\n"), + toMulticastAddress.toString().c_str());); + bResult = ((_prepareMDNSMessage(p_rSendParameter, fromIPAddress)) + && (m_pUDPContext->sendTimeout(toMulticastAddress, DNS_MQUERY_PORT, + MDNS_UDPCONTEXT_TIMEOUT))); + + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _sendMDNSMessage_Multicast: FAILED!\n")); + }); + } } + return bResult; } - return bResult; -} - -/* - MDNSResponder::_prepareMDNSMessage - The MDNS message is composed in a two-step process. - In the first loop 'only' the header informations (mainly number of answers) are collected, - while in the seconds loop, the header and all queries and answers are written to the UDP - output buffer. + /* + MDNSResponder::_prepareMDNSMessage -*/ -bool MDNSResponder::_prepareMDNSMessage(MDNSResponder::stcMDNSSendParameter& p_rSendParameter, - IPAddress p_IPAddress) -{ - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage\n"));); - bool bResult = true; - p_rSendParameter.clearCachedNames(); // Need to remove cached names, p_SendParameter might have been used before on other interface - - // Prepare header; count answers - stcMDNS_MsgHeader msgHeader(p_rSendParameter.m_u16ID, p_rSendParameter.m_bResponse, 0, p_rSendParameter.m_bAuthorative); - // If this is a response, the answers are anwers, - // else this is a query or probe and the answers go into auth section - uint16_t& ru16Answers = (p_rSendParameter.m_bResponse - ? msgHeader.m_u16ANCount - : msgHeader.m_u16NSCount); + The MDNS message is composed in a two-step process. + In the first loop 'only' the header information (mainly number of answers) are collected, + while in the seconds loop, the header and all queries and answers are written to the UDP + output buffer. - /** - enuSequence */ - enum enuSequence - { - Sequence_Count = 0, - Sequence_Send = 1 - }; - - // Two step sequence: 'Count' and 'Send' - for (uint32_t sequence = Sequence_Count; ((bResult) && (sequence <= Sequence_Send)); ++sequence) - { - DEBUG_EX_INFO( - if (Sequence_Send == sequence) + bool MDNSResponder::_prepareMDNSMessage(MDNSResponder::stcMDNSSendParameter& p_rSendParameter, + IPAddress p_IPAddress) { - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: ID:%u QR:%u OP:%u AA:%u TC:%u RD:%u RA:%u R:%u QD:%u AN:%u NS:%u AR:%u\n"), - (unsigned)msgHeader.m_u16ID, - (unsigned)msgHeader.m_1bQR, (unsigned)msgHeader.m_4bOpcode, (unsigned)msgHeader.m_1bAA, (unsigned)msgHeader.m_1bTC, (unsigned)msgHeader.m_1bRD, - (unsigned)msgHeader.m_1bRA, (unsigned)msgHeader.m_4bRCode, - (unsigned)msgHeader.m_u16QDCount, - (unsigned)msgHeader.m_u16ANCount, - (unsigned)msgHeader.m_u16NSCount, - (unsigned)msgHeader.m_u16ARCount); - } - ); - // Count/send - // Header - bResult = ((Sequence_Count == sequence) - ? true - : _writeMDNSMsgHeader(msgHeader, p_rSendParameter)); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSMsgHeader FAILED!\n"));); - // Questions - for (stcMDNS_RRQuestion* pQuestion = p_rSendParameter.m_pQuestions; ((bResult) && (pQuestion)); pQuestion = pQuestion->m_pNext) - { - ((Sequence_Count == sequence) - ? ++msgHeader.m_u16QDCount - : (bResult = _writeMDNSQuestion(*pQuestion, p_rSendParameter))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSQuestion FAILED!\n"));); - } - - // Answers and authorative answers -#ifdef MDNS_IP4_SUPPORT - if ((bResult) && - (p_rSendParameter.m_u8HostReplyMask & ContentFlag_A)) - { - ((Sequence_Count == sequence) - ? ++ru16Answers - : (bResult = _writeMDNSAnswer_A(p_IPAddress, p_rSendParameter))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_A(A) FAILED!\n"));); - } - if ((bResult) && - (p_rSendParameter.m_u8HostReplyMask & ContentFlag_PTR_IP4)) - { - ((Sequence_Count == sequence) - ? ++ru16Answers - : (bResult = _writeMDNSAnswer_PTR_IP4(p_IPAddress, p_rSendParameter))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_PTR_IP4 FAILED!\n"));); - } -#endif -#ifdef MDNS_IP6_SUPPORT - if ((bResult) && - (p_rSendParameter.m_u8HostReplyMask & ContentFlag_AAAA)) - { - ((Sequence_Count == sequence) - ? ++ru16Answers - : (bResult = _writeMDNSAnswer_AAAA(p_IPAddress, p_rSendParameter))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_AAAA(A) FAILED!\n"));); - } - if ((bResult) && - (p_rSendParameter.m_u8HostReplyMask & ContentFlag_PTR_IP6)) + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage\n"));); + bool bResult = true; + p_rSendParameter.clearCachedNames(); // Need to remove cached names, p_SendParameter might + // have been used before on other interface + + // Prepare header; count answers + stcMDNS_MsgHeader msgHeader(p_rSendParameter.m_u16ID, p_rSendParameter.m_bResponse, 0, + p_rSendParameter.m_bAuthorative); + // If this is a response, the answers are anwers, + // else this is a query or probe and the answers go into auth section + uint16_t& ru16Answers + = (p_rSendParameter.m_bResponse ? msgHeader.m_u16ANCount : msgHeader.m_u16NSCount); + + /** + enuSequence + */ + enum enuSequence { - ((Sequence_Count == sequence) - ? ++ru16Answers - : (bResult = _writeMDNSAnswer_PTR_IP6(p_IPAddress, p_rSendParameter))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_PTR_IP6 FAILED!\n"));); - } -#endif + Sequence_Count = 0, + Sequence_Send = 1 + }; - for (stcMDNSService* pService = m_pServices; ((bResult) && (pService)); pService = pService->m_pNext) + // Two step sequence: 'Count' and 'Send' + for (uint32_t sequence = Sequence_Count; ((bResult) && (sequence <= Sequence_Send)); + ++sequence) { - if ((bResult) && - (pService->m_u8ReplyMask & ContentFlag_PTR_TYPE)) - { - ((Sequence_Count == sequence) - ? ++ru16Answers - : (bResult = _writeMDNSAnswer_PTR_TYPE(*pService, p_rSendParameter))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_PTR_TYPE FAILED!\n"));); - } - if ((bResult) && - (pService->m_u8ReplyMask & ContentFlag_PTR_NAME)) + DEBUG_EX_INFO(if (Sequence_Send == sequence) { + DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _prepareMDNSMessage: ID:%u QR:%u OP:%u AA:%u TC:%u RD:%u " + "RA:%u R:%u QD:%u AN:%u NS:%u AR:%u\n"), + (unsigned)msgHeader.m_u16ID, (unsigned)msgHeader.m_1bQR, + (unsigned)msgHeader.m_4bOpcode, (unsigned)msgHeader.m_1bAA, + (unsigned)msgHeader.m_1bTC, (unsigned)msgHeader.m_1bRD, + (unsigned)msgHeader.m_1bRA, (unsigned)msgHeader.m_4bRCode, + (unsigned)msgHeader.m_u16QDCount, (unsigned)msgHeader.m_u16ANCount, + (unsigned)msgHeader.m_u16NSCount, (unsigned)msgHeader.m_u16ARCount); + }); + // Count/send + // Header + bResult + = ((Sequence_Count == sequence) ? true + : _writeMDNSMsgHeader(msgHeader, p_rSendParameter)); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSMsgHeader FAILED!\n"));); + // Questions + for (stcMDNS_RRQuestion* pQuestion = p_rSendParameter.m_pQuestions; + ((bResult) && (pQuestion)); pQuestion = pQuestion->m_pNext) { ((Sequence_Count == sequence) - ? ++ru16Answers - : (bResult = _writeMDNSAnswer_PTR_NAME(*pService, p_rSendParameter))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_PTR_NAME FAILED!\n"));); + ? ++msgHeader.m_u16QDCount + : (bResult = _writeMDNSQuestion(*pQuestion, p_rSendParameter))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSQuestion FAILED!\n"));); } - if ((bResult) && - (pService->m_u8ReplyMask & ContentFlag_SRV)) + + // Answers and authoritative answers +#ifdef MDNS_IP4_SUPPORT + if ((bResult) && (p_rSendParameter.m_u8HostReplyMask & ContentFlag_A)) { ((Sequence_Count == sequence) - ? ++ru16Answers - : (bResult = _writeMDNSAnswer_SRV(*pService, p_rSendParameter))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_SRV(A) FAILED!\n"));); + ? ++ru16Answers + : (bResult = _writeMDNSAnswer_A(p_IPAddress, p_rSendParameter))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_A(A) FAILED!\n"));); } - if ((bResult) && - (pService->m_u8ReplyMask & ContentFlag_TXT)) + if ((bResult) && (p_rSendParameter.m_u8HostReplyMask & ContentFlag_PTR_IP4)) { ((Sequence_Count == sequence) - ? ++ru16Answers - : (bResult = _writeMDNSAnswer_TXT(*pService, p_rSendParameter))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_TXT(A) FAILED!\n"));); + ? ++ru16Answers + : (bResult = _writeMDNSAnswer_PTR_IP4(p_IPAddress, p_rSendParameter))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR( + "[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_PTR_IP4 FAILED!\n"));); } - } // for services - - // Additional answers -#ifdef MDNS_IP4_SUPPORT - bool bNeedsAdditionalAnswerA = false; #endif #ifdef MDNS_IP6_SUPPORT - bool bNeedsAdditionalAnswerAAAA = false; -#endif - for (stcMDNSService* pService = m_pServices; ((bResult) && (pService)); pService = pService->m_pNext) - { - if ((bResult) && - (pService->m_u8ReplyMask & ContentFlag_PTR_NAME) && // If PTR_NAME is requested, AND - (!(pService->m_u8ReplyMask & ContentFlag_SRV))) // NOT SRV -> add SRV as additional answer + if ((bResult) && (p_rSendParameter.m_u8HostReplyMask & ContentFlag_AAAA)) { ((Sequence_Count == sequence) - ? ++msgHeader.m_u16ARCount - : (bResult = _writeMDNSAnswer_SRV(*pService, p_rSendParameter))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_SRV(B) FAILED!\n"));); + ? ++ru16Answers + : (bResult = _writeMDNSAnswer_AAAA(p_IPAddress, p_rSendParameter))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR( + "[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_AAAA(A) FAILED!\n"));); } - if ((bResult) && - (pService->m_u8ReplyMask & ContentFlag_PTR_NAME) && // If PTR_NAME is requested, AND - (!(pService->m_u8ReplyMask & ContentFlag_TXT))) // NOT TXT -> add TXT as additional answer + if ((bResult) && (p_rSendParameter.m_u8HostReplyMask & ContentFlag_PTR_IP6)) { ((Sequence_Count == sequence) - ? ++msgHeader.m_u16ARCount - : (bResult = _writeMDNSAnswer_TXT(*pService, p_rSendParameter))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_TXT(B) FAILED!\n"));); + ? ++ru16Answers + : (bResult = _writeMDNSAnswer_PTR_IP6(p_IPAddress, p_rSendParameter))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR( + "[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_PTR_IP6 FAILED!\n"));); } - if ((pService->m_u8ReplyMask & (ContentFlag_PTR_NAME | ContentFlag_SRV)) || // If service instance name or SRV OR - (p_rSendParameter.m_u8HostReplyMask & (ContentFlag_A | ContentFlag_AAAA))) // any host IP address is requested +#endif + + for (stcMDNSService* pService = m_pServices; ((bResult) && (pService)); + pService = pService->m_pNext) { -#ifdef MDNS_IP4_SUPPORT - if ((bResult) && - (!(p_rSendParameter.m_u8HostReplyMask & ContentFlag_A))) // Add IP4 address + if ((bResult) && (pService->m_u8ReplyMask & ContentFlag_PTR_TYPE)) + { + ((Sequence_Count == sequence) + ? ++ru16Answers + : (bResult = _writeMDNSAnswer_PTR_TYPE(*pService, p_rSendParameter))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_PTR_TYPE " + "FAILED!\n"));); + } + if ((bResult) && (pService->m_u8ReplyMask & ContentFlag_PTR_NAME)) + { + ((Sequence_Count == sequence) + ? ++ru16Answers + : (bResult = _writeMDNSAnswer_PTR_NAME(*pService, p_rSendParameter))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_PTR_NAME " + "FAILED!\n"));); + } + if ((bResult) && (pService->m_u8ReplyMask & ContentFlag_SRV)) + { + ((Sequence_Count == sequence) + ? ++ru16Answers + : (bResult = _writeMDNSAnswer_SRV(*pService, p_rSendParameter))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_SRV(A) " + "FAILED!\n"));); + } + if ((bResult) && (pService->m_u8ReplyMask & ContentFlag_TXT)) { - bNeedsAdditionalAnswerA = true; + ((Sequence_Count == sequence) + ? ++ru16Answers + : (bResult = _writeMDNSAnswer_TXT(*pService, p_rSendParameter))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_TXT(A) " + "FAILED!\n"));); } + } // for services + + // Additional answers +#ifdef MDNS_IP4_SUPPORT + bool bNeedsAdditionalAnswerA = false; #endif #ifdef MDNS_IP6_SUPPORT - if ((bResult) && - (!(p_rSendParameter.m_u8HostReplyMask & ContentFlag_AAAA))) // Add IP6 address + bool bNeedsAdditionalAnswerAAAA = false; +#endif + for (stcMDNSService* pService = m_pServices; ((bResult) && (pService)); + pService = pService->m_pNext) + { + if ((bResult) && (pService->m_u8ReplyMask & ContentFlag_PTR_NAME) + && // If PTR_NAME is requested, AND + (!(pService->m_u8ReplyMask + & ContentFlag_SRV))) // NOT SRV -> add SRV as additional answer { - bNeedsAdditionalAnswerAAAA = true; + ((Sequence_Count == sequence) + ? ++msgHeader.m_u16ARCount + : (bResult = _writeMDNSAnswer_SRV(*pService, p_rSendParameter))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_SRV(B) " + "FAILED!\n"));); } + if ((bResult) && (pService->m_u8ReplyMask & ContentFlag_PTR_NAME) + && // If PTR_NAME is requested, AND + (!(pService->m_u8ReplyMask + & ContentFlag_TXT))) // NOT TXT -> add TXT as additional answer + { + ((Sequence_Count == sequence) + ? ++msgHeader.m_u16ARCount + : (bResult = _writeMDNSAnswer_TXT(*pService, p_rSendParameter))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_TXT(B) " + "FAILED!\n"));); + } + if ((pService->m_u8ReplyMask & (ContentFlag_PTR_NAME | ContentFlag_SRV)) + || // If service instance name or SRV OR + (p_rSendParameter.m_u8HostReplyMask + & (ContentFlag_A | ContentFlag_AAAA))) // any host IP address is requested + { +#ifdef MDNS_IP4_SUPPORT + if ((bResult) + && (!(p_rSendParameter.m_u8HostReplyMask + & ContentFlag_A))) // Add IP4 address + { + bNeedsAdditionalAnswerA = true; + } #endif - } - } // for services +#ifdef MDNS_IP6_SUPPORT + if ((bResult) + && (!(p_rSendParameter.m_u8HostReplyMask + & ContentFlag_AAAA))) // Add IP6 address + { + bNeedsAdditionalAnswerAAAA = true; + } +#endif + } + } // for services - // Answer A needed? + // Answer A needed? #ifdef MDNS_IP4_SUPPORT - if ((bResult) && - (bNeedsAdditionalAnswerA)) - { - ((Sequence_Count == sequence) - ? ++msgHeader.m_u16ARCount - : (bResult = _writeMDNSAnswer_A(p_IPAddress, p_rSendParameter))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_A(B) FAILED!\n"));); - } + if ((bResult) && (bNeedsAdditionalAnswerA)) + { + ((Sequence_Count == sequence) + ? ++msgHeader.m_u16ARCount + : (bResult = _writeMDNSAnswer_A(p_IPAddress, p_rSendParameter))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_A(B) FAILED!\n"));); + } #endif #ifdef MDNS_IP6_SUPPORT - // Answer AAAA needed? - if ((bResult) && - (bNeedsAdditionalAnswerAAAA)) - { - ((Sequence_Count == sequence) - ? ++msgHeader.m_u16ARCount - : (bResult = _writeMDNSAnswer_AAAA(p_IPAddress, p_rSendParameter))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_AAAA(B) FAILED!\n"));); - } + // Answer AAAA needed? + if ((bResult) && (bNeedsAdditionalAnswerAAAA)) + { + ((Sequence_Count == sequence) + ? ++msgHeader.m_u16ARCount + : (bResult = _writeMDNSAnswer_AAAA(p_IPAddress, p_rSendParameter))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR( + "[MDNSResponder] _prepareMDNSMessage: _writeMDNSAnswer_AAAA(B) FAILED!\n"));); + } #endif - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: Loop %i FAILED!\n"), sequence);); - } // for sequence - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _prepareMDNSMessage: FAILED!\n"));); - return bResult; -} - -/* - MDNSResponder::_sendMDNSServiceQuery - - Creates and sends a PTR query for the given service domain. - -*/ -bool MDNSResponder::_sendMDNSServiceQuery(const MDNSResponder::stcMDNSServiceQuery& p_ServiceQuery) -{ - - return _sendMDNSQuery(p_ServiceQuery.m_ServiceTypeDomain, DNS_RRTYPE_PTR); -} - -/* - MDNSResponder::_sendMDNSQuery - - Creates and sends a query for the given domain and query type. + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _prepareMDNSMessage: Loop %i FAILED!\n"), sequence);); + } // for sequence + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _prepareMDNSMessage: FAILED!\n"));); + return bResult; + } -*/ -bool MDNSResponder::_sendMDNSQuery(const MDNSResponder::stcMDNS_RRDomain& p_QueryDomain, - uint16_t p_u16QueryType, - stcMDNSServiceQuery::stcAnswer* p_pKnownAnswers /*= 0*/) -{ + /* + MDNSResponder::_sendMDNSServiceQuery - bool bResult = false; + Creates and sends a PTR query for the given service domain. - stcMDNSSendParameter sendParameter; - if (0 != ((sendParameter.m_pQuestions = new stcMDNS_RRQuestion))) + */ + bool + MDNSResponder::_sendMDNSServiceQuery(const MDNSResponder::stcMDNSServiceQuery& p_ServiceQuery) { - sendParameter.m_pQuestions->m_Header.m_Domain = p_QueryDomain; - - sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Type = p_u16QueryType; - // It seems, that some mDNS implementations don't support 'unicast response' questions... - sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Class = (/*0x8000 |*/ DNS_RRCLASS_IN); // /*Unicast &*/ INternet + return _sendMDNSQuery(p_ServiceQuery.m_ServiceTypeDomain, DNS_RRTYPE_PTR); + } - // TODO: Add knwon answer to the query - (void)p_pKnownAnswers; + /* + MDNSResponder::_sendMDNSQuery - bResult = _sendMDNSMessage(sendParameter); - } // else: FAILED to alloc question - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _sendMDNSQuery: FAILED to alloc question!\n"));); - return bResult; -} + Creates and sends a query for the given domain and query type. -/** - HELPERS -*/ + */ + bool MDNSResponder::_sendMDNSQuery(const MDNSResponder::stcMDNS_RRDomain& p_QueryDomain, + uint16_t p_u16QueryType, + stcMDNSServiceQuery::stcAnswer* p_pKnownAnswers /*= 0*/) + { + bool bResult = false; -/** - RESOURCE RECORDS -*/ + stcMDNSSendParameter sendParameter; + if (0 != ((sendParameter.m_pQuestions = new stcMDNS_RRQuestion))) + { + sendParameter.m_pQuestions->m_Header.m_Domain = p_QueryDomain; + + sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Type = p_u16QueryType; + // It seems, that some mDNS implementations don't support 'unicast response' + // questions... + sendParameter.m_pQuestions->m_Header.m_Attributes.m_u16Class + = (/*0x8000 |*/ DNS_RRCLASS_IN); // /*Unicast &*/ INternet + + // TODO: Add known answer to the query + (void)p_pKnownAnswers; + + bResult = _sendMDNSMessage(sendParameter); + } // else: FAILED to alloc question + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _sendMDNSQuery: FAILED to alloc question!\n"));); + return bResult; + } -/* - MDNSResponder::_readRRQuestion + /** + HELPERS + */ - Reads a question (eg. MyESP._http._tcp.local ANY IN) from the UPD input buffer. + /** + RESOURCE RECORDS + */ -*/ -bool MDNSResponder::_readRRQuestion(MDNSResponder::stcMDNS_RRQuestion& p_rRRQuestion) -{ - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRQuestion\n"));); + /* + MDNSResponder::_readRRQuestion - bool bResult = false; + Reads a question (eg. MyESP._http._tcp.local ANY IN) from the UPD input buffer. - if ((bResult = _readRRHeader(p_rRRQuestion.m_Header))) + */ + bool MDNSResponder::_readRRQuestion(MDNSResponder::stcMDNS_RRQuestion& p_rRRQuestion) { - // Extract unicast flag from class field - p_rRRQuestion.m_bUnicast = (p_rRRQuestion.m_Header.m_Attributes.m_u16Class & 0x8000); - p_rRRQuestion.m_Header.m_Attributes.m_u16Class &= (~0x8000); - - DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRQuestion ")); - _printRRDomain(p_rRRQuestion.m_Header.m_Domain); - DEBUG_OUTPUT.printf_P(PSTR(" Type:0x%04X Class:0x%04X %s\n"), (unsigned)p_rRRQuestion.m_Header.m_Attributes.m_u16Type, (unsigned)p_rRRQuestion.m_Header.m_Attributes.m_u16Class, (p_rRRQuestion.m_bUnicast ? "Unicast" : "Multicast")); - ); - } - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRQuestion: FAILED!\n")); - }); - return bResult; -} + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRQuestion\n"));); -/* - MDNSResponder::_readRRAnswer + bool bResult = false; - Reads an answer (eg. _http._tcp.local PTR OP TTL MyESP._http._tcp.local) - from the UDP input buffer. - After reading the domain and type info, the further processing of the answer - is transferred the answer specific reading functions. - Unknown answer types are processed by the generic answer reader (to remove them - from the input buffer). + if ((bResult = _readRRHeader(p_rRRQuestion.m_Header))) + { + // Extract unicast flag from class field + p_rRRQuestion.m_bUnicast = (p_rRRQuestion.m_Header.m_Attributes.m_u16Class & 0x8000); + p_rRRQuestion.m_Header.m_Attributes.m_u16Class &= (~0x8000); + + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRQuestion ")); + _printRRDomain(p_rRRQuestion.m_Header.m_Domain); DEBUG_OUTPUT.printf_P( + PSTR(" Type:0x%04X Class:0x%04X %s\n"), + (unsigned)p_rRRQuestion.m_Header.m_Attributes.m_u16Type, + (unsigned)p_rRRQuestion.m_Header.m_Attributes.m_u16Class, + (p_rRRQuestion.m_bUnicast ? "Unicast" : "Multicast"));); + } + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRQuestion: FAILED!\n")); + }); + return bResult; + } -*/ -bool MDNSResponder::_readRRAnswer(MDNSResponder::stcMDNS_RRAnswer*& p_rpRRAnswer) -{ - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswer\n"));); + /* + MDNSResponder::_readRRAnswer - bool bResult = false; + Reads an answer (eg. _http._tcp.local PTR OP TTL MyESP._http._tcp.local) + from the UDP input buffer. + After reading the domain and type info, the further processing of the answer + is transferred the answer specific reading functions. + Unknown answer types are processed by the generic answer reader (to remove them + from the input buffer). - stcMDNS_RRHeader header; - uint32_t u32TTL; - uint16_t u16RDLength; - if ((_readRRHeader(header)) && - (_udpRead32(u32TTL)) && - (_udpRead16(u16RDLength))) + */ + bool MDNSResponder::_readRRAnswer(MDNSResponder::stcMDNS_RRAnswer*& p_rpRRAnswer) { + // DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswer\n"));); - /* DEBUG_EX_INFO( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswer: Reading 0x%04X answer (class:0x%04X, TTL:%u, RDLength:%u) for "), header.m_Attributes.m_u16Type, header.m_Attributes.m_u16Class, u32TTL, u16RDLength); - _printRRDomain(header.m_Domain); - DEBUG_OUTPUT.printf_P(PSTR("\n")); - );*/ + bool bResult = false; - switch (header.m_Attributes.m_u16Type & (~0x8000)) // Topmost bit might carry 'cache flush' flag + stcMDNS_RRHeader header; + uint32_t u32TTL; + uint16_t u16RDLength; + if ((_readRRHeader(header)) && (_udpRead32(u32TTL)) && (_udpRead16(u16RDLength))) { -#ifdef MDNS_IP4_SUPPORT - case DNS_RRTYPE_A: - p_rpRRAnswer = new stcMDNS_RRAnswerA(header, u32TTL); - bResult = _readRRAnswerA(*(stcMDNS_RRAnswerA*&)p_rpRRAnswer, u16RDLength); - break; -#endif - case DNS_RRTYPE_PTR: - p_rpRRAnswer = new stcMDNS_RRAnswerPTR(header, u32TTL); - bResult = _readRRAnswerPTR(*(stcMDNS_RRAnswerPTR*&)p_rpRRAnswer, u16RDLength); - break; - case DNS_RRTYPE_TXT: - p_rpRRAnswer = new stcMDNS_RRAnswerTXT(header, u32TTL); - bResult = _readRRAnswerTXT(*(stcMDNS_RRAnswerTXT*&)p_rpRRAnswer, u16RDLength); - break; -#ifdef MDNS_IP6_SUPPORT - case DNS_RRTYPE_AAAA: - p_rpRRAnswer = new stcMDNS_RRAnswerAAAA(header, u32TTL); - bResult = _readRRAnswerAAAA(*(stcMDNS_RRAnswerAAAA*&)p_rpRRAnswer, u16RDLength); - break; -#endif - case DNS_RRTYPE_SRV: - p_rpRRAnswer = new stcMDNS_RRAnswerSRV(header, u32TTL); - bResult = _readRRAnswerSRV(*(stcMDNS_RRAnswerSRV*&)p_rpRRAnswer, u16RDLength); - break; - default: - p_rpRRAnswer = new stcMDNS_RRAnswerGeneric(header, u32TTL); - bResult = _readRRAnswerGeneric(*(stcMDNS_RRAnswerGeneric*&)p_rpRRAnswer, u16RDLength); - break; - } - DEBUG_EX_INFO( - if ((bResult) && - (p_rpRRAnswer)) - { - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswer: ")); - _printRRDomain(p_rpRRAnswer->m_Header.m_Domain); - DEBUG_OUTPUT.printf_P(PSTR(" Type:0x%04X Class:0x%04X TTL:%u, RDLength:%u "), p_rpRRAnswer->m_Header.m_Attributes.m_u16Type, p_rpRRAnswer->m_Header.m_Attributes.m_u16Class, p_rpRRAnswer->m_u32TTL, u16RDLength); - switch (header.m_Attributes.m_u16Type & (~0x8000)) // Topmost bit might carry 'cache flush' flag + /* DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswer: Reading 0x%04X answer + (class:0x%04X, TTL:%u, RDLength:%u) for "), header.m_Attributes.m_u16Type, + header.m_Attributes.m_u16Class, u32TTL, u16RDLength); + _printRRDomain(header.m_Domain); + DEBUG_OUTPUT.printf_P(PSTR("\n")); + );*/ + + switch (header.m_Attributes.m_u16Type + & (~0x8000)) // Topmost bit might carry 'cache flush' flag { #ifdef MDNS_IP4_SUPPORT case DNS_RRTYPE_A: - DEBUG_OUTPUT.printf_P(PSTR("A IP:%s"), ((stcMDNS_RRAnswerA*&)p_rpRRAnswer)->m_IPAddress.toString().c_str()); + p_rpRRAnswer = new stcMDNS_RRAnswerA(header, u32TTL); + bResult = _readRRAnswerA(*(stcMDNS_RRAnswerA*&)p_rpRRAnswer, u16RDLength); break; #endif case DNS_RRTYPE_PTR: - DEBUG_OUTPUT.printf_P(PSTR("PTR ")); - _printRRDomain(((stcMDNS_RRAnswerPTR*&)p_rpRRAnswer)->m_PTRDomain); + p_rpRRAnswer = new stcMDNS_RRAnswerPTR(header, u32TTL); + bResult = _readRRAnswerPTR(*(stcMDNS_RRAnswerPTR*&)p_rpRRAnswer, u16RDLength); break; case DNS_RRTYPE_TXT: - { - size_t stTxtLength = ((stcMDNS_RRAnswerTXT*&)p_rpRRAnswer)->m_Txts.c_strLength(); - char* pTxts = new char[stTxtLength]; - if (pTxts) - { - ((stcMDNS_RRAnswerTXT*&)p_rpRRAnswer)->m_Txts.c_str(pTxts); - DEBUG_OUTPUT.printf_P(PSTR("TXT(%u) %s"), stTxtLength, pTxts); - delete[] pTxts; - } + p_rpRRAnswer = new stcMDNS_RRAnswerTXT(header, u32TTL); + bResult = _readRRAnswerTXT(*(stcMDNS_RRAnswerTXT*&)p_rpRRAnswer, u16RDLength); break; - } #ifdef MDNS_IP6_SUPPORT case DNS_RRTYPE_AAAA: - DEBUG_OUTPUT.printf_P(PSTR("AAAA IP:%s"), ((stcMDNS_RRAnswerA*&)p_rpRRAnswer)->m_IPAddress.toString().c_str()); + p_rpRRAnswer = new stcMDNS_RRAnswerAAAA(header, u32TTL); + bResult = _readRRAnswerAAAA(*(stcMDNS_RRAnswerAAAA*&)p_rpRRAnswer, u16RDLength); break; #endif case DNS_RRTYPE_SRV: - DEBUG_OUTPUT.printf_P(PSTR("SRV Port:%u "), ((stcMDNS_RRAnswerSRV*&)p_rpRRAnswer)->m_u16Port); - _printRRDomain(((stcMDNS_RRAnswerSRV*&)p_rpRRAnswer)->m_SRVDomain); + p_rpRRAnswer = new stcMDNS_RRAnswerSRV(header, u32TTL); + bResult = _readRRAnswerSRV(*(stcMDNS_RRAnswerSRV*&)p_rpRRAnswer, u16RDLength); break; default: - DEBUG_OUTPUT.printf_P(PSTR("generic ")); + p_rpRRAnswer = new stcMDNS_RRAnswerGeneric(header, u32TTL); + bResult + = _readRRAnswerGeneric(*(stcMDNS_RRAnswerGeneric*&)p_rpRRAnswer, u16RDLength); break; } - DEBUG_OUTPUT.printf_P(PSTR("\n")); - } - else - { - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswer: FAILED to read specific answer of type 0x%04X!\n"), p_rpRRAnswer->m_Header.m_Attributes.m_u16Type); + DEBUG_EX_INFO( + if ((bResult) && (p_rpRRAnswer)) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswer: ")); + _printRRDomain(p_rpRRAnswer->m_Header.m_Domain); + DEBUG_OUTPUT.printf_P(PSTR(" Type:0x%04X Class:0x%04X TTL:%u, RDLength:%u "), + p_rpRRAnswer->m_Header.m_Attributes.m_u16Type, + p_rpRRAnswer->m_Header.m_Attributes.m_u16Class, + p_rpRRAnswer->m_u32TTL, u16RDLength); + switch (header.m_Attributes.m_u16Type + & (~0x8000)) // Topmost bit might carry 'cache flush' flag + { +#ifdef MDNS_IP4_SUPPORT + case DNS_RRTYPE_A: + DEBUG_OUTPUT.printf_P( + PSTR("A IP:%s"), + ((stcMDNS_RRAnswerA*&)p_rpRRAnswer)->m_IPAddress.toString().c_str()); + break; +#endif + case DNS_RRTYPE_PTR: + DEBUG_OUTPUT.printf_P(PSTR("PTR ")); + _printRRDomain(((stcMDNS_RRAnswerPTR*&)p_rpRRAnswer)->m_PTRDomain); + break; + case DNS_RRTYPE_TXT: + { + size_t stTxtLength + = ((stcMDNS_RRAnswerTXT*&)p_rpRRAnswer)->m_Txts.c_strLength(); + char* pTxts = new char[stTxtLength]; + if (pTxts) + { + ((stcMDNS_RRAnswerTXT*&)p_rpRRAnswer)->m_Txts.c_str(pTxts); + DEBUG_OUTPUT.printf_P(PSTR("TXT(%zu) %s"), stTxtLength, pTxts); + delete[] pTxts; + } + break; + } +#ifdef MDNS_IP6_SUPPORT + case DNS_RRTYPE_AAAA: + DEBUG_OUTPUT.printf_P( + PSTR("AAAA IP:%s"), + ((stcMDNS_RRAnswerA*&)p_rpRRAnswer)->m_IPAddress.toString().c_str()); + break; +#endif + case DNS_RRTYPE_SRV: + DEBUG_OUTPUT.printf_P(PSTR("SRV Port:%u "), + ((stcMDNS_RRAnswerSRV*&)p_rpRRAnswer)->m_u16Port); + _printRRDomain(((stcMDNS_RRAnswerSRV*&)p_rpRRAnswer)->m_SRVDomain); + break; + default: + DEBUG_OUTPUT.printf_P(PSTR("generic ")); + break; + } + DEBUG_OUTPUT.printf_P(PSTR("\n")); + } else { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswer: FAILED to read " + "specific answer of type 0x%04X!\n"), + p_rpRRAnswer->m_Header.m_Attributes.m_u16Type); + }); // DEBUG_EX_INFO } - ); // DEBUG_EX_INFO + DEBUG_EX_ERR(if (!bResult) + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswer: FAILED!\n"));); + return bResult; } - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswer: FAILED!\n"));); - return bResult; -} #ifdef MDNS_IP4_SUPPORT -/* - MDNSResponder::_readRRAnswerA -*/ -bool MDNSResponder::_readRRAnswerA(MDNSResponder::stcMDNS_RRAnswerA& p_rRRAnswerA, - uint16_t p_u16RDLength) -{ - - uint32_t u32IP4Address; - bool bResult = ((MDNS_IP4_SIZE == p_u16RDLength) && - (_udpReadBuffer((unsigned char*)&u32IP4Address, MDNS_IP4_SIZE)) && - ((p_rRRAnswerA.m_IPAddress = IPAddress(u32IP4Address)))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerA: FAILED!\n"));); - return bResult; -} + /* + MDNSResponder::_readRRAnswerA + */ + bool MDNSResponder::_readRRAnswerA(MDNSResponder::stcMDNS_RRAnswerA& p_rRRAnswerA, + uint16_t p_u16RDLength) + { + uint32_t u32IP4Address; + bool bResult = ((MDNS_IP4_SIZE == p_u16RDLength) + && (_udpReadBuffer((unsigned char*)&u32IP4Address, MDNS_IP4_SIZE)) + && ((p_rRRAnswerA.m_IPAddress = IPAddress(u32IP4Address)))); + DEBUG_EX_ERR(if (!bResult) + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerA: FAILED!\n"));); + return bResult; + } #endif -/* - MDNSResponder::_readRRAnswerPTR -*/ -bool MDNSResponder::_readRRAnswerPTR(MDNSResponder::stcMDNS_RRAnswerPTR& p_rRRAnswerPTR, - uint16_t p_u16RDLength) -{ - - bool bResult = ((p_u16RDLength) && - (_readRRDomain(p_rRRAnswerPTR.m_PTRDomain))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerPTR: FAILED!\n"));); - return bResult; -} - -/* - MDNSResponder::_readRRAnswerTXT + /* + MDNSResponder::_readRRAnswerPTR + */ + bool MDNSResponder::_readRRAnswerPTR(MDNSResponder::stcMDNS_RRAnswerPTR& p_rRRAnswerPTR, + uint16_t p_u16RDLength) + { + bool bResult = ((p_u16RDLength) && (_readRRDomain(p_rRRAnswerPTR.m_PTRDomain))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _readRRAnswerPTR: FAILED!\n"));); + return bResult; + } - Read TXT items from a buffer like 4c#=15ff=20 -*/ -bool MDNSResponder::_readRRAnswerTXT(MDNSResponder::stcMDNS_RRAnswerTXT& p_rRRAnswerTXT, - uint16_t p_u16RDLength) -{ - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: RDLength:%u\n"), p_u16RDLength);); - bool bResult = true; + /* + MDNSResponder::_readRRAnswerTXT - p_rRRAnswerTXT.clear(); - if (p_u16RDLength) + Read TXT items from a buffer like 4c#=15ff=20 + */ + bool MDNSResponder::_readRRAnswerTXT(MDNSResponder::stcMDNS_RRAnswerTXT& p_rRRAnswerTXT, + uint16_t p_u16RDLength) { - bResult = false; + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: RDLength:%u\n"), + p_u16RDLength);); + bool bResult = true; - unsigned char* pucBuffer = new unsigned char[p_u16RDLength]; - if (pucBuffer) + p_rRRAnswerTXT.clear(); + if (p_u16RDLength) { - if (_udpReadBuffer(pucBuffer, p_u16RDLength)) - { - bResult = true; + bResult = false; - const unsigned char* pucCursor = pucBuffer; - while ((pucCursor < (pucBuffer + p_u16RDLength)) && - (bResult)) + unsigned char* pucBuffer = new unsigned char[p_u16RDLength]; + if (pucBuffer) + { + if (_udpReadBuffer(pucBuffer, p_u16RDLength)) { - bResult = false; + bResult = true; - stcMDNSServiceTxt* pTxt = 0; - unsigned char ucLength = *pucCursor++; // Length of the next txt item - if (ucLength) + const unsigned char* pucCursor = pucBuffer; + while ((pucCursor < (pucBuffer + p_u16RDLength)) && (bResult)) { - DEBUG_EX_INFO( - static char sacBuffer[64]; *sacBuffer = 0; - uint8_t u8MaxLength = ((ucLength > (sizeof(sacBuffer) - 1)) ? (sizeof(sacBuffer) - 1) : ucLength); - os_strncpy(sacBuffer, (const char*)pucCursor, u8MaxLength); sacBuffer[u8MaxLength] = 0; - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: Item(%u): %s\n"), ucLength, sacBuffer); - ); - - unsigned char* pucEqualSign = (unsigned char*)os_strchr((const char*)pucCursor, '='); // Position of the '=' sign - unsigned char ucKeyLength; - if ((pucEqualSign) && - ((ucKeyLength = (pucEqualSign - pucCursor)))) + bResult = false; + + stcMDNSServiceTxt* pTxt = 0; + unsigned char ucLength = *pucCursor++; // Length of the next txt item + if (ucLength) { - unsigned char ucValueLength = (ucLength - (pucEqualSign - pucCursor + 1)); - bResult = (((pTxt = new stcMDNSServiceTxt)) && - (pTxt->setKey((const char*)pucCursor, ucKeyLength)) && - (pTxt->setValue((const char*)(pucEqualSign + 1), ucValueLength))); + DEBUG_EX_INFO( + static char sacBuffer[64]; *sacBuffer = 0; + uint8_t u8MaxLength + = ((ucLength > (sizeof(sacBuffer) - 1)) ? (sizeof(sacBuffer) - 1) + : ucLength); + os_strncpy(sacBuffer, (const char*)pucCursor, u8MaxLength); + sacBuffer[u8MaxLength] = 0; DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _readRRAnswerTXT: Item(%u): %s\n"), + ucLength, sacBuffer);); + + unsigned char* pucEqualSign = (unsigned char*)os_strchr( + (const char*)pucCursor, '='); // Position of the '=' sign + unsigned char ucKeyLength; + if ((pucEqualSign) && ((ucKeyLength = (pucEqualSign - pucCursor)))) + { + unsigned char ucValueLength + = (ucLength - (pucEqualSign - pucCursor + 1)); + bResult = (((pTxt = new stcMDNSServiceTxt)) + && (pTxt->setKey((const char*)pucCursor, ucKeyLength)) + && (pTxt->setValue((const char*)(pucEqualSign + 1), + ucValueLength))); + } + else + { + DEBUG_EX_ERR( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: " + "INVALID TXT format (No '=')!\n"));); + } + pucCursor += ucLength; } - else + else // no/zero length TXT { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: INVALID TXT format (No '=')!\n"));); + DEBUG_EX_INFO( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: TXT " + "answer contains no items.\n"));); + bResult = true; } - pucCursor += ucLength; - } - else // no/zero length TXT - { - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: TXT answer contains no items.\n"));); - bResult = true; - } - if ((bResult) && - (pTxt)) // Everythings fine so far - { - // Link TXT item to answer TXTs - pTxt->m_pNext = p_rRRAnswerTXT.m_Txts.m_pTxts; - p_rRRAnswerTXT.m_Txts.m_pTxts = pTxt; - } - else // At least no TXT (migth be OK, if length was 0) OR an error - { - if (!bResult) + if ((bResult) && (pTxt)) // Everything is fine so far { - DEBUG_EX_ERR( - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: FAILED to read TXT item!\n")); - DEBUG_OUTPUT.printf_P(PSTR("RData dump:\n")); - _udpDump((m_pUDPContext->tell() - p_u16RDLength), p_u16RDLength); - DEBUG_OUTPUT.printf_P(PSTR("\n")); - ); + // Link TXT item to answer TXTs + pTxt->m_pNext = p_rRRAnswerTXT.m_Txts.m_pTxts; + p_rRRAnswerTXT.m_Txts.m_pTxts = pTxt; } - if (pTxt) + else // At least no TXT (might be OK, if length was 0) OR an error { - delete pTxt; - pTxt = 0; + if (!bResult) + { + DEBUG_EX_ERR( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: " + "FAILED to read TXT item!\n")); + DEBUG_OUTPUT.printf_P(PSTR("RData dump:\n")); _udpDump( + (m_pUDPContext->tell() - p_u16RDLength), p_u16RDLength); + DEBUG_OUTPUT.printf_P(PSTR("\n"));); + } + if (pTxt) + { + delete pTxt; + pTxt = 0; + } + p_rRRAnswerTXT.clear(); } - p_rRRAnswerTXT.clear(); - } - } // while - - DEBUG_EX_ERR( - if (!bResult) // Some failure - { - DEBUG_OUTPUT.printf_P(PSTR("RData dump:\n")); - _udpDump((m_pUDPContext->tell() - p_u16RDLength), p_u16RDLength); - DEBUG_OUTPUT.printf_P(PSTR("\n")); + } // while + + DEBUG_EX_ERR(if (!bResult) // Some failure + { + DEBUG_OUTPUT.printf_P(PSTR("RData dump:\n")); + _udpDump((m_pUDPContext->tell() - p_u16RDLength), + p_u16RDLength); + DEBUG_OUTPUT.printf_P(PSTR("\n")); + }); + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _readRRAnswerTXT: FAILED to read TXT content!\n"));); } - ); + // Clean up + delete[] pucBuffer; } else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: FAILED to read TXT content!\n"));); + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: FAILED " + "to alloc buffer for TXT content!\n"));); } - // Clean up - delete[] pucBuffer; } else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: FAILED to alloc buffer for TXT content!\n"));); + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _readRRAnswerTXT: WARNING! No content!\n"));); } + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _readRRAnswerTXT: FAILED!\n"));); + return bResult; } - else - { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: WARNING! No content!\n"));); - } - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerTXT: FAILED!\n"));); - return bResult; -} #ifdef MDNS_IP6_SUPPORT -bool MDNSResponder::_readRRAnswerAAAA(MDNSResponder::stcMDNS_RRAnswerAAAA& p_rRRAnswerAAAA, - uint16_t p_u16RDLength) -{ - bool bResult = false; - // TODO: Implement - return bResult; -} + bool MDNSResponder::_readRRAnswerAAAA(MDNSResponder::stcMDNS_RRAnswerAAAA& p_rRRAnswerAAAA, + uint16_t p_u16RDLength) + { + bool bResult = false; + // TODO: Implement + return bResult; + } #endif -/* - MDNSResponder::_readRRAnswerSRV -*/ -bool MDNSResponder::_readRRAnswerSRV(MDNSResponder::stcMDNS_RRAnswerSRV& p_rRRAnswerSRV, - uint16_t p_u16RDLength) -{ + /* + MDNSResponder::_readRRAnswerSRV + */ + bool MDNSResponder::_readRRAnswerSRV(MDNSResponder::stcMDNS_RRAnswerSRV& p_rRRAnswerSRV, + uint16_t p_u16RDLength) + { + bool bResult + = (((3 * sizeof(uint16_t)) < p_u16RDLength) + && (_udpRead16(p_rRRAnswerSRV.m_u16Priority)) + && (_udpRead16(p_rRRAnswerSRV.m_u16Weight)) && (_udpRead16(p_rRRAnswerSRV.m_u16Port)) + && (_readRRDomain(p_rRRAnswerSRV.m_SRVDomain))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _readRRAnswerSRV: FAILED!\n"));); + return bResult; + } - bool bResult = (((3 * sizeof(uint16_t)) < p_u16RDLength) && - (_udpRead16(p_rRRAnswerSRV.m_u16Priority)) && - (_udpRead16(p_rRRAnswerSRV.m_u16Weight)) && - (_udpRead16(p_rRRAnswerSRV.m_u16Port)) && - (_readRRDomain(p_rRRAnswerSRV.m_SRVDomain))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerSRV: FAILED!\n"));); - return bResult; -} + /* + MDNSResponder::_readRRAnswerGeneric + */ + bool + MDNSResponder::_readRRAnswerGeneric(MDNSResponder::stcMDNS_RRAnswerGeneric& p_rRRAnswerGeneric, + uint16_t p_u16RDLength) + { + bool bResult = (0 == p_u16RDLength); -/* - MDNSResponder::_readRRAnswerGeneric -*/ -bool MDNSResponder::_readRRAnswerGeneric(MDNSResponder::stcMDNS_RRAnswerGeneric& p_rRRAnswerGeneric, - uint16_t p_u16RDLength) -{ - bool bResult = (0 == p_u16RDLength); + p_rRRAnswerGeneric.clear(); + if (((p_rRRAnswerGeneric.m_u16RDLength = p_u16RDLength)) + && ((p_rRRAnswerGeneric.m_pu8RDData + = new unsigned char[p_rRRAnswerGeneric.m_u16RDLength]))) + { + bResult + = _udpReadBuffer(p_rRRAnswerGeneric.m_pu8RDData, p_rRRAnswerGeneric.m_u16RDLength); + } + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _readRRAnswerGeneric: FAILED!\n"));); + return bResult; + } - p_rRRAnswerGeneric.clear(); - if (((p_rRRAnswerGeneric.m_u16RDLength = p_u16RDLength)) && - ((p_rRRAnswerGeneric.m_pu8RDData = new unsigned char[p_rRRAnswerGeneric.m_u16RDLength]))) + /* + MDNSResponder::_readRRHeader + */ + bool MDNSResponder::_readRRHeader(MDNSResponder::stcMDNS_RRHeader& p_rRRHeader) { + // DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRHeader\n"));); - bResult = _udpReadBuffer(p_rRRAnswerGeneric.m_pu8RDData, p_rRRAnswerGeneric.m_u16RDLength); + bool bResult = ((_readRRDomain(p_rRRHeader.m_Domain)) + && (_readRRAttributes(p_rRRHeader.m_Attributes))); + DEBUG_EX_ERR(if (!bResult) + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRHeader: FAILED!\n"));); + return bResult; } - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAnswerGeneric: FAILED!\n"));); - return bResult; -} -/* - MDNSResponder::_readRRHeader -*/ -bool MDNSResponder::_readRRHeader(MDNSResponder::stcMDNS_RRHeader& p_rRRHeader) -{ - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRHeader\n"));); + /* + MDNSResponder::_readRRDomain - bool bResult = ((_readRRDomain(p_rRRHeader.m_Domain)) && - (_readRRAttributes(p_rRRHeader.m_Attributes))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRHeader: FAILED!\n"));); - return bResult; -} + Reads a (maybe multilevel compressed) domain from the UDP input buffer. -/* - MDNSResponder::_readRRDomain + */ + bool MDNSResponder::_readRRDomain(MDNSResponder::stcMDNS_RRDomain& p_rRRDomain) + { + // DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain\n"));); - Reads a (maybe multilevel compressed) domain from the UDP input buffer. + bool bResult = ((p_rRRDomain.clear()) && (_readRRDomain_Loop(p_rRRDomain, 0))); + DEBUG_EX_ERR(if (!bResult) + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain: FAILED!\n"));); + return bResult; + } -*/ -bool MDNSResponder::_readRRDomain(MDNSResponder::stcMDNS_RRDomain& p_rRRDomain) -{ - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain\n"));); + /* + MDNSResponder::_readRRDomain_Loop - bool bResult = ((p_rRRDomain.clear()) && - (_readRRDomain_Loop(p_rRRDomain, 0))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain: FAILED!\n"));); - return bResult; -} + Reads a domain from the UDP input buffer. For every compression level, the functions + calls itself recursively. To avoid endless recursion because of malformed MDNS records, + the maximum recursion depth is set by MDNS_DOMAIN_MAX_REDIRCTION. -/* - MDNSResponder::_readRRDomain_Loop - - Reads a domain from the UDP input buffer. For every compression level, the functions - calls itself recursively. To avoid endless recursion because of malformed MDNS records, - the maximum recursion depth is set by MDNS_DOMAIN_MAX_REDIRCTION. - -*/ -bool MDNSResponder::_readRRDomain_Loop(MDNSResponder::stcMDNS_RRDomain& p_rRRDomain, - uint8_t p_u8Depth) -{ - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u)\n"), p_u8Depth);); - - bool bResult = false; - - if (MDNS_DOMAIN_MAX_REDIRCTION >= p_u8Depth) + */ + bool MDNSResponder::_readRRDomain_Loop(MDNSResponder::stcMDNS_RRDomain& p_rRRDomain, + uint8_t p_u8Depth) { - bResult = true; + // DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u)\n"), + // p_u8Depth);); + + bool bResult = false; - uint8_t u8Len = 0; - do + if (MDNS_DOMAIN_MAX_REDIRCTION >= p_u8Depth) { - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): Offset:%u p0:%02x\n"), p_u8Depth, m_pUDPContext->tell(), m_pUDPContext->peek());); - _udpRead8(u8Len); + bResult = true; - if (u8Len & MDNS_DOMAIN_COMPRESS_MARK) + uint8_t u8Len = 0; + do { - // Compressed label(s) - uint16_t u16Offset = ((u8Len & ~MDNS_DOMAIN_COMPRESS_MARK) << 8); // Implicit BE to LE conversion! + // DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): + // Offset:%u p0:%02x\n"), p_u8Depth, m_pUDPContext->tell(), + // m_pUDPContext->peek());); _udpRead8(u8Len); - u16Offset |= u8Len; - if (m_pUDPContext->isValidOffset(u16Offset)) + if (u8Len & MDNS_DOMAIN_COMPRESS_MARK) { - size_t stCurrentPosition = m_pUDPContext->tell(); // Prepare return from recursion + // Compressed label(s) + uint16_t u16Offset = ((u8Len & ~MDNS_DOMAIN_COMPRESS_MARK) + << 8); // Implicit BE to LE conversion! + _udpRead8(u8Len); + u16Offset |= u8Len; - //DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): Redirecting from %u to %u!\n"), p_u8Depth, stCurrentPosition, u16Offset);); - m_pUDPContext->seek(u16Offset); - if (_readRRDomain_Loop(p_rRRDomain, p_u8Depth + 1)) // Do recursion + if (m_pUDPContext->isValidOffset(u16Offset)) { - //DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): Succeeded to read redirected label! Returning to %u\n"), p_u8Depth, stCurrentPosition);); - m_pUDPContext->seek(stCurrentPosition); // Restore after recursion + size_t stCurrentPosition + = m_pUDPContext->tell(); // Prepare return from recursion + + // DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] + // _readRRDomain_Loop(%u): Redirecting from %u to %u!\n"), p_u8Depth, + // stCurrentPosition, u16Offset);); + m_pUDPContext->seek(u16Offset); + if (_readRRDomain_Loop(p_rRRDomain, p_u8Depth + 1)) // Do recursion + { + // DEBUG_EX_RX(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] + // _readRRDomain_Loop(%u): Succeeded to read redirected label! Returning + // to %u\n"), p_u8Depth, stCurrentPosition);); + m_pUDPContext->seek(stCurrentPosition); // Restore after recursion + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _readRRDomain_Loop(%u): FAILED to read " + "redirected label!\n"), + p_u8Depth);); + bResult = false; + } } else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): FAILED to read redirected label!\n"), p_u8Depth);); + DEBUG_EX_ERR( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): " + "INVALID offset in redirection!\n"), + p_u8Depth);); bResult = false; } + break; } else { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): INVALID offset in redirection!\n"), p_u8Depth);); - bResult = false; - } - break; - } - else - { - // Normal (uncompressed) label (maybe '\0' only) - if (MDNS_DOMAIN_MAXLENGTH > (p_rRRDomain.m_u16NameLength + u8Len)) - { - // Add length byte - p_rRRDomain.m_acName[p_rRRDomain.m_u16NameLength] = u8Len; - ++(p_rRRDomain.m_u16NameLength); - if (u8Len) // Add name + // Normal (uncompressed) label (maybe '\0' only) + if (MDNS_DOMAIN_MAXLENGTH > (p_rRRDomain.m_u16NameLength + u8Len)) { - if ((bResult = _udpReadBuffer((unsigned char*) & (p_rRRDomain.m_acName[p_rRRDomain.m_u16NameLength]), u8Len))) + // Add length byte + p_rRRDomain.m_acName[p_rRRDomain.m_u16NameLength] = u8Len; + ++(p_rRRDomain.m_u16NameLength); + if (u8Len) // Add name { - /* DEBUG_EX_INFO( - p_rRRDomain.m_acName[p_rRRDomain.m_u16NameLength + u8Len] = 0; // Closing '\0' for printing - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): Domain label (%u): %s\n"), p_u8Depth, (unsigned)(p_rRRDomain.m_acName[p_rRRDomain.m_u16NameLength - 1]), &(p_rRRDomain.m_acName[p_rRRDomain.m_u16NameLength])); - );*/ - - p_rRRDomain.m_u16NameLength += u8Len; + if ((bResult = _udpReadBuffer( + (unsigned char*)&( + p_rRRDomain.m_acName[p_rRRDomain.m_u16NameLength]), + u8Len))) + { + /* DEBUG_EX_INFO( + p_rRRDomain.m_acName[p_rRRDomain.m_u16NameLength + u8Len] = + 0; // Closing '\0' for printing + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] + _readRRDomain_Loop(%u): Domain label (%u): %s\n"), p_u8Depth, + (unsigned)(p_rRRDomain.m_acName[p_rRRDomain.m_u16NameLength - + 1]), &(p_rRRDomain.m_acName[p_rRRDomain.m_u16NameLength])); + );*/ + + p_rRRDomain.m_u16NameLength += u8Len; + } } + // DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] + // _readRRDomain_Loop(2) offset:%u p0:%x\n"), m_pUDPContext->tell(), + // m_pUDPContext->peek());); + } + else + { + DEBUG_EX_ERR( + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): " + "ERROR! Domain name too long (%u + %u)!\n"), + p_u8Depth, p_rRRDomain.m_u16NameLength, u8Len);); + bResult = false; + break; } - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(2) offset:%u p0:%x\n"), m_pUDPContext->tell(), m_pUDPContext->peek());); - } - else - { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): ERROR! Domain name too long (%u + %u)!\n"), p_u8Depth, p_rRRDomain.m_u16NameLength, u8Len);); - bResult = false; - break; } - } - } while ((bResult) && - (0 != u8Len)); - } - else - { - DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRDomain_Loop(%u): ERROR! Too many redirections!\n"), p_u8Depth);); + } while ((bResult) && (0 != u8Len)); + } + else + { + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _readRRDomain_Loop(%u): ERROR! Too many redirections!\n"), + p_u8Depth);); + } + return bResult; } - return bResult; -} - -/* - MDNSResponder::_readRRAttributes -*/ -bool MDNSResponder::_readRRAttributes(MDNSResponder::stcMDNS_RRAttributes& p_rRRAttributes) -{ - //DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAttributes\n"));); - - bool bResult = ((_udpRead16(p_rRRAttributes.m_u16Type)) && - (_udpRead16(p_rRRAttributes.m_u16Class))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAttributes: FAILED!\n"));); - return bResult; -} - -/* - DOMAIN NAMES -*/ - -/* - MDNSResponder::_buildDomainForHost - - Builds a MDNS host domain (eg. esp8266.local) for the given hostname. + /* + MDNSResponder::_readRRAttributes + */ + bool MDNSResponder::_readRRAttributes(MDNSResponder::stcMDNS_RRAttributes& p_rRRAttributes) + { + // DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readRRAttributes\n"));); -*/ -bool MDNSResponder::_buildDomainForHost(const char* p_pcHostname, - MDNSResponder::stcMDNS_RRDomain& p_rHostDomain) const -{ + bool bResult + = ((_udpRead16(p_rRRAttributes.m_u16Type)) && (_udpRead16(p_rRRAttributes.m_u16Class))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _readRRAttributes: FAILED!\n"));); + return bResult; + } - p_rHostDomain.clear(); - bool bResult = ((p_pcHostname) && - (*p_pcHostname) && - (p_rHostDomain.addLabel(p_pcHostname)) && - (p_rHostDomain.addLabel(scpcLocal)) && - (p_rHostDomain.addLabel(0))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _buildDomainForHost: FAILED!\n"));); - return bResult; -} + /* + DOMAIN NAMES + */ -/* - MDNSResponder::_buildDomainForDNSSD + /* + MDNSResponder::_buildDomainForHost - Builds the '_services._dns-sd._udp.local' domain. - Used while detecting generic service enum question (DNS-SD) and answering these questions. + Builds a MDNS host domain (eg. esp8266.local) for the given hostname. -*/ -bool MDNSResponder::_buildDomainForDNSSD(MDNSResponder::stcMDNS_RRDomain& p_rDNSSDDomain) const -{ + */ + bool MDNSResponder::_buildDomainForHost(const char* p_pcHostname, + MDNSResponder::stcMDNS_RRDomain& p_rHostDomain) const + { + p_rHostDomain.clear(); + bool bResult = ((p_pcHostname) && (*p_pcHostname) && (p_rHostDomain.addLabel(p_pcHostname)) + && (p_rHostDomain.addLabel(scpcLocal)) && (p_rHostDomain.addLabel(0))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _buildDomainForHost: FAILED!\n"));); + return bResult; + } - p_rDNSSDDomain.clear(); - bool bResult = ((p_rDNSSDDomain.addLabel(scpcServices, true)) && - (p_rDNSSDDomain.addLabel(scpcDNSSD, true)) && - (p_rDNSSDDomain.addLabel(scpcUDP, true)) && - (p_rDNSSDDomain.addLabel(scpcLocal)) && - (p_rDNSSDDomain.addLabel(0))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _buildDomainForDNSSD: FAILED!\n"));); - return bResult; -} + /* + MDNSResponder::_buildDomainForDNSSD -/* - MDNSResponder::_buildDomainForService + Builds the '_services._dns-sd._udp.local' domain. + Used while detecting generic service enum question (DNS-SD) and answering these questions. - Builds the domain for the given service (eg. _http._tcp.local or - MyESP._http._tcp.local (if p_bIncludeName is set)). + */ + bool MDNSResponder::_buildDomainForDNSSD(MDNSResponder::stcMDNS_RRDomain& p_rDNSSDDomain) const + { + p_rDNSSDDomain.clear(); + bool bResult = ((p_rDNSSDDomain.addLabel(scpcServices, true)) + && (p_rDNSSDDomain.addLabel(scpcDNSSD, true)) + && (p_rDNSSDDomain.addLabel(scpcUDP, true)) + && (p_rDNSSDDomain.addLabel(scpcLocal)) && (p_rDNSSDDomain.addLabel(0))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _buildDomainForDNSSD: FAILED!\n"));); + return bResult; + } -*/ -bool MDNSResponder::_buildDomainForService(const MDNSResponder::stcMDNSService& p_Service, - bool p_bIncludeName, - MDNSResponder::stcMDNS_RRDomain& p_rServiceDomain) const -{ + /* + MDNSResponder::_buildDomainForService - p_rServiceDomain.clear(); - bool bResult = (((!p_bIncludeName) || - (p_rServiceDomain.addLabel(p_Service.m_pcName))) && - (p_rServiceDomain.addLabel(p_Service.m_pcService, true)) && - (p_rServiceDomain.addLabel(p_Service.m_pcProtocol, true)) && - (p_rServiceDomain.addLabel(scpcLocal)) && - (p_rServiceDomain.addLabel(0))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _buildDomainForService: FAILED!\n"));); - return bResult; -} + Builds the domain for the given service (eg. _http._tcp.local or + MyESP._http._tcp.local (if p_bIncludeName is set)). -/* - MDNSResponder::_buildDomainForService + */ + bool + MDNSResponder::_buildDomainForService(const MDNSResponder::stcMDNSService& p_Service, + bool p_bIncludeName, + MDNSResponder::stcMDNS_RRDomain& p_rServiceDomain) const + { + p_rServiceDomain.clear(); + bool bResult + = (((!p_bIncludeName) || (p_rServiceDomain.addLabel(p_Service.m_pcName))) + && (p_rServiceDomain.addLabel(p_Service.m_pcService, true)) + && (p_rServiceDomain.addLabel(p_Service.m_pcProtocol, true)) + && (p_rServiceDomain.addLabel(scpcLocal)) && (p_rServiceDomain.addLabel(0))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _buildDomainForService: FAILED!\n"));); + return bResult; + } - Builds the domain for the given service properties (eg. _http._tcp.local). - The usual prepended '_' are added, if missing in the input strings. + /* + MDNSResponder::_buildDomainForService -*/ -bool MDNSResponder::_buildDomainForService(const char* p_pcService, - const char* p_pcProtocol, - MDNSResponder::stcMDNS_RRDomain& p_rServiceDomain) const -{ + Builds the domain for the given service properties (eg. _http._tcp.local). + The usual prepended '_' are added, if missing in the input strings. - p_rServiceDomain.clear(); - bool bResult = ((p_pcService) && - (p_pcProtocol) && - (p_rServiceDomain.addLabel(p_pcService, ('_' != *p_pcService))) && - (p_rServiceDomain.addLabel(p_pcProtocol, ('_' != *p_pcProtocol))) && - (p_rServiceDomain.addLabel(scpcLocal)) && - (p_rServiceDomain.addLabel(0))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _buildDomainForService: FAILED for (%s.%s)!\n"), (p_pcService ? : "-"), (p_pcProtocol ? : "-"));); - return bResult; -} + */ + bool + MDNSResponder::_buildDomainForService(const char* p_pcService, const char* p_pcProtocol, + MDNSResponder::stcMDNS_RRDomain& p_rServiceDomain) const + { + p_rServiceDomain.clear(); + bool bResult + = ((p_pcService) && (p_pcProtocol) + && (p_rServiceDomain.addLabel(p_pcService, ('_' != *p_pcService))) + && (p_rServiceDomain.addLabel(p_pcProtocol, ('_' != *p_pcProtocol))) + && (p_rServiceDomain.addLabel(scpcLocal)) && (p_rServiceDomain.addLabel(0))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _buildDomainForService: FAILED for (%s.%s)!\n"), + (p_pcService ?: "-"), (p_pcProtocol ?: "-"));); + return bResult; + } #ifdef MDNS_IP4_SUPPORT -/* - MDNSResponder::_buildDomainForReverseIP4 + /* + MDNSResponder::_buildDomainForReverseIP4 - The IP4 address is stringized by printing the four address bytes into a char buffer in reverse order - and adding 'in-addr.arpa' (eg. 012.789.456.123.in-addr.arpa). - Used while detecting reverse IP4 questions and answering these -*/ -bool MDNSResponder::_buildDomainForReverseIP4(IPAddress p_IP4Address, - MDNSResponder::stcMDNS_RRDomain& p_rReverseIP4Domain) const -{ - - bool bResult = true; + The IP4 address is stringized by printing the four address bytes into a char buffer in + reverse order and adding 'in-addr.arpa' (eg. 012.789.456.123.in-addr.arpa). Used while + detecting reverse IP4 questions and answering these + */ + bool MDNSResponder::_buildDomainForReverseIP4( + IPAddress p_IP4Address, MDNSResponder::stcMDNS_RRDomain& p_rReverseIP4Domain) const + { + bool bResult = true; - p_rReverseIP4Domain.clear(); + p_rReverseIP4Domain.clear(); - char acBuffer[32]; - for (int i = MDNS_IP4_SIZE; ((bResult) && (i >= 1)); --i) - { - itoa(p_IP4Address[i - 1], acBuffer, 10); - bResult = p_rReverseIP4Domain.addLabel(acBuffer); + char acBuffer[32]; + for (int i = MDNS_IP4_SIZE; ((bResult) && (i >= 1)); --i) + { + itoa(p_IP4Address[i - 1], acBuffer, 10); + bResult = p_rReverseIP4Domain.addLabel(acBuffer); + } + bResult = ((bResult) && (p_rReverseIP4Domain.addLabel(scpcReverseIP4Domain)) + && (p_rReverseIP4Domain.addLabel(scpcReverseTopDomain)) + && (p_rReverseIP4Domain.addLabel(0))); + DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] _buildDomainForReverseIP4: FAILED!\n"));); + return bResult; } - bResult = ((bResult) && - (p_rReverseIP4Domain.addLabel(scpcReverseIP4Domain)) && - (p_rReverseIP4Domain.addLabel(scpcReverseTopDomain)) && - (p_rReverseIP4Domain.addLabel(0))); - DEBUG_EX_ERR(if (!bResult) DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _buildDomainForReverseIP4: FAILED!\n"));); - return bResult; -} #endif #ifdef MDNS_IP6_SUPPORT -/* - MDNSResponder::_buildDomainForReverseIP6 - - Used while detecting reverse IP6 questions and answering these -*/ -bool MDNSResponder::_buildDomainForReverseIP6(IPAddress p_IP4Address, - MDNSResponder::stcMDNS_RRDomain& p_rReverseIP6Domain) const -{ - // TODO: Implement - return false; -} -#endif - - -/* - UDP -*/ - -/* - MDNSResponder::_udpReadBuffer -*/ -bool MDNSResponder::_udpReadBuffer(unsigned char* p_pBuffer, - size_t p_stLength) -{ - - bool bResult = ((m_pUDPContext) && - (true/*m_pUDPContext->getSize() > p_stLength*/) && - (p_pBuffer) && - (p_stLength) && - ((p_stLength == m_pUDPContext->read((char*)p_pBuffer, p_stLength)))); - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _udpReadBuffer: FAILED!\n")); - }); - return bResult; -} - -/* - MDNSResponder::_udpRead8 -*/ -bool MDNSResponder::_udpRead8(uint8_t& p_ru8Value) -{ - - return _udpReadBuffer((unsigned char*)&p_ru8Value, sizeof(p_ru8Value)); -} - -/* - MDNSResponder::_udpRead16 -*/ -bool MDNSResponder::_udpRead16(uint16_t& p_ru16Value) -{ - - bool bResult = false; + /* + MDNSResponder::_buildDomainForReverseIP6 - if (_udpReadBuffer((unsigned char*)&p_ru16Value, sizeof(p_ru16Value))) + Used while detecting reverse IP6 questions and answering these + */ + bool MDNSResponder::_buildDomainForReverseIP6( + IPAddress p_IP4Address, MDNSResponder::stcMDNS_RRDomain& p_rReverseIP6Domain) const { - p_ru16Value = lwip_ntohs(p_ru16Value); - bResult = true; + // TODO: Implement + return false; } - return bResult; -} - -/* - MDNSResponder::_udpRead32 -*/ -bool MDNSResponder::_udpRead32(uint32_t& p_ru32Value) -{ +#endif - bool bResult = false; + /* + UDP + */ - if (_udpReadBuffer((unsigned char*)&p_ru32Value, sizeof(p_ru32Value))) + /* + MDNSResponder::_udpReadBuffer + */ + bool MDNSResponder::_udpReadBuffer(unsigned char* p_pBuffer, size_t p_stLength) { - p_ru32Value = lwip_ntohl(p_ru32Value); - bResult = true; + bool bResult = ((m_pUDPContext) && (true /*m_pUDPContext->getSize() > p_stLength*/) + && (p_pBuffer) && (p_stLength) + && ((p_stLength == m_pUDPContext->read((char*)p_pBuffer, p_stLength)))); + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _udpReadBuffer: FAILED!\n")); + }); + return bResult; } - return bResult; -} - -/* - MDNSResponder::_udpAppendBuffer -*/ -bool MDNSResponder::_udpAppendBuffer(const unsigned char* p_pcBuffer, - size_t p_stLength) -{ - - bool bResult = ((m_pUDPContext) && - (p_pcBuffer) && - (p_stLength) && - (p_stLength == m_pUDPContext->append((const char*)p_pcBuffer, p_stLength))); - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _udpAppendBuffer: FAILED!\n")); - }); - return bResult; -} - -/* - MDNSResponder::_udpAppend8 -*/ -bool MDNSResponder::_udpAppend8(uint8_t p_u8Value) -{ - - return (_udpAppendBuffer((unsigned char*)&p_u8Value, sizeof(p_u8Value))); -} -/* - MDNSResponder::_udpAppend16 -*/ -bool MDNSResponder::_udpAppend16(uint16_t p_u16Value) -{ - - p_u16Value = lwip_htons(p_u16Value); - return (_udpAppendBuffer((unsigned char*)&p_u16Value, sizeof(p_u16Value))); -} - -/* - MDNSResponder::_udpAppend32 -*/ -bool MDNSResponder::_udpAppend32(uint32_t p_u32Value) -{ - - p_u32Value = lwip_htonl(p_u32Value); - return (_udpAppendBuffer((unsigned char*)&p_u32Value, sizeof(p_u32Value))); -} - -#ifdef DEBUG_ESP_MDNS_RESPONDER -/* - MDNSResponder::_udpDump -*/ -bool MDNSResponder::_udpDump(bool p_bMovePointer /*= false*/) -{ - - const uint8_t cu8BytesPerLine = 16; - - uint32_t u32StartPosition = m_pUDPContext->tell(); - DEBUG_OUTPUT.println("UDP Context Dump:"); - uint32_t u32Counter = 0; - uint8_t u8Byte = 0; - - while (_udpRead8(u8Byte)) + /* + MDNSResponder::_udpRead8 + */ + bool MDNSResponder::_udpRead8(uint8_t& p_ru8Value) { - DEBUG_OUTPUT.printf_P(PSTR("%02x %s"), u8Byte, ((++u32Counter % cu8BytesPerLine) ? "" : "\n")); + return _udpReadBuffer((unsigned char*)&p_ru8Value, sizeof(p_ru8Value)); } - DEBUG_OUTPUT.printf_P(PSTR("%sDone: %u bytes\n"), (((u32Counter) && (u32Counter % cu8BytesPerLine)) ? "\n" : ""), u32Counter); - if (!p_bMovePointer) // Restore + /* + MDNSResponder::_udpRead16 + */ + bool MDNSResponder::_udpRead16(uint16_t& p_ru16Value) { - m_pUDPContext->seek(u32StartPosition); - } - return true; -} + bool bResult = false; -/* - MDNSResponder::_udpDump -*/ -bool MDNSResponder::_udpDump(unsigned p_uOffset, - unsigned p_uLength) -{ + if (_udpReadBuffer((unsigned char*)&p_ru16Value, sizeof(p_ru16Value))) + { + p_ru16Value = lwip_ntohs(p_ru16Value); + bResult = true; + } + return bResult; + } - if ((m_pUDPContext) && - (m_pUDPContext->isValidOffset(p_uOffset))) + /* + MDNSResponder::_udpRead32 + */ + bool MDNSResponder::_udpRead32(uint32_t& p_ru32Value) { - unsigned uCurrentPosition = m_pUDPContext->tell(); // Remember start position + bool bResult = false; - m_pUDPContext->seek(p_uOffset); - uint8_t u8Byte; - for (unsigned u = 0; ((u < p_uLength) && (_udpRead8(u8Byte))); ++u) + if (_udpReadBuffer((unsigned char*)&p_ru32Value, sizeof(p_ru32Value))) { - DEBUG_OUTPUT.printf_P(PSTR("%02x "), u8Byte); + p_ru32Value = lwip_ntohl(p_ru32Value); + bResult = true; } - // Return to start position - m_pUDPContext->seek(uCurrentPosition); + return bResult; } - return true; -} -#endif - - -/** - READ/WRITE MDNS STRUCTS -*/ - -/* - MDNSResponder::_readMDNSMsgHeader - Read a MDNS header from the UDP input buffer. - | 8 | 8 | 8 | 8 | - 00| Identifier | Flags & Codes | - 01| Question count | Answer count | - 02| NS answer count | Ad answer count | + /* + MDNSResponder::_udpAppendBuffer + */ + bool MDNSResponder::_udpAppendBuffer(const unsigned char* p_pcBuffer, size_t p_stLength) + { + bool bResult + = ((m_pUDPContext) && (p_pcBuffer) && (p_stLength) + && (p_stLength == m_pUDPContext->append((const char*)p_pcBuffer, p_stLength))); + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _udpAppendBuffer: FAILED!\n")); + }); + return bResult; + } - All 16-bit and 32-bit elements need to be translated form network coding to host coding (done in _udpRead16 and _udpRead32) - In addition, bitfield memory order is undefined in C standard (GCC doesn't order them in the coded direction...), so they - need some mapping here -*/ -bool MDNSResponder::_readMDNSMsgHeader(MDNSResponder::stcMDNS_MsgHeader& p_rMsgHeader) -{ + /* + MDNSResponder::_udpAppend8 + */ + bool MDNSResponder::_udpAppend8(uint8_t p_u8Value) + { + return (_udpAppendBuffer((unsigned char*)&p_u8Value, sizeof(p_u8Value))); + } - bool bResult = false; - - uint8_t u8B1; - uint8_t u8B2; - if ((_udpRead16(p_rMsgHeader.m_u16ID)) && - (_udpRead8(u8B1)) && - (_udpRead8(u8B2)) && - (_udpRead16(p_rMsgHeader.m_u16QDCount)) && - (_udpRead16(p_rMsgHeader.m_u16ANCount)) && - (_udpRead16(p_rMsgHeader.m_u16NSCount)) && - (_udpRead16(p_rMsgHeader.m_u16ARCount))) + /* + MDNSResponder::_udpAppend16 + */ + bool MDNSResponder::_udpAppend16(uint16_t p_u16Value) { + p_u16Value = lwip_htons(p_u16Value); + return (_udpAppendBuffer((unsigned char*)&p_u16Value, sizeof(p_u16Value))); + } - p_rMsgHeader.m_1bQR = (u8B1 & 0x80); // Query/Responde flag - p_rMsgHeader.m_4bOpcode = (u8B1 & 0x78); // Operation code (0: Standard query, others ignored) - p_rMsgHeader.m_1bAA = (u8B1 & 0x04); // Authorative answer - p_rMsgHeader.m_1bTC = (u8B1 & 0x02); // Truncation flag - p_rMsgHeader.m_1bRD = (u8B1 & 0x01); // Recursion desired - - p_rMsgHeader.m_1bRA = (u8B2 & 0x80); // Recursion available - p_rMsgHeader.m_3bZ = (u8B2 & 0x70); // Zero - p_rMsgHeader.m_4bRCode = (u8B2 & 0x0F); // Response code - - /* DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readMDNSMsgHeader: ID:%u QR:%u OP:%u AA:%u TC:%u RD:%u RA:%u R:%u QD:%u AN:%u NS:%u AR:%u\n"), - (unsigned)p_rMsgHeader.m_u16ID, - (unsigned)p_rMsgHeader.m_1bQR, (unsigned)p_rMsgHeader.m_4bOpcode, (unsigned)p_rMsgHeader.m_1bAA, (unsigned)p_rMsgHeader.m_1bTC, (unsigned)p_rMsgHeader.m_1bRD, - (unsigned)p_rMsgHeader.m_1bRA, (unsigned)p_rMsgHeader.m_4bRCode, - (unsigned)p_rMsgHeader.m_u16QDCount, - (unsigned)p_rMsgHeader.m_u16ANCount, - (unsigned)p_rMsgHeader.m_u16NSCount, - (unsigned)p_rMsgHeader.m_u16ARCount););*/ - bResult = true; + /* + MDNSResponder::_udpAppend32 + */ + bool MDNSResponder::_udpAppend32(uint32_t p_u32Value) + { + p_u32Value = lwip_htonl(p_u32Value); + return (_udpAppendBuffer((unsigned char*)&p_u32Value, sizeof(p_u32Value))); } - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readMDNSMsgHeader: FAILED!\n")); - }); - return bResult; -} -/* - MDNSResponder::_write8 -*/ -bool MDNSResponder::_write8(uint8_t p_u8Value, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) -{ +#ifdef DEBUG_ESP_MDNS_RESPONDER + /* + MDNSResponder::_udpDump + */ + bool MDNSResponder::_udpDump(bool p_bMovePointer /*= false*/) + { + const uint8_t cu8BytesPerLine = 16; - return ((_udpAppend8(p_u8Value)) && - (p_rSendParameter.shiftOffset(sizeof(p_u8Value)))); -} + uint32_t u32StartPosition = m_pUDPContext->tell(); + DEBUG_OUTPUT.println("UDP Context Dump:"); + uint32_t u32Counter = 0; + uint8_t u8Byte = 0; -/* - MDNSResponder::_write16 -*/ -bool MDNSResponder::_write16(uint16_t p_u16Value, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) -{ + while (_udpRead8(u8Byte)) + { + DEBUG_OUTPUT.printf_P(PSTR("%02x %s"), u8Byte, + ((++u32Counter % cu8BytesPerLine) ? "" : "\n")); + } + DEBUG_OUTPUT.printf_P(PSTR("%sDone: %u bytes\n"), + (((u32Counter) && (u32Counter % cu8BytesPerLine)) ? "\n" : ""), + u32Counter); - return ((_udpAppend16(p_u16Value)) && - (p_rSendParameter.shiftOffset(sizeof(p_u16Value)))); -} + if (!p_bMovePointer) // Restore + { + m_pUDPContext->seek(u32StartPosition); + } + return true; + } -/* - MDNSResponder::_write32 -*/ -bool MDNSResponder::_write32(uint32_t p_u32Value, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) -{ + /* + MDNSResponder::_udpDump + */ + bool MDNSResponder::_udpDump(unsigned p_uOffset, unsigned p_uLength) + { + if ((m_pUDPContext) && (m_pUDPContext->isValidOffset(p_uOffset))) + { + unsigned uCurrentPosition = m_pUDPContext->tell(); // Remember start position - return ((_udpAppend32(p_u32Value)) && - (p_rSendParameter.shiftOffset(sizeof(p_u32Value)))); -} + m_pUDPContext->seek(p_uOffset); + uint8_t u8Byte; + for (unsigned u = 0; ((u < p_uLength) && (_udpRead8(u8Byte))); ++u) + { + DEBUG_OUTPUT.printf_P(PSTR("%02x "), u8Byte); + } + // Return to start position + m_pUDPContext->seek(uCurrentPosition); + } + return true; + } +#endif -/* - MDNSResponder::_writeMDNSMsgHeader + /** + READ/WRITE MDNS STRUCTS + */ - Write MDNS header to the UDP output buffer. + /* + MDNSResponder::_readMDNSMsgHeader - All 16-bit and 32-bit elements need to be translated form host coding to network coding (done in _udpAppend16 and _udpAppend32) - In addition, bitfield memory order is undefined in C standard (GCC doesn't order them in the coded direction...), so they - need some mapping here -*/ -bool MDNSResponder::_writeMDNSMsgHeader(const MDNSResponder::stcMDNS_MsgHeader& p_MsgHeader, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) -{ - /* DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSMsgHeader: ID:%u QR:%u OP:%u AA:%u TC:%u RD:%u RA:%u R:%u QD:%u AN:%u NS:%u AR:%u\n"), - (unsigned)p_MsgHeader.m_u16ID, - (unsigned)p_MsgHeader.m_1bQR, (unsigned)p_MsgHeader.m_4bOpcode, (unsigned)p_MsgHeader.m_1bAA, (unsigned)p_MsgHeader.m_1bTC, (unsigned)p_MsgHeader.m_1bRD, - (unsigned)p_MsgHeader.m_1bRA, (unsigned)p_MsgHeader.m_4bRCode, - (unsigned)p_MsgHeader.m_u16QDCount, - (unsigned)p_MsgHeader.m_u16ANCount, - (unsigned)p_MsgHeader.m_u16NSCount, - (unsigned)p_MsgHeader.m_u16ARCount););*/ - - uint8_t u8B1((p_MsgHeader.m_1bQR << 7) | (p_MsgHeader.m_4bOpcode << 3) | (p_MsgHeader.m_1bAA << 2) | (p_MsgHeader.m_1bTC << 1) | (p_MsgHeader.m_1bRD)); - uint8_t u8B2((p_MsgHeader.m_1bRA << 7) | (p_MsgHeader.m_3bZ << 4) | (p_MsgHeader.m_4bRCode)); - bool bResult = ((_write16(p_MsgHeader.m_u16ID, p_rSendParameter)) && - (_write8(u8B1, p_rSendParameter)) && - (_write8(u8B2, p_rSendParameter)) && - (_write16(p_MsgHeader.m_u16QDCount, p_rSendParameter)) && - (_write16(p_MsgHeader.m_u16ANCount, p_rSendParameter)) && - (_write16(p_MsgHeader.m_u16NSCount, p_rSendParameter)) && - (_write16(p_MsgHeader.m_u16ARCount, p_rSendParameter))); - - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSMsgHeader: FAILED!\n")); - }); - return bResult; -} + Read a MDNS header from the UDP input buffer. + | 8 | 8 | 8 | 8 | + 00| Identifier | Flags & Codes | + 01| Question count | Answer count | + 02| NS answer count | Ad answer count | -/* - MDNSResponder::_writeRRAttributes -*/ -bool MDNSResponder::_writeMDNSRRAttributes(const MDNSResponder::stcMDNS_RRAttributes& p_Attributes, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) -{ + All 16-bit and 32-bit elements need to be translated form network coding to host coding + (done in _udpRead16 and _udpRead32) In addition, bitfield memory order is undefined in C + standard (GCC doesn't order them in the coded direction...), so they need some mapping here + */ + bool MDNSResponder::_readMDNSMsgHeader(MDNSResponder::stcMDNS_MsgHeader& p_rMsgHeader) + { + bool bResult = false; - bool bResult = ((_write16(p_Attributes.m_u16Type, p_rSendParameter)) && - (_write16(p_Attributes.m_u16Class, p_rSendParameter))); + uint8_t u8B1; + uint8_t u8B2; + if ((_udpRead16(p_rMsgHeader.m_u16ID)) && (_udpRead8(u8B1)) && (_udpRead8(u8B2)) + && (_udpRead16(p_rMsgHeader.m_u16QDCount)) && (_udpRead16(p_rMsgHeader.m_u16ANCount)) + && (_udpRead16(p_rMsgHeader.m_u16NSCount)) && (_udpRead16(p_rMsgHeader.m_u16ARCount))) + { + p_rMsgHeader.m_1bQR = (u8B1 & 0x80); // Query/Respond flag + p_rMsgHeader.m_4bOpcode + = (u8B1 & 0x78); // Operation code (0: Standard query, others ignored) + p_rMsgHeader.m_1bAA = (u8B1 & 0x04); // Authoritative answer + p_rMsgHeader.m_1bTC = (u8B1 & 0x02); // Truncation flag + p_rMsgHeader.m_1bRD = (u8B1 & 0x01); // Recursion desired + + p_rMsgHeader.m_1bRA = (u8B2 & 0x80); // Recursion available + p_rMsgHeader.m_3bZ = (u8B2 & 0x70); // Zero + p_rMsgHeader.m_4bRCode = (u8B2 & 0x0F); // Response code + + /* DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readMDNSMsgHeader: ID:%u + QR:%u OP:%u AA:%u TC:%u RD:%u RA:%u R:%u QD:%u AN:%u NS:%u AR:%u\n"), + (unsigned)p_rMsgHeader.m_u16ID, + (unsigned)p_rMsgHeader.m_1bQR, (unsigned)p_rMsgHeader.m_4bOpcode, + (unsigned)p_rMsgHeader.m_1bAA, (unsigned)p_rMsgHeader.m_1bTC, + (unsigned)p_rMsgHeader.m_1bRD, (unsigned)p_rMsgHeader.m_1bRA, + (unsigned)p_rMsgHeader.m_4bRCode, (unsigned)p_rMsgHeader.m_u16QDCount, + (unsigned)p_rMsgHeader.m_u16ANCount, + (unsigned)p_rMsgHeader.m_u16NSCount, + (unsigned)p_rMsgHeader.m_u16ARCount););*/ + bResult = true; + } + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _readMDNSMsgHeader: FAILED!\n")); + }); + return bResult; + } - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSRRAttributes: FAILED!\n")); - }); - return bResult; -} + /* + MDNSResponder::_write8 + */ + bool MDNSResponder::_write8(uint8_t p_u8Value, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) + { + return ((_udpAppend8(p_u8Value)) && (p_rSendParameter.shiftOffset(sizeof(p_u8Value)))); + } -/* - MDNSResponder::_writeMDNSRRDomain -*/ -bool MDNSResponder::_writeMDNSRRDomain(const MDNSResponder::stcMDNS_RRDomain& p_Domain, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) -{ + /* + MDNSResponder::_write16 + */ + bool MDNSResponder::_write16(uint16_t p_u16Value, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) + { + return ((_udpAppend16(p_u16Value)) && (p_rSendParameter.shiftOffset(sizeof(p_u16Value)))); + } - bool bResult = ((_udpAppendBuffer((const unsigned char*)p_Domain.m_acName, p_Domain.m_u16NameLength)) && - (p_rSendParameter.shiftOffset(p_Domain.m_u16NameLength))); + /* + MDNSResponder::_write32 + */ + bool MDNSResponder::_write32(uint32_t p_u32Value, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) + { + return ((_udpAppend32(p_u32Value)) && (p_rSendParameter.shiftOffset(sizeof(p_u32Value)))); + } - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSRRDomain: FAILED!\n")); - }); - return bResult; -} + /* + MDNSResponder::_writeMDNSMsgHeader -/* - MDNSResponder::_writeMDNSHostDomain + Write MDNS header to the UDP output buffer. - Write a host domain to the UDP output buffer. - If the domain record is part of the answer, the records length is - prepended (p_bPrependRDLength is set). + All 16-bit and 32-bit elements need to be translated form host coding to network coding + (done in _udpAppend16 and _udpAppend32) In addition, bitfield memory order is undefined in C + standard (GCC doesn't order them in the coded direction...), so they need some mapping here + */ + bool MDNSResponder::_writeMDNSMsgHeader(const MDNSResponder::stcMDNS_MsgHeader& p_MsgHeader, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) + { + /* DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSMsgHeader: ID:%u + QR:%u OP:%u AA:%u TC:%u RD:%u RA:%u R:%u QD:%u AN:%u NS:%u AR:%u\n"), + (unsigned)p_MsgHeader.m_u16ID, + (unsigned)p_MsgHeader.m_1bQR, (unsigned)p_MsgHeader.m_4bOpcode, + (unsigned)p_MsgHeader.m_1bAA, (unsigned)p_MsgHeader.m_1bTC, (unsigned)p_MsgHeader.m_1bRD, + (unsigned)p_MsgHeader.m_1bRA, (unsigned)p_MsgHeader.m_4bRCode, + (unsigned)p_MsgHeader.m_u16QDCount, + (unsigned)p_MsgHeader.m_u16ANCount, + (unsigned)p_MsgHeader.m_u16NSCount, + (unsigned)p_MsgHeader.m_u16ARCount););*/ + + uint8_t u8B1((p_MsgHeader.m_1bQR << 7) | (p_MsgHeader.m_4bOpcode << 3) + | (p_MsgHeader.m_1bAA << 2) | (p_MsgHeader.m_1bTC << 1) + | (p_MsgHeader.m_1bRD)); + uint8_t u8B2((p_MsgHeader.m_1bRA << 7) | (p_MsgHeader.m_3bZ << 4) + | (p_MsgHeader.m_4bRCode)); + bool bResult = ((_write16(p_MsgHeader.m_u16ID, p_rSendParameter)) + && (_write8(u8B1, p_rSendParameter)) && (_write8(u8B2, p_rSendParameter)) + && (_write16(p_MsgHeader.m_u16QDCount, p_rSendParameter)) + && (_write16(p_MsgHeader.m_u16ANCount, p_rSendParameter)) + && (_write16(p_MsgHeader.m_u16NSCount, p_rSendParameter)) + && (_write16(p_MsgHeader.m_u16ARCount, p_rSendParameter))); + + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSMsgHeader: FAILED!\n")); + }); + return bResult; + } - A very simple form of name compression is applied here: - If the domain is written to the UDP output buffer, the write offset is stored - together with a domain id (the pointer) in a p_rSendParameter substructure (cache). - If the same domain (pointer) should be written to the UDP output later again, - the old offset is retrieved from the cache, marked as a compressed domain offset - and written to the output buffer. + /* + MDNSResponder::_writeRRAttributes + */ + bool + MDNSResponder::_writeMDNSRRAttributes(const MDNSResponder::stcMDNS_RRAttributes& p_Attributes, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) + { + bool bResult = ((_write16(p_Attributes.m_u16Type, p_rSendParameter)) + && (_write16(p_Attributes.m_u16Class, p_rSendParameter))); -*/ -bool MDNSResponder::_writeMDNSHostDomain(const char* p_pcHostname, - bool p_bPrependRDLength, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) -{ + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSRRAttributes: FAILED!\n")); + }); + return bResult; + } - // The 'skip-compression' version is handled in '_writeMDNSAnswer_SRV' - uint16_t u16CachedDomainOffset = p_rSendParameter.findCachedDomainOffset((const void*)p_pcHostname, false); - - stcMDNS_RRDomain hostDomain; - bool bResult = (u16CachedDomainOffset - // Found cached domain -> mark as compressed domain - ? ((MDNS_DOMAIN_COMPRESS_MARK > ((u16CachedDomainOffset >> 8) & ~MDNS_DOMAIN_COMPRESS_MARK)) && // Valid offset - ((!p_bPrependRDLength) || - (_write16(2, p_rSendParameter))) && // Length of 'Cxxx' - (_write8(((u16CachedDomainOffset >> 8) | MDNS_DOMAIN_COMPRESS_MARK), p_rSendParameter)) && // Compression mark (and offset) - (_write8((uint8_t)(u16CachedDomainOffset & 0xFF), p_rSendParameter))) - // No cached domain -> add this domain to cache and write full domain name - : ((_buildDomainForHost(p_pcHostname, hostDomain)) && // eg. esp8266.local - ((!p_bPrependRDLength) || - (_write16(hostDomain.m_u16NameLength, p_rSendParameter))) && // RDLength (if needed) - (p_rSendParameter.addDomainCacheItem((const void*)p_pcHostname, false, p_rSendParameter.m_u16Offset)) && - (_writeMDNSRRDomain(hostDomain, p_rSendParameter)))); - - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSHostDomain: FAILED!\n")); - }); - return bResult; + /* + MDNSResponder::_writeMDNSRRDomain + */ + bool MDNSResponder::_writeMDNSRRDomain(const MDNSResponder::stcMDNS_RRDomain& p_Domain, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) + { + bool bResult + = ((_udpAppendBuffer((const unsigned char*)p_Domain.m_acName, p_Domain.m_u16NameLength)) + && (p_rSendParameter.shiftOffset(p_Domain.m_u16NameLength))); -} + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSRRDomain: FAILED!\n")); + }); + return bResult; + } -/* - MDNSResponder::_writeMDNSServiceDomain + /* + MDNSResponder::_writeMDNSHostDomain - Write a service domain to the UDP output buffer. - If the domain record is part of the answer, the records length is - prepended (p_bPrependRDLength is set). + Write a host domain to the UDP output buffer. + If the domain record is part of the answer, the records length is + prepended (p_bPrependRDLength is set). - A very simple form of name compression is applied here: see '_writeMDNSHostDomain' - The cache differentiates of course between service domains which includes - the instance name (p_bIncludeName is set) and thoose who don't. + A very simple form of name compression is applied here: + If the domain is written to the UDP output buffer, the write offset is stored + together with a domain id (the pointer) in a p_rSendParameter substructure (cache). + If the same domain (pointer) should be written to the UDP output later again, + the old offset is retrieved from the cache, marked as a compressed domain offset + and written to the output buffer. -*/ -bool MDNSResponder::_writeMDNSServiceDomain(const MDNSResponder::stcMDNSService& p_Service, - bool p_bIncludeName, - bool p_bPrependRDLength, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) -{ + */ + bool MDNSResponder::_writeMDNSHostDomain(const char* p_pcHostname, bool p_bPrependRDLength, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) + { + // The 'skip-compression' version is handled in '_writeMDNSAnswer_SRV' + uint16_t u16CachedDomainOffset + = p_rSendParameter.findCachedDomainOffset((const void*)p_pcHostname, false); + + stcMDNS_RRDomain hostDomain; + bool bResult + = (u16CachedDomainOffset + // Found cached domain -> mark as compressed domain + ? ((MDNS_DOMAIN_COMPRESS_MARK + > ((u16CachedDomainOffset >> 8) & ~MDNS_DOMAIN_COMPRESS_MARK)) + && // Valid offset + ((!p_bPrependRDLength) || (_write16(2, p_rSendParameter))) + && // Length of 'Cxxx' + (_write8(((u16CachedDomainOffset >> 8) | MDNS_DOMAIN_COMPRESS_MARK), + p_rSendParameter)) + && // Compression mark (and offset) + (_write8((uint8_t)(u16CachedDomainOffset & 0xFF), p_rSendParameter))) + // No cached domain -> add this domain to cache and write full domain name + : ((_buildDomainForHost(p_pcHostname, hostDomain)) && // eg. esp8266.local + ((!p_bPrependRDLength) + || (_write16(hostDomain.m_u16NameLength, p_rSendParameter))) + && // RDLength (if needed) + (p_rSendParameter.addDomainCacheItem((const void*)p_pcHostname, false, + p_rSendParameter.m_u16Offset)) + && (_writeMDNSRRDomain(hostDomain, p_rSendParameter)))); + + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSHostDomain: FAILED!\n")); + }); + return bResult; + } - // The 'skip-compression' version is handled in '_writeMDNSAnswer_SRV' - uint16_t u16CachedDomainOffset = p_rSendParameter.findCachedDomainOffset((const void*)&p_Service, p_bIncludeName); - - stcMDNS_RRDomain serviceDomain; - bool bResult = (u16CachedDomainOffset - // Found cached domain -> mark as compressed domain - ? ((MDNS_DOMAIN_COMPRESS_MARK > ((u16CachedDomainOffset >> 8) & ~MDNS_DOMAIN_COMPRESS_MARK)) && // Valid offset - ((!p_bPrependRDLength) || - (_write16(2, p_rSendParameter))) && // Lenght of 'Cxxx' - (_write8(((u16CachedDomainOffset >> 8) | MDNS_DOMAIN_COMPRESS_MARK), p_rSendParameter)) && // Compression mark (and offset) - (_write8((uint8_t)(u16CachedDomainOffset & 0xFF), p_rSendParameter))) - // No cached domain -> add this domain to cache and write full domain name - : ((_buildDomainForService(p_Service, p_bIncludeName, serviceDomain)) && // eg. MyESP._http._tcp.local - ((!p_bPrependRDLength) || - (_write16(serviceDomain.m_u16NameLength, p_rSendParameter))) && // RDLength (if needed) - (p_rSendParameter.addDomainCacheItem((const void*)&p_Service, p_bIncludeName, p_rSendParameter.m_u16Offset)) && - (_writeMDNSRRDomain(serviceDomain, p_rSendParameter)))); - - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSServiceDomain: FAILED!\n")); - }); - return bResult; + /* + MDNSResponder::_writeMDNSServiceDomain -} + Write a service domain to the UDP output buffer. + If the domain record is part of the answer, the records length is + prepended (p_bPrependRDLength is set). -/* - MDNSResponder::_writeMDNSQuestion + A very simple form of name compression is applied here: see '_writeMDNSHostDomain' + The cache differentiates of course between service domains which includes + the instance name (p_bIncludeName is set) and thoose who don't. - Write a MDNS question to the UDP output buffer + */ + bool + MDNSResponder::_writeMDNSServiceDomain(const MDNSResponder::stcMDNSService& p_Service, + bool p_bIncludeName, bool p_bPrependRDLength, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) + { + // The 'skip-compression' version is handled in '_writeMDNSAnswer_SRV' + uint16_t u16CachedDomainOffset + = p_rSendParameter.findCachedDomainOffset((const void*)&p_Service, p_bIncludeName); + + stcMDNS_RRDomain serviceDomain; + bool bResult + = (u16CachedDomainOffset + // Found cached domain -> mark as compressed domain + ? ((MDNS_DOMAIN_COMPRESS_MARK + > ((u16CachedDomainOffset >> 8) & ~MDNS_DOMAIN_COMPRESS_MARK)) + && // Valid offset + ((!p_bPrependRDLength) || (_write16(2, p_rSendParameter))) + && // Length of 'Cxxx' + (_write8(((u16CachedDomainOffset >> 8) | MDNS_DOMAIN_COMPRESS_MARK), + p_rSendParameter)) + && // Compression mark (and offset) + (_write8((uint8_t)(u16CachedDomainOffset & 0xFF), p_rSendParameter))) + // No cached domain -> add this domain to cache and write full domain name + : ((_buildDomainForService(p_Service, p_bIncludeName, serviceDomain)) + && // eg. MyESP._http._tcp.local + ((!p_bPrependRDLength) + || (_write16(serviceDomain.m_u16NameLength, p_rSendParameter))) + && // RDLength (if needed) + (p_rSendParameter.addDomainCacheItem((const void*)&p_Service, p_bIncludeName, + p_rSendParameter.m_u16Offset)) + && (_writeMDNSRRDomain(serviceDomain, p_rSendParameter)))); + + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSServiceDomain: FAILED!\n")); + }); + return bResult; + } - QNAME (host/service domain, eg. esp8266.local) - QTYPE (16bit, eg. ANY) - QCLASS (16bit, eg. IN) + /* + MDNSResponder::_writeMDNSQuestion -*/ -bool MDNSResponder::_writeMDNSQuestion(MDNSResponder::stcMDNS_RRQuestion& p_Question, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) -{ - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSQuestion\n"));); + Write a MDNS question to the UDP output buffer - bool bResult = ((_writeMDNSRRDomain(p_Question.m_Header.m_Domain, p_rSendParameter)) && - (_writeMDNSRRAttributes(p_Question.m_Header.m_Attributes, p_rSendParameter))); + QNAME (host/service domain, eg. esp8266.local) + QTYPE (16bit, eg. ANY) + QCLASS (16bit, eg. IN) - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSQuestion: FAILED!\n")); - }); - return bResult; + */ + bool MDNSResponder::_writeMDNSQuestion(MDNSResponder::stcMDNS_RRQuestion& p_Question, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSQuestion\n"));); -} + bool bResult + = ((_writeMDNSRRDomain(p_Question.m_Header.m_Domain, p_rSendParameter)) + && (_writeMDNSRRAttributes(p_Question.m_Header.m_Attributes, p_rSendParameter))); + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSQuestion: FAILED!\n")); + }); + return bResult; + } #ifdef MDNS_IP4_SUPPORT -/* - MDNSResponder::_writeMDNSAnswer_A + /* + MDNSResponder::_writeMDNSAnswer_A - Write a MDNS A answer to the UDP output buffer. + Write a MDNS A answer to the UDP output buffer. - NAME (var, host/service domain, eg. esp8266.local - TYPE (16bit, eg. A) - CLASS (16bit, eg. IN) - TTL (32bit, eg. 120) - RDLENGTH (16bit, eg 4) - RDATA (var, eg. 123.456.789.012) + NAME (var, host/service domain, eg. esp8266.local + TYPE (16bit, eg. A) + CLASS (16bit, eg. IN) + TTL (32bit, eg. 120) + RDLENGTH (16bit, eg 4) + RDATA (var, eg. 123.456.789.012) - eg. esp8266.local A 0x8001 120 4 123.456.789.012 - Ref: http://www.zytrax.com/books/dns/ch8/a.html -*/ -bool MDNSResponder::_writeMDNSAnswer_A(IPAddress p_IPAddress, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) -{ - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_A (%s)\n"), p_IPAddress.toString().c_str());); - - stcMDNS_RRAttributes attributes(DNS_RRTYPE_A, - ((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet - const unsigned char aucIPAddress[MDNS_IP4_SIZE] = { p_IPAddress[0], p_IPAddress[1], p_IPAddress[2], p_IPAddress[3] }; - bool bResult = ((_writeMDNSHostDomain(m_pcHostname, false, p_rSendParameter)) && - (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS - (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_HOST_TTL), p_rSendParameter)) && // TTL - (_write16(MDNS_IP4_SIZE, p_rSendParameter)) && // RDLength - (_udpAppendBuffer(aucIPAddress, MDNS_IP4_SIZE)) && // RData - (p_rSendParameter.shiftOffset(MDNS_IP4_SIZE))); - - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_A: FAILED!\n")); - }); - return bResult; - -} + eg. esp8266.local A 0x8001 120 4 123.456.789.012 + Ref: http://www.zytrax.com/books/dns/ch8/a.html + */ + bool MDNSResponder::_writeMDNSAnswer_A(IPAddress p_IPAddress, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_A (%s)\n"), + p_IPAddress.toString().c_str());); + + stcMDNS_RRAttributes attributes(DNS_RRTYPE_A, + ((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) + | DNS_RRCLASS_IN)); // Cache flush? & INternet + const unsigned char aucIPAddress[MDNS_IP4_SIZE] + = { p_IPAddress[0], p_IPAddress[1], p_IPAddress[2], p_IPAddress[3] }; + bool bResult + = ((_writeMDNSHostDomain(m_pcHostname, false, p_rSendParameter)) + && (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS + (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_HOST_TTL), p_rSendParameter)) + && // TTL + (_write16(MDNS_IP4_SIZE, p_rSendParameter)) && // RDLength + (_udpAppendBuffer(aucIPAddress, MDNS_IP4_SIZE)) && // RData + (p_rSendParameter.shiftOffset(MDNS_IP4_SIZE))); + + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_A: FAILED!\n")); + }); + return bResult; + } -/* - MDNSResponder::_writeMDNSAnswer_PTR_IP4 + /* + MDNSResponder::_writeMDNSAnswer_PTR_IP4 - Write a MDNS reverse IP4 PTR answer to the UDP output buffer. - See: '_writeMDNSAnswer_A' + Write a MDNS reverse IP4 PTR answer to the UDP output buffer. + See: '_writeMDNSAnswer_A' - eg. 012.789.456.123.in-addr.arpa PTR 0x8001 120 15 esp8266.local - Used while answering reverse IP4 questions -*/ -bool MDNSResponder::_writeMDNSAnswer_PTR_IP4(IPAddress p_IPAddress, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) -{ - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_IP4 (%s)\n"), p_IPAddress.toString().c_str());); - - stcMDNS_RRDomain reverseIP4Domain; - stcMDNS_RRAttributes attributes(DNS_RRTYPE_PTR, - ((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet - stcMDNS_RRDomain hostDomain; - bool bResult = ((_buildDomainForReverseIP4(p_IPAddress, reverseIP4Domain)) && // 012.789.456.123.in-addr.arpa - (_writeMDNSRRDomain(reverseIP4Domain, p_rSendParameter)) && - (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS - (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_HOST_TTL), p_rSendParameter)) && // TTL - (_writeMDNSHostDomain(m_pcHostname, true, p_rSendParameter))); // RDLength & RData (host domain, eg. esp8266.local) - - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_IP4: FAILED!\n")); - }); - return bResult; -} + eg. 012.789.456.123.in-addr.arpa PTR 0x8001 120 15 esp8266.local + Used while answering reverse IP4 questions + */ + bool + MDNSResponder::_writeMDNSAnswer_PTR_IP4(IPAddress p_IPAddress, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_IP4 (%s)\n"), + p_IPAddress.toString().c_str());); + + stcMDNS_RRDomain reverseIP4Domain; + stcMDNS_RRAttributes attributes(DNS_RRTYPE_PTR, + ((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) + | DNS_RRCLASS_IN)); // Cache flush? & INternet + stcMDNS_RRDomain hostDomain; + bool bResult + = ((_buildDomainForReverseIP4(p_IPAddress, reverseIP4Domain)) + && // 012.789.456.123.in-addr.arpa + (_writeMDNSRRDomain(reverseIP4Domain, p_rSendParameter)) + && (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS + (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_HOST_TTL), p_rSendParameter)) + && // TTL + (_writeMDNSHostDomain( + m_pcHostname, true, + p_rSendParameter))); // RDLength & RData (host domain, eg. esp8266.local) + + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_IP4: FAILED!\n")); + }); + return bResult; + } #endif -/* - MDNSResponder::_writeMDNSAnswer_PTR_TYPE - - Write a MDNS PTR answer to the UDP output buffer. - See: '_writeMDNSAnswer_A' - - PTR all-services -> service type - eg. _services._dns-sd._udp.local PTR 0x8001 5400 xx _http._tcp.local - http://www.zytrax.com/books/dns/ch8/ptr.html -*/ -bool MDNSResponder::_writeMDNSAnswer_PTR_TYPE(MDNSResponder::stcMDNSService& p_rService, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) -{ - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_TYPE\n"));); - - stcMDNS_RRDomain dnssdDomain; - stcMDNS_RRDomain serviceDomain; - stcMDNS_RRAttributes attributes(DNS_RRTYPE_PTR, DNS_RRCLASS_IN); // No cache flush! only INternet - bool bResult = ((_buildDomainForDNSSD(dnssdDomain)) && // _services._dns-sd._udp.local - (_writeMDNSRRDomain(dnssdDomain, p_rSendParameter)) && - (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS - (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_SERVICE_TTL), p_rSendParameter)) && // TTL - (_writeMDNSServiceDomain(p_rService, false, true, p_rSendParameter))); // RDLength & RData (service domain, eg. _http._tcp.local) - - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_TYPE: FAILED!\n")); - }); - return bResult; -} - -/* - MDNSResponder::_writeMDNSAnswer_PTR_NAME + /* + MDNSResponder::_writeMDNSAnswer_PTR_TYPE - Write a MDNS PTR answer to the UDP output buffer. - See: '_writeMDNSAnswer_A' + Write a MDNS PTR answer to the UDP output buffer. + See: '_writeMDNSAnswer_A' - PTR service type -> service name - eg. _http.tcp.local PTR 0x8001 120 xx myESP._http._tcp.local - http://www.zytrax.com/books/dns/ch8/ptr.html -*/ -bool MDNSResponder::_writeMDNSAnswer_PTR_NAME(MDNSResponder::stcMDNSService& p_rService, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) -{ - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_NAME\n"));); + PTR all-services -> service type + eg. _services._dns-sd._udp.local PTR 0x8001 5400 xx _http._tcp.local + http://www.zytrax.com/books/dns/ch8/ptr.html + */ + bool + MDNSResponder::_writeMDNSAnswer_PTR_TYPE(MDNSResponder::stcMDNSService& p_rService, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_TYPE\n"));); + + stcMDNS_RRDomain dnssdDomain; + stcMDNS_RRDomain serviceDomain; + stcMDNS_RRAttributes attributes(DNS_RRTYPE_PTR, + DNS_RRCLASS_IN); // No cache flush! only INternet + bool bResult + = ((_buildDomainForDNSSD(dnssdDomain)) && // _services._dns-sd._udp.local + (_writeMDNSRRDomain(dnssdDomain, p_rSendParameter)) + && (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS + (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_SERVICE_TTL), p_rSendParameter)) + && // TTL + (_writeMDNSServiceDomain( + p_rService, false, true, + p_rSendParameter))); // RDLength & RData (service domain, eg. _http._tcp.local) + + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_TYPE: FAILED!\n")); + }); + return bResult; + } - stcMDNS_RRAttributes attributes(DNS_RRTYPE_PTR, DNS_RRCLASS_IN); // No cache flush! only INternet - bool bResult = ((_writeMDNSServiceDomain(p_rService, false, false, p_rSendParameter)) && // _http._tcp.local - (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS - (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_SERVICE_TTL), p_rSendParameter)) && // TTL - (_writeMDNSServiceDomain(p_rService, true, true, p_rSendParameter))); // RDLength & RData (service domain, eg. MyESP._http._tcp.local) + /* + MDNSResponder::_writeMDNSAnswer_PTR_NAME - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_NAME: FAILED!\n")); - }); - return bResult; -} + Write a MDNS PTR answer to the UDP output buffer. + See: '_writeMDNSAnswer_A' + PTR service type -> service name + eg. _http.tcp.local PTR 0x8001 120 xx myESP._http._tcp.local + http://www.zytrax.com/books/dns/ch8/ptr.html + */ + bool + MDNSResponder::_writeMDNSAnswer_PTR_NAME(MDNSResponder::stcMDNSService& p_rService, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_NAME\n"));); + + stcMDNS_RRAttributes attributes(DNS_RRTYPE_PTR, + DNS_RRCLASS_IN); // No cache flush! only INternet + bool bResult + = ((_writeMDNSServiceDomain(p_rService, false, false, p_rSendParameter)) + && // _http._tcp.local + (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS + (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_SERVICE_TTL), p_rSendParameter)) + && // TTL + (_writeMDNSServiceDomain(p_rService, true, true, + p_rSendParameter))); // RDLength & RData (service domain, + // eg. MyESP._http._tcp.local) + + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_NAME: FAILED!\n")); + }); + return bResult; + } -/* - MDNSResponder::_writeMDNSAnswer_TXT + /* + MDNSResponder::_writeMDNSAnswer_TXT - Write a MDNS TXT answer to the UDP output buffer. - See: '_writeMDNSAnswer_A' + Write a MDNS TXT answer to the UDP output buffer. + See: '_writeMDNSAnswer_A' - The TXT items in the RDATA block are 'length byte encoded': [len]vardata + The TXT items in the RDATA block are 'length byte encoded': [len]vardata - eg. myESP._http._tcp.local TXT 0x8001 120 4 c#=1 - http://www.zytrax.com/books/dns/ch8/txt.html -*/ -bool MDNSResponder::_writeMDNSAnswer_TXT(MDNSResponder::stcMDNSService& p_rService, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) -{ - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_TXT\n"));); - - bool bResult = false; + eg. myESP._http._tcp.local TXT 0x8001 120 4 c#=1 + http://www.zytrax.com/books/dns/ch8/txt.html + */ + bool MDNSResponder::_writeMDNSAnswer_TXT(MDNSResponder::stcMDNSService& p_rService, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_TXT\n"));); - stcMDNS_RRAttributes attributes(DNS_RRTYPE_TXT, - ((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet + bool bResult = false; - if ((_collectServiceTxts(p_rService)) && - (_writeMDNSServiceDomain(p_rService, true, false, p_rSendParameter)) && // MyESP._http._tcp.local - (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS - (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_SERVICE_TTL), p_rSendParameter)) && // TTL - (_write16(p_rService.m_Txts.length(), p_rSendParameter))) // RDLength - { + stcMDNS_RRAttributes attributes(DNS_RRTYPE_TXT, + ((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) + | DNS_RRCLASS_IN)); // Cache flush? & INternet - bResult = true; - // RData Txts - for (stcMDNSServiceTxt* pTxt = p_rService.m_Txts.m_pTxts; ((bResult) && (pTxt)); pTxt = pTxt->m_pNext) - { - unsigned char ucLengthByte = pTxt->length(); - bResult = ((_udpAppendBuffer((unsigned char*)&ucLengthByte, sizeof(ucLengthByte))) && // Length - (p_rSendParameter.shiftOffset(sizeof(ucLengthByte))) && - ((size_t)os_strlen(pTxt->m_pcKey) == m_pUDPContext->append(pTxt->m_pcKey, os_strlen(pTxt->m_pcKey))) && // Key - (p_rSendParameter.shiftOffset((size_t)os_strlen(pTxt->m_pcKey))) && - (1 == m_pUDPContext->append("=", 1)) && // = - (p_rSendParameter.shiftOffset(1)) && - ((!pTxt->m_pcValue) || - (((size_t)os_strlen(pTxt->m_pcValue) == m_pUDPContext->append(pTxt->m_pcValue, os_strlen(pTxt->m_pcValue))) && // Value - (p_rSendParameter.shiftOffset((size_t)os_strlen(pTxt->m_pcValue)))))); - - DEBUG_EX_ERR(if (!bResult) + if ((_collectServiceTxts(p_rService)) + && (_writeMDNSServiceDomain(p_rService, true, false, p_rSendParameter)) + && // MyESP._http._tcp.local + (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS + (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_SERVICE_TTL), p_rSendParameter)) + && // TTL + (_write16(p_rService.m_Txts.length(), p_rSendParameter))) // RDLength { - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_TXT: FAILED to write %sTxt %s=%s!\n"), (pTxt->m_bTemp ? "temp. " : ""), (pTxt->m_pcKey ? : "?"), (pTxt->m_pcValue ? : "?")); - }); + bResult = true; + // RData Txts + for (stcMDNSServiceTxt* pTxt = p_rService.m_Txts.m_pTxts; ((bResult) && (pTxt)); + pTxt = pTxt->m_pNext) + { + unsigned char ucLengthByte = pTxt->length(); + bResult = ((_udpAppendBuffer((unsigned char*)&ucLengthByte, sizeof(ucLengthByte))) + && // Length + (p_rSendParameter.shiftOffset(sizeof(ucLengthByte))) + && ((size_t)os_strlen(pTxt->m_pcKey) + == m_pUDPContext->append(pTxt->m_pcKey, os_strlen(pTxt->m_pcKey))) + && // Key + (p_rSendParameter.shiftOffset((size_t)os_strlen(pTxt->m_pcKey))) + && (1 == m_pUDPContext->append("=", 1)) && // = + (p_rSendParameter.shiftOffset(1)) + && ((!pTxt->m_pcValue) + || (((size_t)os_strlen(pTxt->m_pcValue) + == m_pUDPContext->append(pTxt->m_pcValue, + os_strlen(pTxt->m_pcValue))) + && // Value + (p_rSendParameter.shiftOffset( + (size_t)os_strlen(pTxt->m_pcValue)))))); + + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P( + PSTR( + "[MDNSResponder] _writeMDNSAnswer_TXT: FAILED to write %sTxt %s=%s!\n"), + (pTxt->m_bTemp ? "temp. " : ""), (pTxt->m_pcKey ?: "?"), + (pTxt->m_pcValue ?: "?")); + }); + } } - } - _releaseTempServiceTxts(p_rService); + _releaseTempServiceTxts(p_rService); - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_TXT: FAILED!\n")); - }); - return bResult; -} + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_TXT: FAILED!\n")); + }); + return bResult; + } #ifdef MDNS_IP6_SUPPORT -/* - MDNSResponder::_writeMDNSAnswer_AAAA - - Write a MDNS AAAA answer to the UDP output buffer. - See: '_writeMDNSAnswer_A' - - eg. esp8266.local AAAA 0x8001 120 16 xxxx::xx - http://www.zytrax.com/books/dns/ch8/aaaa.html -*/ -bool MDNSResponder::_writeMDNSAnswer_AAAA(IPAddress p_IPAddress, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) -{ - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_AAAA\n"));); + /* + MDNSResponder::_writeMDNSAnswer_AAAA - 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 + Write a MDNS AAAA answer to the UDP output buffer. + See: '_writeMDNSAnswer_A' - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_AAAA: FAILED!\n")); - }); - return bResult; -} + eg. esp8266.local AAAA 0x8001 120 16 xxxx::xx + http://www.zytrax.com/books/dns/ch8/aaaa.html + */ + bool MDNSResponder::_writeMDNSAnswer_AAAA(IPAddress p_IPAddress, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_AAAA\n"));); + + 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 + + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_AAAA: FAILED!\n")); + }); + return bResult; + } -/* - MDNSResponder::_writeMDNSAnswer_PTR_IP6 + /* + MDNSResponder::_writeMDNSAnswer_PTR_IP6 - Write a MDNS reverse IP6 PTR answer to the UDP output buffer. - See: '_writeMDNSAnswer_A' + Write a MDNS reverse IP6 PTR answer to the UDP output buffer. + See: '_writeMDNSAnswer_A' - eg. xxxx::xx.in6.arpa PTR 0x8001 120 15 esp8266.local - Used while answering reverse IP6 questions -*/ -bool MDNSResponder::_writeMDNSAnswer_PTR_IP6(IPAddress p_IPAddress, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) -{ - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_IP6\n"));); - - stcMDNS_RRDomain reverseIP6Domain; - stcMDNS_RRAttributes attributes(DNS_RRTYPE_PTR, - ((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet - bool bResult = ((_buildDomainForReverseIP6(p_IPAddress, reverseIP6Domain)) && // xxxx::xx.ip6.arpa - (_writeMDNSRRDomain(reverseIP6Domain, p_rSendParameter)) && - (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS - (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_HOST_TTL), p_rSendParameter)) && // TTL - (_writeMDNSHostDomain(m_pcHostname, true, p_rSendParameter))); // RDLength & RData (host domain, eg. esp8266.local) - - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_IP6: FAILED!\n")); - }); - return bResult; -} + eg. xxxx::xx.in6.arpa PTR 0x8001 120 15 esp8266.local + Used while answering reverse IP6 questions + */ + bool + MDNSResponder::_writeMDNSAnswer_PTR_IP6(IPAddress p_IPAddress, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_IP6\n"));); + + stcMDNS_RRDomain reverseIP6Domain; + stcMDNS_RRAttributes attributes(DNS_RRTYPE_PTR, + ((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) + | DNS_RRCLASS_IN)); // Cache flush? & INternet + bool bResult + = ((_buildDomainForReverseIP6(p_IPAddress, reverseIP6Domain)) && // xxxx::xx.ip6.arpa + (_writeMDNSRRDomain(reverseIP6Domain, p_rSendParameter)) + && (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS + (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_HOST_TTL), p_rSendParameter)) + && // TTL + (_writeMDNSHostDomain( + m_pcHostname, true, + p_rSendParameter))); // RDLength & RData (host domain, eg. esp8266.local) + + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_PTR_IP6: FAILED!\n")); + }); + return bResult; + } #endif -/* - MDNSResponder::_writeMDNSAnswer_SRV - - eg. MyESP._http.tcp.local SRV 0x8001 120 0 0 60068 esp8266.local - http://www.zytrax.com/books/dns/ch8/srv.html ???? Include instance name ???? -*/ -bool MDNSResponder::_writeMDNSAnswer_SRV(MDNSResponder::stcMDNSService& p_rService, - MDNSResponder::stcMDNSSendParameter& p_rSendParameter) -{ - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_SRV\n"));); - - uint16_t u16CachedDomainOffset = (p_rSendParameter.m_bLegacyQuery - ? 0 - : p_rSendParameter.findCachedDomainOffset((const void*)m_pcHostname, false)); - - stcMDNS_RRAttributes attributes(DNS_RRTYPE_SRV, - ((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) | DNS_RRCLASS_IN)); // Cache flush? & INternet - stcMDNS_RRDomain hostDomain; - bool bResult = ((_writeMDNSServiceDomain(p_rService, true, false, p_rSendParameter)) && // MyESP._http._tcp.local - (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS - (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_SERVICE_TTL), p_rSendParameter)) && // TTL - (!u16CachedDomainOffset - // No cache for domain name (or no compression allowed) - ? ((_buildDomainForHost(m_pcHostname, hostDomain)) && - (_write16((sizeof(uint16_t /*Prio*/) + // RDLength - sizeof(uint16_t /*Weight*/) + - sizeof(uint16_t /*Port*/) + - hostDomain.m_u16NameLength), p_rSendParameter)) && // Domain length - (_write16(MDNS_SRV_PRIORITY, p_rSendParameter)) && // Priority - (_write16(MDNS_SRV_WEIGHT, p_rSendParameter)) && // Weight - (_write16(p_rService.m_u16Port, p_rSendParameter)) && // Port - (p_rSendParameter.addDomainCacheItem((const void*)m_pcHostname, false, p_rSendParameter.m_u16Offset)) && - (_writeMDNSRRDomain(hostDomain, p_rSendParameter))) // Host, eg. esp8266.local - // Cache available for domain - : ((MDNS_DOMAIN_COMPRESS_MARK > ((u16CachedDomainOffset >> 8) & ~MDNS_DOMAIN_COMPRESS_MARK)) && // Valid offset - (_write16((sizeof(uint16_t /*Prio*/) + // RDLength - sizeof(uint16_t /*Weight*/) + - sizeof(uint16_t /*Port*/) + - 2), p_rSendParameter)) && // Length of 'C0xx' - (_write16(MDNS_SRV_PRIORITY, p_rSendParameter)) && // Priority - (_write16(MDNS_SRV_WEIGHT, p_rSendParameter)) && // Weight - (_write16(p_rService.m_u16Port, p_rSendParameter)) && // Port - (_write8(((u16CachedDomainOffset >> 8) | MDNS_DOMAIN_COMPRESS_MARK), p_rSendParameter)) && // Compression mark (and offset) - (_write8((uint8_t)u16CachedDomainOffset, p_rSendParameter))))); // Offset - - DEBUG_EX_ERR(if (!bResult) -{ - DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_SRV: FAILED!\n")); - }); - return bResult; -} - -} // namespace MDNSImplementation - -} // namespace esp8266 - - - + /* + MDNSResponder::_writeMDNSAnswer_SRV + eg. MyESP._http.tcp.local SRV 0x8001 120 0 0 60068 esp8266.local + http://www.zytrax.com/books/dns/ch8/srv.html ???? Include instance name ???? + */ + bool MDNSResponder::_writeMDNSAnswer_SRV(MDNSResponder::stcMDNSService& p_rService, + MDNSResponder::stcMDNSSendParameter& p_rSendParameter) + { + DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_SRV\n"));); + + uint16_t u16CachedDomainOffset + = (p_rSendParameter.m_bLegacyQuery + ? 0 + : p_rSendParameter.findCachedDomainOffset((const void*)m_pcHostname, false)); + + stcMDNS_RRAttributes attributes(DNS_RRTYPE_SRV, + ((p_rSendParameter.m_bCacheFlush ? 0x8000 : 0) + | DNS_RRCLASS_IN)); // Cache flush? & INternet + stcMDNS_RRDomain hostDomain; + bool bResult + = ((_writeMDNSServiceDomain(p_rService, true, false, p_rSendParameter)) + && // MyESP._http._tcp.local + (_writeMDNSRRAttributes(attributes, p_rSendParameter)) && // TYPE & CLASS + (_write32((p_rSendParameter.m_bUnannounce ? 0 : MDNS_SERVICE_TTL), p_rSendParameter)) + && // TTL + (!u16CachedDomainOffset + // No cache for domain name (or no compression allowed) + ? ((_buildDomainForHost(m_pcHostname, hostDomain)) + && (_write16((sizeof(uint16_t /*Prio*/) + // RDLength + sizeof(uint16_t /*Weight*/) + sizeof(uint16_t /*Port*/) + + hostDomain.m_u16NameLength), + p_rSendParameter)) + && // Domain length + (_write16(MDNS_SRV_PRIORITY, p_rSendParameter)) && // Priority + (_write16(MDNS_SRV_WEIGHT, p_rSendParameter)) && // Weight + (_write16(p_rService.m_u16Port, p_rSendParameter)) && // Port + (p_rSendParameter.addDomainCacheItem((const void*)m_pcHostname, false, + p_rSendParameter.m_u16Offset)) + && (_writeMDNSRRDomain(hostDomain, + p_rSendParameter))) // Host, eg. esp8266.local + // Cache available for domain + : ((MDNS_DOMAIN_COMPRESS_MARK + > ((u16CachedDomainOffset >> 8) & ~MDNS_DOMAIN_COMPRESS_MARK)) + && // Valid offset + (_write16((sizeof(uint16_t /*Prio*/) + // RDLength + sizeof(uint16_t /*Weight*/) + sizeof(uint16_t /*Port*/) + 2), + p_rSendParameter)) + && // Length of 'C0xx' + (_write16(MDNS_SRV_PRIORITY, p_rSendParameter)) && // Priority + (_write16(MDNS_SRV_WEIGHT, p_rSendParameter)) && // Weight + (_write16(p_rService.m_u16Port, p_rSendParameter)) && // Port + (_write8(((u16CachedDomainOffset >> 8) | MDNS_DOMAIN_COMPRESS_MARK), + p_rSendParameter)) + && // Compression mark (and offset) + (_write8((uint8_t)u16CachedDomainOffset, p_rSendParameter))))); // Offset + + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] _writeMDNSAnswer_SRV: FAILED!\n")); + }); + return bResult; + } +} // namespace MDNSImplementation +} // namespace esp8266 diff --git a/libraries/ESP8266mDNS/src/LEAmDNS_lwIPdefs.h b/libraries/ESP8266mDNS/src/LEAmDNS_lwIPdefs.h index 3686440f10..ea2128a9ed 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS_lwIPdefs.h +++ b/libraries/ESP8266mDNS/src/LEAmDNS_lwIPdefs.h @@ -27,4 +27,4 @@ #include // DNS_RRTYPE_xxx, DNS_MQUERY_PORT -#endif // MDNS_LWIPDEFS_H +#endif // MDNS_LWIPDEFS_H 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/GDBStub/README.md b/libraries/GDBStub/README.md index 0883a2c1b5..1968ed2f22 100644 --- a/libraries/GDBStub/README.md +++ b/libraries/GDBStub/README.md @@ -30,9 +30,9 @@ in Git. * Modify your Makefile. You'll need to include the gdbstub sources: if your Makefile is structured like the ones in the Espressif examples, you can add `gdbstub` to the `SUBDIRS` define and `gdbstub/libgdbstub.a` to the `COMPONENTS_eagle.app.v6` define. Also, you probably want to add `-ggdb` to your compiler flags (`TARGET_LDFLAGS`) -and, if you are debugging, change any optimation flags (-Os, -O2 etc) into `-Og`. Finally, make sure your Makefile +and, if you are debugging, change any optimization flags (-Os, -O2 etc) into `-Og`. Finally, make sure your Makefile also compiles .S files. - * Configure gdbstub by editting `gdbstub-cfg.h`. There are a bunch of options you can tweak: FreeRTOS or bare SDK, + * Configure gdbstub by editing `gdbstub-cfg.h`. There are a bunch of options you can tweak: FreeRTOS or bare SDK, private exception/breakpoint stack, console redirection to GDB, wait till debugger attachment etc. You can also configure the options by including the proper -Dwhatever gcc flags in your Makefiles. * In your user_main.c, add an `#include <../gdbstub/gdbstub.h>` and call `gdbstub_init();` somewhere in user_main. diff --git a/libraries/GDBStub/src/internal/gdbstub-cfg.h b/libraries/GDBStub/src/internal/gdbstub-cfg.h index be40ab98f6..c6adfda010 100644 --- a/libraries/GDBStub/src/internal/gdbstub-cfg.h +++ b/libraries/GDBStub/src/internal/gdbstub-cfg.h @@ -67,7 +67,7 @@ likely crash. #define ATTR_GDBINIT ICACHE_FLASH_ATTR #endif #ifndef ATTR_GDBFN -#define ATTR_GDBFN ICACHE_RAM_ATTR +#define ATTR_GDBFN IRAM_ATTR #endif #ifndef ATTR_GDBEXTERNFN #define ATTR_GDBEXTERNFN ICACHE_FLASH_ATTR diff --git a/libraries/GDBStub/src/internal/gdbstub.c b/libraries/GDBStub/src/internal/gdbstub.c index e5895a3a0b..537de3c3f7 100644 --- a/libraries/GDBStub/src/internal/gdbstub.c +++ b/libraries/GDBStub/src/internal/gdbstub.c @@ -458,7 +458,7 @@ static inline int gdbHandleCommand() { return ST_CONT; } else if (cmd[0] == 's') { //single-step instruction //Single-stepping can go wrong if an interrupt is pending, especially when it is e.g. a task switch: - //the ICOUNT register will overflow in the task switch code. That is why we disable interupts when + //the ICOUNT register will overflow in the task switch code. That is why we disable interrupts when //doing single-instruction stepping. singleStepPs=gdbstub_savedRegs.ps; gdbstub_savedRegs.ps=(gdbstub_savedRegs.ps & ~0xf) | (XCHAL_DEBUGLEVEL - 1); diff --git a/libraries/Hash/examples/sha1/sha1.ino b/libraries/Hash/examples/sha1/sha1.ino index e9260ed9c5..4fe9f8a9c3 100644 --- a/libraries/Hash/examples/sha1/sha1.ino +++ b/libraries/Hash/examples/sha1/sha1.ino @@ -22,9 +22,7 @@ void loop() { sha1("test", &hash[0]); Serial.print("SHA1:"); - for (uint16_t i = 0; i < 20; i++) { - Serial.printf("%02x", hash[i]); - } + for (uint16_t i = 0; i < 20; i++) { Serial.printf("%02x", hash[i]); } Serial.println(); delay(1000); diff --git a/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino b/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino new file mode 100644 index 0000000000..18f1cad1f9 --- /dev/null +++ b/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino @@ -0,0 +1,36 @@ +/* + This example reads audio data from an Invensense's ICS43432 I2S microphone + breakout board, and prints out the samples to the Serial console. The + Serial Plotter built into the Arduino IDE can be used to plot the audio + data (Tools -> Serial Plotter) + created 17 November 2016 + by Sandeep Mistry +*/ + +#include + +void setup() { + // Open serial communications and wait for port to open: + // A baud rate of 115200 is used instead of 9600 for a faster data rate + // on non-native USB ports + Serial.begin(115200); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + // 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 + } +} + +void loop() { + // read a sample + int sample = I2S.read(); + + if (sample) { + // if it's non-zero print value to serial + Serial.println(sample); + } +} diff --git a/libraries/I2S/examples/SimpleTone/SimpleTone.ino b/libraries/I2S/examples/SimpleTone/SimpleTone.ino new file mode 100644 index 0000000000..e2dcb7b3f6 --- /dev/null +++ b/libraries/I2S/examples/SimpleTone/SimpleTone.ino @@ -0,0 +1,45 @@ +/* + This example generates a square wave based tone at a specified frequency + and sample rate. Then outputs the data using the I2S interface to a + MAX08357 I2S Amp Breakout board. + + created 17 November 2016 + by Sandeep Mistry + modified for ESP8266 by Earle F. Philhower, III +*/ + +#include + +const int frequency = 440; // frequency of square wave in Hz +const int amplitude = 500; // amplitude of square wave +const int sampleRate = 8000; // sample rate in Hz + +const int halfWavelength = (sampleRate / frequency); // half wavelength of square wave + +short sample = amplitude; // current sample value +int count = 0; + +void setup() { + Serial.begin(115200); + Serial.println("I2S simple tone"); + + // 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 + } +} + +void loop() { + if (count % halfWavelength == 0) { + // invert the sample every half wavelength count multiple to generate square wave + sample = -1 * sample; + } + + // write the same sample twice, once for left and once for the right channel + I2S.write(sample); + I2S.write(sample); + + // increment the counter for the next sample + count++; +} diff --git a/libraries/I2S/keywords.txt b/libraries/I2S/keywords.txt new file mode 100644 index 0000000000..ad80bcb4c0 --- /dev/null +++ b/libraries/I2S/keywords.txt @@ -0,0 +1,23 @@ +####################################### +# Syntax Coloring Map I2S +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +I2S KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### +begin KEYWORD2 +end KEYWORD2 + +onReceive KEYWORD2 +onTransmit KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### +I2S_PHILIPS_MODE LITERAL1 diff --git a/libraries/I2S/library.properties b/libraries/I2S/library.properties new file mode 100644 index 0000000000..9f1d31f2fa --- /dev/null +++ b/libraries/I2S/library.properties @@ -0,0 +1,9 @@ +name=I2S +version=1.0 +author=Earle F. Philhower, III +maintainer=Earle F. Philhower, III +sentence=Enables the communication with devices that use the Inter-IC Sound (I2S) Bus. Specific implementation for ESP8266, based off of SAMD. +paragraph= +category=Communication +url=http://www.arduino.cc/en/Reference/I2S +architectures=esp8266 diff --git a/libraries/I2S/src/I2S.cpp b/libraries/I2S/src/I2S.cpp new file mode 100644 index 0000000000..6220d97a5f --- /dev/null +++ b/libraries/I2S/src/I2S.cpp @@ -0,0 +1,211 @@ +/* + Based off of ArduinoCore-SAMD I2S interface. Modified for the + ESP8266 by Earle F. Philhower, III + + 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 "I2S.h" + +I2SClass::I2SClass(bool enableTransmit, bool enableRecv, bool driveClocks) { + _enableTx = enableTransmit; + _enableRx = enableRecv; + _driveClk = driveClocks; + _running = false; + _onTransmit = nullptr; + _onReceive = nullptr; + _havePeeked = 0; + _peekedData = 0; + _bps = 0; + _writtenHalf = false; +} + +int I2SClass::begin(i2s_mode_t mode, long sampleRate, int bitsPerSample) { + if ( _running || (mode != I2S_PHILIPS_MODE) || ( (bitsPerSample != 16) && (bitsPerSample != 24) ) ) { + return 0; + } + if (!i2s_rxtxdrive_begin(_enableRx, _enableTx, _driveClk, _driveClk)) { + return 0; + } + i2s_set_rate(sampleRate); + i2s_set_callback(_onTransmit); + i2s_rx_set_callback(_onReceive); + _bps = bitsPerSample; + _running = true; + return 1; +} + +void I2SClass::end() { + if (_running) { + i2s_end(); + } + i2s_set_callback(nullptr); + i2s_rx_set_callback(nullptr); + _running = false; +} + +void I2SClass::onTransmit(void(*fcn)(void)) { + i2s_set_callback(fcn); + _onTransmit = fcn; +} + +void I2SClass::onReceive(void(*fcn)(void)) { + i2s_rx_set_callback(fcn); + _onReceive = fcn; +} + +int I2SClass::available() { + if (!_running) return 0; + return i2s_rx_available(); +} + +int I2SClass::availableForWrite() { + if (!_running) return 0; + return i2s_available(); +} + +void I2SClass::flush() { + /* No-op */ +} + +int I2SClass::read() { + if (!_running) return -1; + // Always just read from the peeked value to simplify operation + if (!_havePeeked) { + peek(); + } + if (_havePeeked) { + if (_bps == 16) { + _havePeeked--; + int ret = _peekedData; + _peekedData >>= 16; + return ret; + } else /* _bps == 24 */ { + _havePeeked = 0; + return _peekedData; + } + } + return 0; +} + +int I2SClass::peek() { + if (!_running) return -1; + if (_havePeeked) { + if (_bps == 16) { + int16_t sample = (int16_t)_peekedData; // Will extends sign on return + return sample; + } else { + return _peekedData; + } + } + int16_t l, r; + i2s_read_sample(&l, &r, true); + _peekedData = ((int)l << 16) | (0xffff & (int)r); + _havePeeked = 2; // We now have 2 16-bit quantities which can also be used as 1 32-bit(24-bit) + if (_bps == 16) { + return r; + } else { + return _peekedData; + } +} + +int I2SClass::read(void *buffer, size_t size) { + if (!_running) return -1; + int cnt = 0; + + if ( ((_bps == 24) && (size % 4)) || ((_bps == 16) && (size % 2)) || (size < 2) ) { + return 0; // Invalid, can only read in units of samples + } + // Make sure any peeked data is consumed first + if (_havePeeked) { + if (_bps == 16) { + while (_havePeeked && size) { + uint16_t *p = (uint16_t *)buffer; + *(p++) = _peekedData; + _peekedData >>= 16; + _havePeeked--; + buffer = (void *)p; + size -= 2; + cnt += 2; + } + } else { + uint32_t *p = (uint32_t *)buffer; + *(p++) = _peekedData; + buffer = (void *)p; + size -= 4; + cnt += 4; + } + } + // Now just non-blocking read up to the remaining size + int16_t l, r; + int16_t *p = (int16_t *)buffer; + while (size && i2s_read_sample(&l, &r, false)) { + *(p++) = l; + size--; + cnt++; + if (size) { + *(p++) = r; + size--; + cnt++; + } else { + // We read a simple we can't return, stuff it in the peeked data + _havePeeked = 1; + _peekedData = r; + } + } + return cnt; +} + +size_t I2SClass::write(uint8_t s) { + if (!_running) return 0; + return write((int32_t)s); +} + +size_t I2SClass::write(const uint8_t *buffer, size_t size) { + return write((const void *)buffer, size); +} + +size_t I2SClass::write(int32_t s) { + if (!_running) return 0; + // Because our HW really wants 32b writes, store any 16b writes until another + // 16b write comes in and then send the combined write down. + if (_bps == 16) { + if (_writtenHalf) { + _writtenData <<= 16; + _writtenData |= 0xffff & s; + _writtenHalf = false; + return i2s_write_sample(_writtenData) ? 1 : 0; + } else { + _writtenHalf = true; + _writtenData = s & 0xffff; + return 1; + } + } else { + return i2s_write_sample((uint32_t)s) ? 1 : 0; + } +} + +// SAMD core has this as non-blocking +size_t I2SClass::write(const void *buffer, size_t size) { + if (!_running) return 0; + return i2s_write_buffer_nb((int16_t *)buffer, size / 2); +} + + +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_I2S) +I2SClass I2S; +#endif + diff --git a/libraries/I2S/src/I2S.h b/libraries/I2S/src/I2S.h new file mode 100644 index 0000000000..08400d2997 --- /dev/null +++ b/libraries/I2S/src/I2S.h @@ -0,0 +1,95 @@ +/* + Based off of ArduinoCore-SAMD I2S interface. Modified for the + ESP8266 by Earle F. Philhower, III + + Copyright (c) 2016 Arduino LLC. All right reserved. + + 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 _I2S_H_INCLUDED +#define _I2S_H_INCLUDED + +#include +#include + +typedef enum { + I2S_PHILIPS_MODE // Only mode allowed for now by the core +} i2s_mode_t; + +class I2SClass : public Stream +{ +public: + // By default only transmit and drive the clock pins + I2SClass(bool enableTransmit = true, bool enableRecv = false, + bool driveClocks = true); + + // Only 16 and 24 bitsPerSample are allowed by the hardware + // 24-bit is MSB-aligned, with 0x00 in the lowest byte of each element. + int begin(i2s_mode_t mode, long sampleRate, int bitsPerSample); + void end(); + + // from Stream + virtual int available(); + virtual int read(); // Blocking, will wait for incoming data + virtual int peek(); // Blocking, will wait for incoming data + virtual void flush(); + + // from Print (see notes on write() methods below) + virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *buffer, size_t size); + virtual int availableForWrite(); + + // Read up to size samples from the I2S device. Non-blocking, will read + // from 0...size samples and return the count read. Be sure your app handles + // the partial read case (i.e. yield()ing and trying to read more). + int read(void* buffer, size_t size); + + // Write a single sample to the I2S device. Blocking until write succeeds + size_t write(int32_t); + // Write up to size samples to the I2S device. Non-blocking, will write + // from 0...size samples and return that count. Be sure your app handles + // partial writes (i.e. by yield()ing and then retrying to write the + // remaining data. + size_t write(const void *buffer, size_t size); + + // Note that these callback are called from **INTERRUPT CONTEXT** and hence + // must be both stored in IRAM and not perform anything that's not legal in + // an interrupt + void onTransmit(void(*)(void)); + void onReceive(void(*)(void)); + +private: + int _bps; + bool _running; + bool _enableTx; + bool _enableRx; + bool _driveClk; + void (*_onTransmit)(void); + void (*_onReceive)(void); + // Support for peek() on read path + uint32_t _peekedData; + int _havePeeked; + // Support for ::write(x) on 16b wuantities + uint32_t _writtenData; + bool _writtenHalf; +}; + +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_I2S) +extern I2SClass I2S; +#endif + +#endif + diff --git a/libraries/LittleFS/examples/LittleFS_Timestamp/LittleFS_Timestamp.ino b/libraries/LittleFS/examples/LittleFS_Timestamp/LittleFS_Timestamp.ino index dfec9ee301..ab6cf58eb8 100644 --- a/libraries/LittleFS/examples/LittleFS_Timestamp/LittleFS_Timestamp.ino +++ b/libraries/LittleFS/examples/LittleFS_Timestamp/LittleFS_Timestamp.ino @@ -9,7 +9,7 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif const char *ssid = STASSID; @@ -19,30 +19,7 @@ long timezone = 2; byte daysavetime = 1; -bool getLocalTime(struct tm * info, uint32_t ms) { - uint32_t count = ms / 10; - time_t now; - - time(&now); - localtime_r(&now, info); - - if (info->tm_year > (2016 - 1900)) { - return true; - } - - while (count--) { - delay(10); - time(&now); - localtime_r(&now, info); - if (info->tm_year > (2016 - 1900)) { - return true; - } - } - return false; -} - - -void listDir(const char * dirname) { +void listDir(const char *dirname) { Serial.printf("Listing directory: %s\n", dirname); Dir root = LittleFS.openDir(dirname); @@ -56,7 +33,7 @@ void listDir(const char * dirname) { time_t cr = file.getCreationTime(); time_t lw = file.getLastWrite(); file.close(); - struct tm * tmstruct = localtime(&cr); + struct tm *tmstruct = localtime(&cr); Serial.printf(" CREATION: %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); tmstruct = localtime(&lw); Serial.printf(" LAST 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); @@ -64,7 +41,7 @@ void listDir(const char * dirname) { } -void readFile(const char * path) { +void readFile(const char *path) { Serial.printf("Reading file: %s\n", path); File file = LittleFS.open(path, "r"); @@ -74,13 +51,11 @@ void readFile(const char * path) { } Serial.print("Read from file: "); - while (file.available()) { - Serial.write(file.read()); - } + while (file.available()) { Serial.write(file.read()); } file.close(); } -void writeFile(const char * path, const char * message) { +void writeFile(const char *path, const char *message) { Serial.printf("Writing file: %s\n", path); File file = LittleFS.open(path, "w"); @@ -93,11 +68,11 @@ void writeFile(const char * path, const char * message) { } else { Serial.println("Write failed"); } - delay(2000); // Make sure the CREATE and LASTWRITE times are different + delay(2000); // Make sure the CREATE and LASTWRITE times are different file.close(); } -void appendFile(const char * path, const char * message) { +void appendFile(const char *path, const char *message) { Serial.printf("Appending to file: %s\n", path); File file = LittleFS.open(path, "a"); @@ -113,7 +88,7 @@ void appendFile(const char * path, const char * message) { file.close(); } -void renameFile(const char * path1, const char * path2) { +void renameFile(const char *path1, const char *path2) { Serial.printf("Renaming file %s to %s\n", path1, path2); if (LittleFS.rename(path1, path2)) { Serial.println("File renamed"); @@ -122,7 +97,7 @@ void renameFile(const char * path1, const char * path2) { } } -void deleteFile(const char * path) { +void deleteFile(const char *path) { Serial.printf("Deleting file: %s\n", path); if (LittleFS.remove(path)) { Serial.println("File deleted"); @@ -150,7 +125,7 @@ void setup() { Serial.println(WiFi.localIP()); Serial.println("Contacting Time Server"); configTime(3600 * timezone, daysavetime * 3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org"); - struct tm tmstruct ; + struct tm tmstruct; delay(2000); tmstruct.tm_year = 0; getLocalTime(&tmstruct, 5000); @@ -181,9 +156,6 @@ void setup() { } readFile("/hello.txt"); listDir("/"); - - } -void loop() { } - +void loop() {} diff --git a/libraries/LittleFS/examples/SpeedTest/SpeedTest.ino b/libraries/LittleFS/examples/SpeedTest/SpeedTest.ino index 5987e10807..c3dd6b2c1f 100644 --- a/libraries/LittleFS/examples/SpeedTest/SpeedTest.ino +++ b/libraries/LittleFS/examples/SpeedTest/SpeedTest.ino @@ -8,12 +8,31 @@ // 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 +// Format speed in bytes/second. Static buffer so not re-entrant safe +const char *rate(unsigned long start, unsigned long stop, unsigned long bytes) { + static char buff[64]; + if (stop == start) { + strcpy_P(buff, PSTR("Inf b/s")); + } else { + unsigned long delta = stop - start; + float r = 1000.0 * (float)bytes / (float)delta; + if (r >= 1000000.0) { + sprintf_P(buff, PSTR("%0.2f MB/s"), r / 1000000.0); + } else if (r >= 1000.0) { + sprintf_P(buff, PSTR("%0.2f KB/s"), r / 1000.0); + } else { + sprintf_P(buff, PSTR("%d bytes/s"), (int)r); + } + } + return buff; +} + void DoTest(FS *fs) { if (!fs->format()) { Serial.printf("Unable to format(), aborting\n"); @@ -25,55 +44,46 @@ void DoTest(FS *fs) { } uint8_t data[256]; - for (int i = 0; i < 256; i++) { - data[i] = (uint8_t) i; - } + for (int i = 0; i < 256; i++) { data[i] = (uint8_t)i; } Serial.printf("Creating %dKB file, may take a while...\n", TESTSIZEKB); - long start = millis(); + unsigned long start = millis(); File f = fs->open("/testwrite.bin", "w"); if (!f) { Serial.printf("Unable to open file for writing, aborting\n"); return; } for (int i = 0; i < TESTSIZEKB; i++) { - for (int j = 0; j < 4; j++) { - f.write(data, 256); - } + for (int j = 0; j < 4; j++) { f.write(data, 256); } } f.close(); - long stop = millis(); - Serial.printf("==> Time to write %dKB in 256b chunks = %ld milliseconds\n", TESTSIZEKB, stop - start); + unsigned long stop = millis(); + Serial.printf("==> Time to write %dKB in 256b chunks = %lu milliseconds\n", TESTSIZEKB, stop - start); f = fs->open("/testwrite.bin", "r"); - Serial.printf("==> Created file size = %d\n", f.size()); + Serial.printf("==> Created file size = %zu\n", f.size()); f.close(); Serial.printf("Reading %dKB file sequentially in 256b chunks\n", TESTSIZEKB); start = millis(); f = fs->open("/testwrite.bin", "r"); for (int i = 0; i < TESTSIZEKB; i++) { - for (int j = 0; j < 4; j++) { - f.read(data, 256); - } + for (int j = 0; j < 4; j++) { f.read(data, 256); } } f.close(); stop = millis(); - Serial.printf("==> Time to read %dKB sequentially in 256b chunks = %ld milliseconds = %ld bytes/s\n", TESTSIZEKB, stop - start, TESTSIZEKB * 1024 / (stop - start) * 1000); + Serial.printf("==> Time to read %dKB sequentially in 256b chunks = %lu milliseconds = %s\n", TESTSIZEKB, stop - start, rate(start, stop, TESTSIZEKB * 1024)); Serial.printf("Reading %dKB file MISALIGNED in flash and RAM sequentially in 256b chunks\n", TESTSIZEKB); start = millis(); f = fs->open("/testwrite.bin", "r"); f.read(); for (int i = 0; i < TESTSIZEKB; i++) { - for (int j = 0; j < 4; j++) { - f.read(data + 1, 256); - } + for (int j = 0; j < 4; j++) { f.read(data + 1, 256); } } f.close(); stop = millis(); - Serial.printf("==> Time to read %dKB sequentially MISALIGNED in flash and RAM in 256b chunks = %ld milliseconds = %ld bytes/s\n", TESTSIZEKB, stop - start, TESTSIZEKB * 1024 / (stop - start) * 1000); - + Serial.printf("==> Time to read %dKB sequentially MISALIGNED in flash and RAM in 256b chunks = %lu milliseconds = %s\n", TESTSIZEKB, stop - start, rate(start, stop, TESTSIZEKB * 1024)); Serial.printf("Reading %dKB file in reverse by 256b chunks\n", TESTSIZEKB); start = millis(); @@ -92,31 +102,36 @@ void DoTest(FS *fs) { } f.close(); stop = millis(); - Serial.printf("==> Time to read %dKB in reverse in 256b chunks = %ld milliseconds = %ld bytes/s\n", TESTSIZEKB, stop - start, TESTSIZEKB * 1024 / (stop - start) * 1000); - + Serial.printf("==> Time to read %dKB in reverse in 256b chunks = %lu milliseconds = %s\n", TESTSIZEKB, stop - start, rate(start, stop, TESTSIZEKB * 1024)); Serial.printf("Writing 64K file in 1-byte chunks\n"); start = millis(); f = fs->open("/test1b.bin", "w"); - for (int i = 0; i < 65536; i++) { - f.write((uint8_t*)&i, 1); - } + for (int i = 0; i < 65536; i++) { f.write((uint8_t *)&i, 1); } f.close(); stop = millis(); - Serial.printf("==> Time to write 64KB in 1b chunks = %ld milliseconds = %ld bytes/s\n", stop - start, 65536 / (stop - start) * 1000); + Serial.printf("==> Time to write 64KB in 1b chunks = %lu milliseconds = %s\n", stop - start, rate(start, stop, 65536)); Serial.printf("Reading 64K file in 1-byte chunks\n"); start = millis(); f = fs->open("/test1b.bin", "r"); for (int i = 0; i < 65536; i++) { char c; - f.read((uint8_t*)&c, 1); + f.read((uint8_t *)&c, 1); } f.close(); stop = millis(); - Serial.printf("==> Time to read 64KB in 1b chunks = %ld milliseconds = %ld bytes/s\n", stop - start, 65536 / (stop - start) * 1000); + Serial.printf("==> Time to read 64KB in 1b chunks = %lu milliseconds = %s\n", stop - start, rate(start, stop, 65536)); + start = millis(); + auto dest = fs->open("/test1bw.bin", "w"); + f = fs->open("/test1b.bin", "r"); + auto copysize = f.sendAll(dest); + dest.close(); + stop = millis(); + Serial.printf("==> Time to copy %d = %zd bytes = %lu milliseconds = %s\n", f.size(), copysize, stop - start, rate(start, stop, f.size())); + f.close(); } void setup() { @@ -124,6 +139,7 @@ void setup() { Serial.printf("Beginning test\n"); Serial.flush(); DoTest(&TESTFS); + Serial.println("done"); } void loop() { 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/library.properties b/libraries/LittleFS/library.properties index a13e6eb755..eeaf017ac7 100644 --- a/libraries/LittleFS/library.properties +++ b/libraries/LittleFS/library.properties @@ -1,4 +1,4 @@ -name=LittleFS(esp8266) +name=LittleFS version=0.1.0 author=Earle F. Philhower, III maintainer=Earle F. Philhower, III diff --git a/libraries/LittleFS/src/LittleFS.h b/libraries/LittleFS/src/LittleFS.h index 0a483c9b1d..d5203ae6e0 100644 --- a/libraries/LittleFS/src/LittleFS.h +++ b/libraries/LittleFS/src/LittleFS.h @@ -59,24 +59,26 @@ class LittleFSImpl : public FSImpl _mounted(false) { memset(&_lfs, 0, sizeof(_lfs)); memset(&_lfs_cfg, 0, sizeof(_lfs_cfg)); - _lfs_cfg.context = (void*) this; - _lfs_cfg.read = lfs_flash_read; - _lfs_cfg.prog = lfs_flash_prog; - _lfs_cfg.erase = lfs_flash_erase; - _lfs_cfg.sync = lfs_flash_sync; - _lfs_cfg.read_size = 64; - _lfs_cfg.prog_size = 64; - _lfs_cfg.block_size = _blockSize; - _lfs_cfg.block_count =_blockSize? _size / _blockSize: 0; - _lfs_cfg.block_cycles = 16; // TODO - need better explanation - _lfs_cfg.cache_size = 64; - _lfs_cfg.lookahead_size = 64; - _lfs_cfg.read_buffer = nullptr; - _lfs_cfg.prog_buffer = nullptr; - _lfs_cfg.lookahead_buffer = nullptr; - _lfs_cfg.name_max = 0; - _lfs_cfg.file_max = 0; - _lfs_cfg.attr_max = 0; + if (_size && _blockSize) { + _lfs_cfg.context = (void*) this; + _lfs_cfg.read = lfs_flash_read; + _lfs_cfg.prog = lfs_flash_prog; + _lfs_cfg.erase = lfs_flash_erase; + _lfs_cfg.sync = lfs_flash_sync; + _lfs_cfg.read_size = 64; + _lfs_cfg.prog_size = 64; + _lfs_cfg.block_size = _blockSize; + _lfs_cfg.block_count = _size / _blockSize; + _lfs_cfg.block_cycles = 16; // TODO - need better explanation + _lfs_cfg.cache_size = 64; + _lfs_cfg.lookahead_size = 64; + _lfs_cfg.read_buffer = nullptr; + _lfs_cfg.prog_buffer = nullptr; + _lfs_cfg.lookahead_buffer = nullptr; + _lfs_cfg.name_max = 0; + _lfs_cfg.file_max = 0; + _lfs_cfg.attr_max = 0; + } } ~LittleFSImpl() { @@ -165,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); } @@ -181,7 +191,10 @@ class LittleFSImpl : public FSImpl } bool begin() override { - if (_size <= 0) { + if (_mounted) { + return true; + } + if ((_blockSize <= 0) || (_size <= 0)) { DEBUGV("LittleFS size is <= zero"); return false; } @@ -203,7 +216,7 @@ class LittleFSImpl : public FSImpl } bool format() override { - if (_size == 0) { + if ((_blockSize <= 0) || (_size <= 0)) { DEBUGV("lfs size is zero\n"); return false; } @@ -221,6 +234,26 @@ class LittleFSImpl : public FSImpl return false; } + if(_timeCallback && _tryMount()) { + // Mounting is required to set attributes + + time_t t = _timeCallback(); + rc = lfs_setattr(&_lfs, "/", 'c', &t, 8); + if (rc != 0) { + DEBUGV("lfs_format, lfs_setattr 'c': rc=%d\n", rc); + return false; + } + + rc = lfs_setattr(&_lfs, "/", 't', &t, 8); + if (rc != 0) { + DEBUGV("lfs_format, lfs_setattr 't': rc=%d\n", rc); + return false; + } + + lfs_unmount(&_lfs); + _mounted = false; + } + if (wasMounted) { return _tryMount(); } @@ -228,6 +261,19 @@ class LittleFSImpl : public FSImpl return true; } + time_t getCreationTime() override { + time_t t; + uint32_t t32b; + + if (lfs_getattr(&_lfs, "/", 'c', &t, 8) == 8) { + return t; + } else if (lfs_getattr(&_lfs, "/", 'c', &t32b, 4) == 4) { + return (time_t)t32b; + } else { + return 0; + } + } + protected: friend class LittleFSFileImpl; @@ -334,6 +380,30 @@ class LittleFSFileImpl : public FileImpl } } + int availableForWrite () override { + if (!_opened || !_fd) { + return 0; + } + + const auto f = _getFD(); + const auto fs = _fs->getFS(); + + // check for remaining size in current block + // ignore inline feature (per code in lfs_file_rawwrite()) + auto afw = fs->cfg->block_size - f->off; + + if (afw == 0) { + // current block is full + // check for filesystem full (per code in lfs_alloc()) + if (!(fs->free.i == fs->free.size && fs->free.ack == 0)) { + // fs is not full, return a full sector as free space + afw = fs->cfg->block_size; + } + } + + return afw; + } + size_t write(const uint8_t *buf, size_t size) override { if (!_opened || !_fd || !buf) { return 0; @@ -346,7 +416,7 @@ class LittleFSFileImpl : public FileImpl return result; } - size_t read(uint8_t* buf, size_t size) override { + int read(uint8_t* buf, size_t size) override { if (!_opened || !_fd | !buf) { return 0; } @@ -429,14 +499,14 @@ class LittleFSFileImpl : public FileImpl if (_creation) { int rc = lfs_setattr(_fs->getFS(), _name.get(), 'c', (const void *)&_creation, sizeof(_creation)); if (rc < 0) { - DEBUGV("Unable to set creation time on '%s' to %d\n", _name.get(), _creation); + DEBUGV("Unable to set creation time on '%s' to %ld\n", _name.get(), (long)_creation); } } // Add metadata with last write time time_t now = _timeCallback(); int rc = lfs_setattr(_fs->getFS(), _name.get(), 't', (const void *)&now, sizeof(now)); if (rc < 0) { - DEBUGV("Unable to set last write time on '%s' to %d\n", _name.get(), now); + DEBUGV("Unable to set last write time on '%s' to %ld\n", _name.get(), (long)now); } } } diff --git a/libraries/LittleFS/src/lfs.c b/libraries/LittleFS/src/lfs.c index 62022fc375..9e770084e6 100644 --- a/libraries/LittleFS/src/lfs.c +++ b/libraries/LittleFS/src/lfs.c @@ -1,4 +1,4 @@ -// Can't place library in ths src/ directory, Arduino will attempt to build the tests/etc. +// Can't place library in this src/ directory, Arduino will attempt to build the tests/etc. // Just have a stub here that redirects to the actual source file #pragma GCC diagnostic ignored "-Wmissing-field-initializers" diff --git a/libraries/Netdump/examples/Netdump/Netdump.ino b/libraries/Netdump/examples/Netdump/Netdump.ino index dbba63869b..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 @@ -12,7 +12,7 @@ using namespace NetCapture; #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif const char* ssid = STASSID; @@ -20,43 +20,36 @@ const char* password = STAPSK; Netdump nd; -//FS* filesystem = &SPIFFS; +// FS* filesystem = &SPIFFS; FS* filesystem = &LittleFS; -ESP8266WebServer webServer(80); // Used for sending commands -WiFiServer tcpServer(8000); // Used to show netcat option. -File tracefile; +ESP8266WebServer webServer(80); // Used for sending commands +WiFiServer tcpServer(8000); // Used to show netcat option. +File tracefile; std::map packetCount; -enum class SerialOption : uint8_t { - AllFull, - LocalNone, - HTTPChar -}; +enum class SerialOption : uint8_t { AllFull, + LocalNone, + HTTPChar }; void startSerial(SerialOption option) { switch (option) { - case SerialOption::AllFull : //All Packets, show packet summary. + case SerialOption::AllFull: // All Packets, show packet summary. nd.printDump(Serial, Packet::PacketDetail::FULL); break; - case SerialOption::LocalNone : // Only local IP traffic, full details - nd.printDump(Serial, Packet::PacketDetail::NONE, - [](Packet n) { + case SerialOption::LocalNone: // Only local IP traffic, full details + nd.printDump(Serial, Packet::PacketDetail::NONE, [](Packet n) { return (n.hasIP(WiFi.localIP())); - } - ); + }); break; - case SerialOption::HTTPChar : // Only HTTP traffic, show packet content as chars - nd.printDump(Serial, Packet::PacketDetail::CHAR, - [](Packet n) { + case SerialOption::HTTPChar: // Only HTTP traffic, show packet content as chars + nd.printDump(Serial, Packet::PacketDetail::CHAR, [](Packet n) { return (n.isHTTP()); - } - ); + }); break; - default : - Serial.printf("No valid SerialOption provided\r\n"); + default: Serial.printf("No valid SerialOption provided\r\n"); }; } @@ -80,49 +73,39 @@ void setup(void) { if (WiFi.waitForConnectResult() != WL_CONNECTED) { Serial.println("WiFi Failed, stopping sketch"); - while (1) { - delay(1000); - } + while (1) { delay(1000); } } - if (!MDNS.begin("netdumphost")) { - Serial.println("Error setting up MDNS responder!"); - } + if (!MDNS.begin("netdumphost")) { Serial.println("Error setting up MDNS responder!"); } filesystem->begin(); - webServer.on("/list", - []() { + webServer.on("/list", []() { Dir dir = filesystem->openDir("/"); String d = "

    File list

    "; while (dir.next()) { d.concat("
  3. " + dir.fileName() + "
  4. "); } webServer.send(200, "text.html", d); - } - ); + }); - webServer.on("/req", - []() { + webServer.on("/req", []() { static int rq = 0; String a = "

    You are connected, Number of requests = " + String(rq++) + "

    "; webServer.send(200, "text/html", a); - } - ); + }); - webServer.on("/reset", - []() { + webServer.on("/reset", []() { nd.reset(); tracefile.close(); tcpServer.close(); webServer.send(200, "text.html", "

    Netdump session reset

    "); - } - ); + }); webServer.serveStatic("/", *filesystem, "/"); webServer.begin(); - startSerial(SerialOption::AllFull); // Serial output examples, use enum SerialOption for selection + startSerial(SerialOption::AllFull); // Serial output examples, use enum SerialOption for selection // startTcpDump(); // tcpdump option // startTracefile(); // output to SPIFFS or LittleFS @@ -132,18 +115,18 @@ void setup(void) { nd.setCallback( [](Packet p) { - Serial.printf("PKT : %s : ",p.sourceIP().toString().c_str()); - for ( auto pp : p.allPacketTypes()) - { - Serial.printf("%s ",pp.toString().c_str()); - packetCount[pp]++; - } - Serial.printf("\r\n CNT "); - for (auto pc : packetCount) - { - Serial.printf("%s %d ", pc.first.toString().c_str(),pc.second); - } - Serial.printf("\r\n"); + Serial.printf("PKT : %s : ",p.sourceIP().toString().c_str()); + for ( auto pp : p.allPacketTypes()) + { + Serial.printf("%s ",pp.toString().c_str()); + packetCount[pp]++; + } + Serial.printf("\r\n CNT "); + for (auto pc : packetCount) + { + Serial.printf("%s %d ", pc.first.toString().c_str(),pc.second); + } + Serial.printf("\r\n"); } ); */ @@ -153,4 +136,3 @@ void loop(void) { webServer.handleClient(); MDNS.update(); } - diff --git a/libraries/Netdump/library.properties b/libraries/Netdump/library.properties index bc23b9ceed..8fdee5a741 100644 --- a/libraries/Netdump/library.properties +++ b/libraries/Netdump/library.properties @@ -3,7 +3,7 @@ version=2 author=Herman Reintke maintainer=Herman Reintke sentence=tcpdump-like logger for esp8266/Arduino -paragraph=Dumps input / output packets on "Print"able type, or provide a TCP server for the real tcpdump. Check examples. Some other unrelated and independant tools are included. +paragraph=Dumps input / output packets on "Print"able type, or provide a TCP server for the real tcpdump. Check examples. Some other unrelated and independent tools are included. category=Communication url=https:// architectures=esp8266 diff --git a/libraries/Netdump/src/Netdump.cpp b/libraries/Netdump/src/Netdump.cpp index 4d6a9f42dc..6cee79a646 100644 --- a/libraries/Netdump/src/Netdump.cpp +++ b/libraries/Netdump/src/Netdump.cpp @@ -23,7 +23,6 @@ #include #include "Schedule.h" - namespace NetCapture { @@ -68,42 +67,52 @@ void Netdump::reset() void Netdump::printDump(Print& out, Packet::PacketDetail ndd, const Filter nf) { - out.printf("netDump starting\r\n"); - setCallback([&out, ndd, this](const Packet & ndp) - { - printDumpProcess(out, ndd, ndp); - }, nf); + out.printf_P(PSTR("netDump starting\r\n")); + setCallback( + [&out, ndd, this](const Packet& ndp) + { + printDumpProcess(out, ndd, ndp); + }, + nf); } void Netdump::fileDump(File& outfile, const Filter nf) { - writePcapHeader(outfile); - setCallback([&outfile, this](const Packet & ndp) - { - fileDumpProcess(outfile, ndp); - }, nf); + setCallback( + [&outfile, this](const Packet& ndp) + { + fileDumpProcess(outfile, ndp); + }, + nf); } -void Netdump::tcpDump(WiFiServer &tcpDumpServer, const Filter nf) +bool Netdump::tcpDump(WiFiServer& tcpDumpServer, const Filter nf) { - if (!packetBuffer) { packetBuffer = new (std::nothrow) char[tcpBufferSize]; + + if (!packetBuffer) + { + return false; + } } bufferIndex = 0; - schedule_function([&tcpDumpServer, this, nf]() - { - tcpDumpLoop(tcpDumpServer, nf); - }); + schedule_function( + [&tcpDumpServer, this, nf]() + { + tcpDumpLoop(tcpDumpServer, nf); + }); + return true; } void Netdump::capture(int netif_idx, const char* data, size_t len, int out, int success) { if (lwipCallback.execute(netif_idx, data, len, out, success) == 0) { - phy_capture = nullptr; // No active callback/netdump instances, will be set again by new object. + phy_capture + = nullptr; // No active callback/netdump instances, will be set again by new object. } } @@ -112,7 +121,7 @@ void Netdump::netdumpCapture(int netif_idx, const char* data, size_t len, int ou if (netDumpCallback) { Packet np(millis(), netif_idx, data, len, out, success); - if (netDumpFilter && !netDumpFilter(np)) + if (netDumpFilter && !netDumpFilter(np)) { return; } @@ -125,8 +134,8 @@ void Netdump::writePcapHeader(Stream& s) const uint32_t pcapHeader[6]; pcapHeader[0] = 0xa1b2c3d4; // pcap magic number pcapHeader[1] = 0x00040002; // pcap major/minor version - pcapHeader[2] = 0; // pcap UTC correction in seconds - pcapHeader[3] = 0; // pcap time stamp accuracy + pcapHeader[2] = 0; // pcap UTC correction in seconds + pcapHeader[3] = 0; // pcap time stamp accuracy pcapHeader[4] = maxPcapLength; // pcap max packet length per record pcapHeader[5] = 1; // pacp data linkt type = ethernet s.write(reinterpret_cast(pcapHeader), 24); @@ -134,12 +143,12 @@ void Netdump::writePcapHeader(Stream& s) const void Netdump::printDumpProcess(Print& out, Packet::PacketDetail ndd, const Packet& np) const { - out.printf_P(PSTR("%8d %s"), np.getTime(), np.toString(ndd).c_str()); + out.printf_P(PSTR("%8lld %s"), np.getTime(), np.toString(ndd).c_str()); } void Netdump::fileDumpProcess(File& outfile, const Packet& np) const { - size_t incl_len = np.getPacketSize() > maxPcapLength ? maxPcapLength : np.getPacketSize(); + size_t incl_len = np.getPacketSize() > maxPcapLength ? maxPcapLength : np.getPacketSize(); uint32_t pcapHeader[4]; struct timeval tv; @@ -148,7 +157,7 @@ void Netdump::fileDumpProcess(File& outfile, const Packet& np) const pcapHeader[1] = tv.tv_usec; pcapHeader[2] = incl_len; pcapHeader[3] = np.getPacketSize(); - outfile.write(reinterpret_cast(pcapHeader), 16); // pcap record header + outfile.write(reinterpret_cast(pcapHeader), 16); // pcap record header outfile.write(np.rawData(), incl_len); } @@ -162,16 +171,16 @@ void Netdump::tcpDumpProcess(const Packet& np) } size_t incl_len = np.getPacketSize() > maxPcapLength ? maxPcapLength : np.getPacketSize(); - if (bufferIndex + 16 + incl_len < tcpBufferSize) // only add if enough space available + if (bufferIndex + 16 + incl_len < tcpBufferSize) // only add if enough space available { struct timeval tv; gettimeofday(&tv, nullptr); uint32_t* pcapHeader = reinterpret_cast(&packetBuffer[bufferIndex]); - pcapHeader[0] = tv.tv_sec; // add pcap record header - pcapHeader[1] = tv.tv_usec; - pcapHeader[2] = incl_len; - pcapHeader[3] = np.getPacketSize(); - bufferIndex += 16; // pcap header size + pcapHeader[0] = tv.tv_sec; // add pcap record header + pcapHeader[1] = tv.tv_usec; + pcapHeader[2] = incl_len; + pcapHeader[3] = np.getPacketSize(); + bufferIndex += 16; // pcap header size memcpy(&packetBuffer[bufferIndex], np.rawData(), incl_len); bufferIndex += incl_len; } @@ -183,20 +192,22 @@ void Netdump::tcpDumpProcess(const Packet& np) } } -void Netdump::tcpDumpLoop(WiFiServer &tcpDumpServer, const Filter nf) +void Netdump::tcpDumpLoop(WiFiServer& tcpDumpServer, const Filter nf) { if (tcpDumpServer.hasClient()) { - tcpDumpClient = tcpDumpServer.available(); + tcpDumpClient = tcpDumpServer.accept(); tcpDumpClient.setNoDelay(true); bufferIndex = 0; writePcapHeader(tcpDumpClient); - setCallback([this](const Packet & ndp) - { - tcpDumpProcess(ndp); - }, nf); + setCallback( + [this](const Packet& ndp) + { + tcpDumpProcess(ndp); + }, + nf); } if (!tcpDumpClient || !tcpDumpClient.connected()) { @@ -210,11 +221,12 @@ void Netdump::tcpDumpLoop(WiFiServer &tcpDumpServer, const Filter nf) if (tcpDumpServer.status() != CLOSED) { - schedule_function([&tcpDumpServer, this, nf]() - { - tcpDumpLoop(tcpDumpServer, nf); - }); + schedule_function( + [&tcpDumpServer, this, nf]() + { + tcpDumpLoop(tcpDumpServer, nf); + }); } } -} // namespace NetCapture +} // namespace NetCapture diff --git a/libraries/Netdump/src/Netdump.h b/libraries/Netdump/src/Netdump.h index 1011a8e955..928000622b 100644 --- a/libraries/Netdump/src/Netdump.h +++ b/libraries/Netdump/src/Netdump.h @@ -38,9 +38,8 @@ using namespace experimental::CBListImplentation; class Netdump { public: - - using Filter = std::function; - using Callback = std::function; + using Filter = std::function; + using Callback = std::function; using LwipCallback = std::function; Netdump(); @@ -53,15 +52,14 @@ class Netdump void printDump(Print& out, Packet::PacketDetail ndd, const Filter nf = nullptr); void fileDump(File& outfile, const Filter nf = nullptr); - void tcpDump(WiFiServer &tcpDumpServer, const Filter nf = nullptr); - + bool tcpDump(WiFiServer& tcpDumpServer, const Filter nf = nullptr); private: Callback netDumpCallback = nullptr; Filter netDumpFilter = nullptr; static void capture(int netif_idx, const char* data, size_t len, int out, int success); - static CallBackList lwipCallback; + static CallBackList lwipCallback; CallBackList::CallBackHandler lwipHandler; void netdumpCapture(int netif_idx, const char* data, size_t len, int out, int success); @@ -69,19 +67,19 @@ class Netdump void printDumpProcess(Print& out, Packet::PacketDetail ndd, const Packet& np) const; void fileDumpProcess(File& outfile, const Packet& np) const; void tcpDumpProcess(const Packet& np); - void tcpDumpLoop(WiFiServer &tcpDumpServer, const Filter nf); + void tcpDumpLoop(WiFiServer& tcpDumpServer, const Filter nf); void writePcapHeader(Stream& s) const; WiFiClient tcpDumpClient; - char* packetBuffer = nullptr; - size_t bufferIndex = 0; + char* packetBuffer = nullptr; + int bufferIndex = 0; - static constexpr int tcpBufferSize = 2048; - static constexpr int maxPcapLength = 1024; - static constexpr uint32_t pcapMagic = 0xa1b2c3d4; + static constexpr int tcpBufferSize = 2048; + static constexpr int maxPcapLength = 1024; + static constexpr uint32_t pcapMagic = 0xa1b2c3d4; }; -} // namespace NetCapture +} // namespace NetCapture #endif /* __NETDUMP_H */ diff --git a/libraries/Netdump/src/NetdumpIP.cpp b/libraries/Netdump/src/NetdumpIP.cpp index d66459d801..f71b618f1d 100644 --- a/libraries/Netdump/src/NetdumpIP.cpp +++ b/libraries/Netdump/src/NetdumpIP.cpp @@ -24,11 +24,10 @@ namespace NetCapture { -NetdumpIP::NetdumpIP() -{ -} +NetdumpIP::NetdumpIP() { } -NetdumpIP::NetdumpIP(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet) +NetdumpIP::NetdumpIP(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, + uint8_t fourth_octet) { setV4(); (*this)[0] = first_octet; @@ -37,7 +36,7 @@ NetdumpIP::NetdumpIP(uint8_t first_octet, uint8_t second_octet, uint8_t third_oc (*this)[3] = fourth_octet; } -NetdumpIP::NetdumpIP(const uint8_t *address, bool v4) +NetdumpIP::NetdumpIP(const uint8_t* address, bool v4) { uint8_t cnt; if (v4) @@ -88,7 +87,7 @@ NetdumpIP::NetdumpIP(const String& ip) } } -bool NetdumpIP::fromString(const char *address) +bool NetdumpIP::fromString(const char* address) { if (!fromString4(address)) { @@ -97,12 +96,12 @@ bool NetdumpIP::fromString(const char *address) return true; } -bool NetdumpIP::fromString4(const char *address) +bool NetdumpIP::fromString4(const char* address) { // TODO: (IPv4) add support for "a", "a.b", "a.b.c" formats - uint16_t acc = 0; // Accumulator - uint8_t dots = 0; + uint16_t acc = 0; // Accumulator + uint8_t dots = 0; while (*address) { @@ -124,7 +123,7 @@ bool NetdumpIP::fromString4(const char *address) return false; } (*this)[dots++] = acc; - acc = 0; + acc = 0; } else { @@ -144,12 +143,12 @@ bool NetdumpIP::fromString4(const char *address) return true; } -bool NetdumpIP::fromString6(const char *address) +bool NetdumpIP::fromString6(const char* address) { // TODO: test test test - uint32_t acc = 0; // Accumulator - int dots = 0, doubledots = -1; + uint32_t acc = 0; // Accumulator + int dots = 0, doubledots = -1; while (*address) { @@ -162,7 +161,7 @@ bool NetdumpIP::fromString6(const char *address) } acc = acc * 16 + (c - '0'); if (acc > 0xffff) - // Value out of range + // Value out of range { return false; } @@ -172,7 +171,7 @@ bool NetdumpIP::fromString6(const char *address) if (*address == ':') { if (doubledots >= 0) - // :: allowed once + // :: allowed once { return false; } @@ -181,22 +180,22 @@ bool NetdumpIP::fromString6(const char *address) address++; } if (dots == 7) - // too many separators + // too many separators { return false; } reinterpret_cast(rawip)[dots++] = PP_HTONS(acc); - acc = 0; + acc = 0; } else - // Invalid char + // Invalid char { return false; } } if (doubledots == -1 && dots != 7) - // Too few separators + // Too few separators { return false; } @@ -206,7 +205,8 @@ bool NetdumpIP::fromString6(const char *address) { for (int i = dots - doubledots - 1; i >= 0; i--) { - reinterpret_cast(rawip)[8 - dots + doubledots + i] = reinterpret_cast(rawip)[doubledots + i]; + reinterpret_cast(rawip)[8 - dots + doubledots + i] + = reinterpret_cast(rawip)[doubledots + i]; } for (int i = doubledots; i < 8 - dots + doubledots; i++) { @@ -223,12 +223,11 @@ String NetdumpIP::toString() StreamString sstr; if (isV6()) { - sstr.reserve(40); // 8 shorts x 4 chars each + 7 colons + nullterm - + sstr.reserve(40); // 8 shorts x 4 chars each + 7 colons + nullterm } else { - sstr.reserve(16); // 4 bytes with 3 chars max + 3 dots + nullterm, or '(IP unset)' + sstr.reserve(16); // 4 bytes with 3 chars max + 3 dots + nullterm, or '(IP unset)' } printTo(sstr); return sstr; @@ -251,9 +250,9 @@ size_t NetdumpIP::printTo(Print& p) uint16_t bit = PP_NTOHS(reinterpret_cast(rawip)[i]); if (bit || count0 < 0) { - n += p.printf("%x", bit); + n += p.printf_P(PSTR("%x"), bit); if (count0 > 0) - // no more hiding 0 + // no more hiding 0 { count0 = -8; } @@ -280,7 +279,7 @@ size_t NetdumpIP::printTo(Print& p) return n; } -bool NetdumpIP::compareRaw(IPversion v, const uint8_t* a, const uint8_t* b) const +bool NetdumpIP::compareRaw(IPversion v, const uint8_t* a, const uint8_t* b) const { for (int i = 0; i < (v == IPversion::IPV4 ? 4 : 16); i++) { @@ -296,7 +295,7 @@ bool NetdumpIP::compareIP(const IPAddress& ip) const { switch (ipv) { - case IPversion::UNSET : + case IPversion::UNSET: if (ip.isSet()) { return false; @@ -306,7 +305,7 @@ bool NetdumpIP::compareIP(const IPAddress& ip) const return true; } break; - case IPversion::IPV4 : + case IPversion::IPV4: if (ip.isV6() || !ip.isSet()) { return false; @@ -316,7 +315,7 @@ bool NetdumpIP::compareIP(const IPAddress& ip) const return compareRaw(IPversion::IPV4, rawip, reinterpret_cast(&ip.v4())); } break; - case IPversion::IPV6 : + case IPversion::IPV6: if (ip.isV4() || !ip.isSet()) { return false; @@ -326,7 +325,7 @@ bool NetdumpIP::compareIP(const IPAddress& ip) const return compareRaw(IPversion::IPV6, rawip, reinterpret_cast(ip.raw6())); } break; - default : + default: return false; break; } @@ -336,7 +335,7 @@ bool NetdumpIP::compareIP(const NetdumpIP& nip) const { switch (ipv) { - case IPversion::UNSET : + case IPversion::UNSET: if (nip.isSet()) { return false; @@ -346,7 +345,7 @@ bool NetdumpIP::compareIP(const NetdumpIP& nip) const return true; } break; - case IPversion::IPV4 : + case IPversion::IPV4: if (nip.isV6() || !nip.isSet()) { return false; @@ -356,7 +355,7 @@ bool NetdumpIP::compareIP(const NetdumpIP& nip) const return compareRaw(IPversion::IPV4, rawip, nip.rawip); } break; - case IPversion::IPV6 : + case IPversion::IPV6: if (nip.isV4() || !nip.isSet()) { return false; @@ -366,10 +365,10 @@ bool NetdumpIP::compareIP(const NetdumpIP& nip) const return compareRaw(IPversion::IPV6, rawip, nip.rawip); } break; - default : + default: return false; break; } } -} // namespace NetCapture +} // namespace NetCapture diff --git a/libraries/Netdump/src/NetdumpIP.h b/libraries/Netdump/src/NetdumpIP.h index 8a450c374a..18747a9103 100644 --- a/libraries/Netdump/src/NetdumpIP.h +++ b/libraries/Netdump/src/NetdumpIP.h @@ -22,7 +22,7 @@ class NetdumpIP NetdumpIP(); NetdumpIP(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet); - NetdumpIP(const uint8_t *address, bool V4 = true); + NetdumpIP(const uint8_t* address, bool V4 = true); NetdumpIP(const IPAddress& ip); NetdumpIP(const String& ip); @@ -31,15 +31,20 @@ class NetdumpIP return rawip[index]; } - bool fromString(const char *address); + bool fromString(const char* address); String toString(); private: - enum class IPversion {UNSET, IPV4, IPV6}; + enum class IPversion + { + UNSET, + IPV4, + IPV6 + }; IPversion ipv = IPversion::UNSET; - uint8_t rawip[16] = {0}; + uint8_t rawip[16] = { 0 }; void setV4() { @@ -70,14 +75,15 @@ class NetdumpIP return (ipv != IPversion::UNSET); }; - bool compareRaw(IPversion v, const uint8_t* a, const uint8_t* b) const; + bool compareRaw(IPversion v, const uint8_t* a, const uint8_t* b) const; bool compareIP(const IPAddress& ip) const; bool compareIP(const NetdumpIP& nip) const; - bool fromString4(const char *address); - bool fromString6(const char *address); + bool fromString4(const char* address); + bool fromString6(const char* address); size_t printTo(Print& p); + public: bool operator==(const IPAddress& addr) const { @@ -95,9 +101,8 @@ class NetdumpIP { return !compareIP(addr); }; - }; -} // namespace NetCapture +} // namespace NetCapture #endif /* LIBRARIES_ESPGOODIES_HR_SRC_NETDUMP_NETDUMPIP_H_ */ diff --git a/libraries/Netdump/src/NetdumpPacket.cpp b/libraries/Netdump/src/NetdumpPacket.cpp index 4e2fcbaf6f..49efaecc1a 100644 --- a/libraries/Netdump/src/NetdumpPacket.cpp +++ b/libraries/Netdump/src/NetdumpPacket.cpp @@ -25,7 +25,8 @@ namespace NetCapture { -void Packet::printDetail(Print& out, const String& indent, const char* data, size_t size, PacketDetail pd) const +void Packet::printDetail(Print& out, const String& indent, const char* data, size_t size, + PacketDetail pd) const { if (pd == PacketDetail::NONE) { @@ -42,21 +43,21 @@ void Packet::printDetail(Print& out, const String& indent, const char* data, siz { end = size; } - out.printf("%s", indent.c_str()); + out.printf_P(PSTR("%s"), indent.c_str()); if (pd != PacketDetail::CHAR) { for (size_t i = start; i < end; i++) { - out.printf("%02x ", (unsigned char)data[i]); + out.printf_P(PSTR("%02x "), (unsigned char)data[i]); } for (size_t i = end; i < start + charCount; i++) { - out.print(" "); + out.printf_P(PSTR(" ")); } } for (size_t i = start; i < end; i++) { - out.printf("%c", data[i] >= 32 && data[i] < 128 ? data[i] : '.'); + out.printf_P(PSTR("%c"), data[i] >= 32 && data[i] < 128 ? data[i] : '.'); } out.println(); @@ -159,21 +160,24 @@ void Packet::MACtoString(int dataIdx, StreamString& sstr) const sstr.print(':'); } } - } void Packet::ARPtoString(PacketDetail netdumpDetail, StreamString& sstr) const { switch (getARPType()) { - case 1 : sstr.printf_P(PSTR("who has %s tell %s"), getIP(ETH_HDR_LEN + 24).toString().c_str(), getIP(ETH_HDR_LEN + 14).toString().c_str()); + case 1: + sstr.printf_P(PSTR("who has %s tell %s"), getIP(ETH_HDR_LEN + 24).toString().c_str(), + getIP(ETH_HDR_LEN + 14).toString().c_str()); break; - case 2 : sstr.printf_P(PSTR("%s is at "), getIP(ETH_HDR_LEN + 14).toString().c_str()); + case 2: + sstr.printf_P(PSTR("%s is at "), getIP(ETH_HDR_LEN + 14).toString().c_str()); MACtoString(ETH_HDR_LEN + 8, sstr); break; } sstr.printf("\r\n"); - printDetail(sstr, PSTR(" D "), &data[ETH_HDR_LEN], packetLength - ETH_HDR_LEN, netdumpDetail); + printDetail(sstr, PSTR(" D "), &data[ETH_HDR_LEN], packetLength - ETH_HDR_LEN, + netdumpDetail); } void Packet::DNStoString(PacketDetail netdumpDetail, StreamString& sstr) const @@ -198,8 +202,10 @@ void Packet::DNStoString(PacketDetail netdumpDetail, StreamString& sstr) const sstr.printf_P(PSTR("DR=%d "), t); } sstr.printf_P(PSTR("\r\n")); - printDetail(sstr, PSTR(" H "), &data[ETH_HDR_LEN + getIpHdrLen()], getUdpHdrLen(), netdumpDetail); - printDetail(sstr, PSTR(" D "), &data[ETH_HDR_LEN + getIpHdrLen() + getUdpHdrLen()], getUdpLen(), netdumpDetail); + printDetail(sstr, PSTR(" H "), &data[ETH_HDR_LEN + getIpHdrLen()], getUdpHdrLen(), + netdumpDetail); + printDetail(sstr, PSTR(" D "), &data[ETH_HDR_LEN + getIpHdrLen() + getUdpHdrLen()], + getUdpLen(), netdumpDetail); } void Packet::UDPtoString(PacketDetail netdumpDetail, StreamString& sstr) const @@ -207,8 +213,10 @@ void Packet::UDPtoString(PacketDetail netdumpDetail, StreamString& sstr) const sstr.printf_P(PSTR("%s>%s "), sourceIP().toString().c_str(), destIP().toString().c_str()); sstr.printf_P(PSTR("%d:%d"), getSrcPort(), getDstPort()); sstr.printf_P(PSTR("\r\n")); - printDetail(sstr, PSTR(" H "), &data[ETH_HDR_LEN + getIpHdrLen()], getUdpHdrLen(), netdumpDetail); - printDetail(sstr, PSTR(" D "), &data[ETH_HDR_LEN + getIpHdrLen() + getUdpHdrLen()], getUdpLen(), netdumpDetail); + printDetail(sstr, PSTR(" H "), &data[ETH_HDR_LEN + getIpHdrLen()], getUdpHdrLen(), + netdumpDetail); + printDetail(sstr, PSTR(" D "), &data[ETH_HDR_LEN + getIpHdrLen() + getUdpHdrLen()], + getUdpLen(), netdumpDetail); } void Packet::TCPtoString(PacketDetail netdumpDetail, StreamString& sstr) const @@ -217,17 +225,20 @@ void Packet::TCPtoString(PacketDetail netdumpDetail, StreamString& sstr) const sstr.printf_P(PSTR("%d:%d "), getSrcPort(), getDstPort()); uint16_t flags = getTcpFlags(); sstr.print('['); - const char chars [] = "FSRPAUECN"; + const char chars[] = "FSRPAUECN"; for (uint8_t i = 0; i < sizeof chars; i++) if (flags & (1 << i)) { sstr.print(chars[i]); } sstr.print(']'); - sstr.printf_P(PSTR(" len: %u seq: %u, ack: %u, wnd: %u "), getTcpLen(), getTcpSeq(), getTcpAck(), getTcpWindow()); + sstr.printf_P(PSTR(" len: %u seq: %u, ack: %u, wnd: %u "), getTcpLen(), getTcpSeq(), + getTcpAck(), getTcpWindow()); sstr.printf_P(PSTR("\r\n")); - printDetail(sstr, PSTR(" H "), &data[ETH_HDR_LEN + getIpHdrLen()], getTcpHdrLen(), netdumpDetail); - printDetail(sstr, PSTR(" D "), &data[ETH_HDR_LEN + getIpHdrLen() + getTcpHdrLen()], getTcpLen(), netdumpDetail); + printDetail(sstr, PSTR(" H "), &data[ETH_HDR_LEN + getIpHdrLen()], getTcpHdrLen(), + netdumpDetail); + printDetail(sstr, PSTR(" D "), &data[ETH_HDR_LEN + getIpHdrLen() + getTcpHdrLen()], + getTcpLen(), netdumpDetail); } void Packet::ICMPtoString(PacketDetail, StreamString& sstr) const @@ -237,41 +248,81 @@ void Packet::ICMPtoString(PacketDetail, StreamString& sstr) const { switch (getIcmpType()) { - case 0 : sstr.printf_P(PSTR("ping reply")); break; - case 8 : sstr.printf_P(PSTR("ping request")); break; - default: sstr.printf_P(PSTR("type(0x%02x)"), getIcmpType()); break; + case 0: + sstr.printf_P(PSTR("ping reply")); + break; + case 8: + sstr.printf_P(PSTR("ping request")); + break; + default: + sstr.printf_P(PSTR("type(0x%02x)"), getIcmpType()); + break; } } if (isIPv6()) { switch (getIcmpType()) { - case 129 : sstr.printf_P(PSTR("ping reply")); break; - case 128 : sstr.printf_P(PSTR("ping request")); break; - case 135 : sstr.printf_P(PSTR("Neighbour solicitation")); break; - case 136 : sstr.printf_P(PSTR("Neighbour advertisement")); break; - default: sstr.printf_P(PSTR("type(0x%02x)"), getIcmpType()); break; + case 129: + sstr.printf_P(PSTR("ping reply")); + break; + case 128: + sstr.printf_P(PSTR("ping request")); + break; + case 135: + sstr.printf_P(PSTR("Neighbour solicitation")); + break; + case 136: + sstr.printf_P(PSTR("Neighbour advertisement")); + break; + default: + sstr.printf_P(PSTR("type(0x%02x)"), getIcmpType()); + break; } } - sstr.printf("\r\n"); + sstr.printf_P(PSTR("\r\n")); } void Packet::IGMPtoString(PacketDetail, StreamString& sstr) const { switch (getIgmpType()) { - case 1 : sstr.printf_P(PSTR("Create Group Request")); break; - case 2 : sstr.printf_P(PSTR("Create Group Reply")); break; - case 3 : sstr.printf_P(PSTR("Join Group Request")); break; - case 4 : sstr.printf_P(PSTR("Join Group Reply")); break; - case 5 : sstr.printf_P(PSTR("Leave Group Request")); break; - case 6 : sstr.printf_P(PSTR("Leave Group Reply")); break; - case 7 : sstr.printf_P(PSTR("Confirm Group Request")); break; - case 8 : sstr.printf_P(PSTR("Confirm Group Reply")); break; - case 0x11 : sstr.printf_P(PSTR("Group Membership Query")); break; - case 0x12 : sstr.printf_P(PSTR("IGMPv1 Membership Report")); break; - case 0x22 : sstr.printf_P(PSTR("IGMPv3 Membership Report")); break; - default: sstr.printf_P(PSTR("type(0x%02x)"), getIgmpType()); break; + case 1: + sstr.printf_P(PSTR("Create Group Request")); + break; + case 2: + sstr.printf_P(PSTR("Create Group Reply")); + break; + case 3: + sstr.printf_P(PSTR("Join Group Request")); + break; + case 4: + sstr.printf_P(PSTR("Join Group Reply")); + break; + case 5: + sstr.printf_P(PSTR("Leave Group Request")); + break; + case 6: + sstr.printf_P(PSTR("Leave Group Reply")); + break; + case 7: + sstr.printf_P(PSTR("Confirm Group Request")); + break; + case 8: + sstr.printf_P(PSTR("Confirm Group Reply")); + break; + case 0x11: + sstr.printf_P(PSTR("Group Membership Query")); + break; + case 0x12: + sstr.printf_P(PSTR("IGMPv1 Membership Report")); + break; + case 0x22: + sstr.printf_P(PSTR("IGMPv3 Membership Report")); + break; + default: + sstr.printf_P(PSTR("type(0x%02x)"), getIgmpType()); + break; } sstr.printf_P(PSTR("\r\n")); } @@ -281,7 +332,8 @@ void Packet::IPtoString(PacketDetail netdumpDetail, StreamString& sstr) const sstr.printf_P(PSTR("%s>%s "), sourceIP().toString().c_str(), destIP().toString().c_str()); sstr.printf_P(PSTR("Unknown IP type : %d\r\n"), ipType()); printDetail(sstr, PSTR(" H "), &data[ETH_HDR_LEN], getIpHdrLen(), netdumpDetail); - printDetail(sstr, PSTR(" D "), &data[ETH_HDR_LEN + getIpHdrLen()], getIpTotalLen() - getIpHdrLen(), netdumpDetail); + printDetail(sstr, PSTR(" D "), &data[ETH_HDR_LEN + getIpHdrLen()], + getIpTotalLen() - getIpHdrLen(), netdumpDetail); } void Packet::UKNWtoString(PacketDetail, StreamString& sstr) const @@ -298,13 +350,13 @@ const String Packet::toString() const return toString(PacketDetail::NONE); } - const String Packet::toString(PacketDetail netdumpDetail) const { StreamString sstr; sstr.reserve(128); - sstr.printf_P(PSTR("%d %3s %-4s "), netif_idx, out ? "out" : "in ", packetType().toString().c_str()); + sstr.printf_P(PSTR("%d %3s %-4s "), netif_idx, out ? "out" : "in ", + packetType().toString().c_str()); if (netdumpDetail == PacketDetail::RAW) { @@ -320,56 +372,56 @@ const String Packet::toString(PacketDetail netdumpDetail) const switch (thisPacketType) { - case PacketType::ARP : + case PacketType::ARP: { ARPtoString(netdumpDetail, sstr); break; } - case PacketType::MDNS : - case PacketType::DNS : + case PacketType::MDNS: + case PacketType::DNS: { DNStoString(netdumpDetail, sstr); break; } - case PacketType::SSDP : - case PacketType::DHCP : - case PacketType::WSDD : - case PacketType::NETBIOS : - case PacketType::SMB : - case PacketType::OTA : - case PacketType::UDP : + case PacketType::SSDP: + case PacketType::DHCP: + case PacketType::WSDD: + case PacketType::NETBIOS: + case PacketType::SMB: + case PacketType::OTA: + case PacketType::UDP: { UDPtoString(netdumpDetail, sstr); break; } - case PacketType::TCP : - case PacketType::HTTP : + case PacketType::TCP: + case PacketType::HTTP: { TCPtoString(netdumpDetail, sstr); break; } - case PacketType::ICMP : + case PacketType::ICMP: { ICMPtoString(netdumpDetail, sstr); break; } - case PacketType::IGMP : + case PacketType::IGMP: { IGMPtoString(netdumpDetail, sstr); break; } - case PacketType::IPv4 : - case PacketType::IPv6 : + case PacketType::IPv4: + case PacketType::IPv6: { IPtoString(netdumpDetail, sstr); break; } - case PacketType::UKNW : + case PacketType::UKNW: { UKNWtoString(netdumpDetail, sstr); break; } - default : + default: { sstr.printf_P(PSTR("Non identified packet\r\n")); break; @@ -378,4 +430,4 @@ const String Packet::toString(PacketDetail netdumpDetail) const return sstr; } -} // namespace NetCapture +} // namespace NetCapture diff --git a/libraries/Netdump/src/NetdumpPacket.h b/libraries/Netdump/src/NetdumpPacket.h index a898ef230f..37e20abb2e 100644 --- a/libraries/Netdump/src/NetdumpPacket.h +++ b/libraries/Netdump/src/NetdumpPacket.h @@ -37,8 +37,8 @@ int constexpr ETH_HDR_LEN = 14; class Packet { public: - Packet(unsigned long msec, int n, const char* d, size_t l, int o, int s) - : packetTime(msec), netif_idx(n), data(d), packetLength(l), out(o), success(s) + Packet(unsigned long msec, int n, const char* d, size_t l, int o, int s) : + packetTime(msec), netif_idx(n), data(d), packetLength(l), out(o), success(s) { setPacketTypes(); }; @@ -75,7 +75,7 @@ class Packet { return ntoh16(idx + 2) | (((uint32_t)ntoh16(idx)) << 16); }; - uint8_t byteData(uint16_t idx) const + uint8_t byteData(uint16_t idx) const { return data[idx]; } @@ -87,17 +87,18 @@ class Packet { return ntoh16(12); }; - uint8_t ipType() const + uint8_t ipType() const { return isIP() ? isIPv4() ? data[ETH_HDR_LEN + 9] : data[ETH_HDR_LEN + 6] : 0; }; uint16_t getIpHdrLen() const { - return isIPv4() ? (((unsigned char)data[ETH_HDR_LEN]) & 0x0f) << 2 : 40 ; // IPv6 is fixed length + return isIPv4() ? (((unsigned char)data[ETH_HDR_LEN]) & 0x0f) << 2 + : 40; // IPv6 is fixed length } uint16_t getIpTotalLen() const { - return isIP() ? isIPv4() ? ntoh16(ETH_HDR_LEN + 2) : (packetLength - ETH_HDR_LEN) : 0; + return isIP() ? isIPv4() ? ntoh16(ETH_HDR_LEN + 2) : (packetLength - ETH_HDR_LEN) : 0; } uint32_t getTcpSeq() const { @@ -115,20 +116,20 @@ class Packet { return isTCP() ? ntoh16(ETH_HDR_LEN + getIpHdrLen() + 14) : 0; } - uint8_t getTcpHdrLen() const + uint8_t getTcpHdrLen() const { return isTCP() ? (data[ETH_HDR_LEN + getIpHdrLen() + 12] >> 4) * 4 : 0; - };//Header len is in multiple of 4 bytes + }; // Header len is in multiple of 4 bytes uint16_t getTcpLen() const { - return isTCP() ? getIpTotalLen() - getIpHdrLen() - getTcpHdrLen() : 0 ; + return isTCP() ? getIpTotalLen() - getIpHdrLen() - getTcpHdrLen() : 0; }; - uint8_t getIcmpType() const + uint8_t getIcmpType() const { return isICMP() ? data[ETH_HDR_LEN + getIpHdrLen() + 0] : 0; } - uint8_t getIgmpType() const + uint8_t getIgmpType() const { return isIGMP() ? data[ETH_HDR_LEN + getIpHdrLen() + 0] : 0; } @@ -136,11 +137,11 @@ class Packet { return isARP() ? data[ETH_HDR_LEN + 7] : 0; } - bool is_ARP_who() const + bool is_ARP_who() const { return (getARPType() == 1); } - bool is_ARP_is() const + bool is_ARP_is() const { return (getARPType() == 2); } @@ -244,7 +245,7 @@ class Packet return ip; }; - bool hasIP(NetdumpIP ip) const + bool hasIP(NetdumpIP ip) const { return (isIP() && ((ip == sourceIP()) || (ip == destIP()))); } @@ -270,21 +271,20 @@ class Packet { return isIP() ? ntoh16(ETH_HDR_LEN + getIpHdrLen() + 2) : 0; } - bool hasPort(uint16_t p) const + bool hasPort(uint16_t p) const { return (isIP() && ((getSrcPort() == p) || (getDstPort() == p))); } const String toString() const; const String toString(PacketDetail netdumpDetail) const; - void printDetail(Print& out, const String& indent, const char* data, size_t size, PacketDetail pd) const; + void printDetail(Print& out, const String& indent, const char* data, size_t size, + PacketDetail pd) const; - const PacketType packetType() const; + const PacketType packetType() const; const std::vector& allPacketTypes() const; - private: - void setPacketType(PacketType); void setPacketTypes(); @@ -298,17 +298,16 @@ class Packet void IPtoString(PacketDetail netdumpDetail, StreamString& sstr) const; void UKNWtoString(PacketDetail netdumpDetail, StreamString& sstr) const; - - time_t packetTime; - int netif_idx; - const char* data; - size_t packetLength; - int out; - int success; - PacketType thisPacketType; + time_t packetTime; + int netif_idx; + const char* data; + size_t packetLength; + int out; + int success; + PacketType thisPacketType; std::vector thisAllPacketTypes; }; -} // namespace NetCapture +} // namespace NetCapture #endif /* __NETDUMP_PACKET_H */ diff --git a/libraries/Netdump/src/PacketType.cpp b/libraries/Netdump/src/PacketType.cpp index 565aa55f0a..9e08e31c23 100644 --- a/libraries/Netdump/src/PacketType.cpp +++ b/libraries/Netdump/src/PacketType.cpp @@ -10,33 +10,50 @@ namespace NetCapture { -PacketType::PacketType() -{ -} +PacketType::PacketType() { } String PacketType::toString() const { switch (ptype) { - case PType::ARP : return PSTR("ARP"); - case PType::IP : return PSTR("IP"); - case PType::UDP : return PSTR("UDP"); - case PType::MDNS : return PSTR("MDNS"); - case PType::DNS : return PSTR("DNS"); - case PType::SSDP : return PSTR("SSDP"); - case PType::DHCP : return PSTR("DHCP"); - case PType::WSDD : return PSTR("WSDD"); - case PType::NETBIOS: return PSTR("NBIO"); - case PType::SMB : return PSTR("SMB"); - case PType::OTA : return PSTR("OTA"); - case PType::TCP : return PSTR("TCP"); - case PType::HTTP : return PSTR("HTTP"); - case PType::ICMP : return PSTR("ICMP"); - case PType::IGMP : return PSTR("IGMP"); - case PType::IPv4: return PSTR("IPv4"); - case PType::IPv6: return PSTR("IPv6"); - case PType::UKNW : return PSTR("UKNW"); - default : return PSTR("ERR"); + case PType::ARP: + return PSTR("ARP"); + case PType::IP: + return PSTR("IP"); + case PType::UDP: + return PSTR("UDP"); + case PType::MDNS: + return PSTR("MDNS"); + case PType::DNS: + return PSTR("DNS"); + case PType::SSDP: + return PSTR("SSDP"); + case PType::DHCP: + return PSTR("DHCP"); + case PType::WSDD: + return PSTR("WSDD"); + case PType::NETBIOS: + return PSTR("NBIO"); + case PType::SMB: + return PSTR("SMB"); + case PType::OTA: + return PSTR("OTA"); + case PType::TCP: + return PSTR("TCP"); + case PType::HTTP: + return PSTR("HTTP"); + case PType::ICMP: + return PSTR("ICMP"); + case PType::IGMP: + return PSTR("IGMP"); + case PType::IPv4: + return PSTR("IPv4"); + case PType::IPv6: + return PSTR("IPv6"); + case PType::UKNW: + return PSTR("UKNW"); + default: + return PSTR("ERR"); }; } diff --git a/libraries/Netdump/src/PacketType.h b/libraries/Netdump/src/PacketType.h index 8f4aa5ce79..bbf5b61d64 100644 --- a/libraries/Netdump/src/PacketType.h +++ b/libraries/Netdump/src/PacketType.h @@ -15,7 +15,6 @@ namespace NetCapture class PacketType { public: - enum PType : int { ARP, diff --git a/libraries/SD/examples/Datalogger/Datalogger.ino b/libraries/SD/examples/Datalogger/Datalogger.ino index a023b63821..c71149f41a 100644 --- a/libraries/SD/examples/Datalogger/Datalogger.ino +++ b/libraries/SD/examples/Datalogger/Datalogger.ino @@ -48,9 +48,7 @@ void loop() { for (int analogPin = 0; analogPin < 3; analogPin++) { int sensor = analogRead(analogPin); dataString += String(sensor); - if (analogPin < 2) { - dataString += ","; - } + if (analogPin < 2) { dataString += ","; } } // open the file. note that only one file can be open at a time, @@ -65,16 +63,5 @@ void loop() { Serial.println(dataString); } // if the file isn't open, pop up an error: - else { - Serial.println("error opening datalog.txt"); - } + else { Serial.println("error opening datalog.txt"); } } - - - - - - - - - diff --git a/libraries/SD/examples/DumpFile/DumpFile.ino b/libraries/SD/examples/DumpFile/DumpFile.ino index 3574652890..f96861db1d 100644 --- a/libraries/SD/examples/DumpFile/DumpFile.ino +++ b/libraries/SD/examples/DumpFile/DumpFile.ino @@ -45,17 +45,11 @@ void setup() { // if the file is available, write to it: if (dataFile) { - while (dataFile.available()) { - Serial.write(dataFile.read()); - } + while (dataFile.available()) { Serial.write(dataFile.read()); } dataFile.close(); } // if the file isn't open, pop up an error: - else { - Serial.println("error opening datalog.txt"); - } -} - -void loop() { + else { Serial.println("error opening datalog.txt"); } } +void loop() {} diff --git a/libraries/SD/examples/Files/Files.ino b/libraries/SD/examples/Files/Files.ino index 1c1b83ec20..f162837dcf 100644 --- a/libraries/SD/examples/Files/Files.ino +++ b/libraries/SD/examples/Files/Files.ino @@ -66,6 +66,3 @@ void setup() { void loop() { // nothing happens after setup finishes. } - - - diff --git a/libraries/SD/examples/ReadWrite/ReadWrite.ino b/libraries/SD/examples/ReadWrite/ReadWrite.ino index 7aa9a40c4b..90742198b9 100644 --- a/libraries/SD/examples/ReadWrite/ReadWrite.ino +++ b/libraries/SD/examples/ReadWrite/ReadWrite.ino @@ -57,9 +57,7 @@ void setup() { Serial.println("test.txt:"); // read from the file until there's nothing else in it: - while (myFile.available()) { - Serial.write(myFile.read()); - } + while (myFile.available()) { Serial.write(myFile.read()); } // close the file: myFile.close(); } else { @@ -71,5 +69,3 @@ void setup() { void loop() { // nothing happens after setup } - - diff --git a/libraries/SD/examples/listfiles/listfiles.ino b/libraries/SD/examples/listfiles/listfiles.ino index 55478d7080..1eaec2300d 100644 --- a/libraries/SD/examples/listfiles/listfiles.ino +++ b/libraries/SD/examples/listfiles/listfiles.ino @@ -52,14 +52,12 @@ void loop() { void printDirectory(File dir, int numTabs) { while (true) { - File entry = dir.openNextFile(); - if (! entry) { + File entry = dir.openNextFile(); + if (!entry) { // no more files break; } - for (uint8_t i = 0; i < numTabs; i++) { - Serial.print('\t'); - } + for (uint8_t i = 0; i < numTabs; i++) { Serial.print('\t'); } Serial.print(entry.name()); if (entry.isDirectory()) { Serial.println("/"); @@ -70,7 +68,7 @@ void printDirectory(File dir, int numTabs) { Serial.print(entry.size(), DEC); time_t cr = entry.getCreationTime(); time_t lw = entry.getLastWrite(); - struct tm * tmstruct = localtime(&cr); + 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); 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/library.properties b/libraries/SD/library.properties index 5091024bb6..72210dfb17 100644 --- a/libraries/SD/library.properties +++ b/libraries/SD/library.properties @@ -1,4 +1,4 @@ -name=SD(esp8266) +name=SD version=2.0.0 author=Earle F. Philhower, III maintainer=Earle F. Philhower, III 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 fee20da548..8511dfe584 100644 --- a/libraries/SD/src/SD.h +++ b/libraries/SD/src/SD.h @@ -24,16 +24,49 @@ #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 sdfat::O_READ -#undef FILE_WRITE -#define FILE_WRITE (sdfat::O_READ | sdfat::O_WRITE | sdfat::O_CREAT | sdfat::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: - boolean begin(uint8_t csPin, uint32_t cfg = SPI_HALF_SPEED) { - SDFS.setConfig(SDFSConfig(csPin, cfg)); + bool begin(uint8_t csPin, uint32_t cfg = SPI_HALF_SPEED) { + SDFS.setConfig(SDFSConfig(csPin, cfg)); return (boolean)SDFS.begin(); } @@ -44,19 +77,19 @@ class SDClass { } } - File open(const char *filename, uint8_t mode = FILE_READ) { - return SDFS.open(filename, getMode(mode)); + fs::File open(const char *filename, uint8_t mode = FILE_READ) { + return SDFS.open(filename, SDClassFileMode(mode)); } - File open(const char *filename, const char *mode) { + fs::File open(const char *filename, const char *mode) { return SDFS.open(filename, mode); } - File open(const String &filename, uint8_t mode = FILE_READ) { + fs::File open(const String &filename, uint8_t mode = FILE_READ) { return open(filename.c_str(), mode); } - File open(const String &filename, const char *mode) { + fs::File open(const String &filename, const char *mode) { return open(filename.c_str(), mode); } @@ -135,8 +168,8 @@ class SDClass { size_t size() { uint64_t sz = size64(); #ifdef DEBUG_ESP_PORT - if (sz > (uint64_t)SIZE_MAX) { - DEBUG_ESP_PORT.printf_P(PSTR("WARNING: SD card size overflow (%lld>= 4GB). Please update source to use size64().\n"), sz); + if (sz > std::numeric_limits::max()) { + DEBUG_ESP_PORT.printf_P(PSTR("WARNING: SD card size overflow (%lld >= 4GB). Please update source to use size64().\n"), (long long)sz); } #endif return (size_t)sz; @@ -158,18 +191,6 @@ class SDClass { } private: - const char *getMode(uint8_t mode) { - bool read = (mode & sdfat::O_READ) ? true : false; - bool write = (mode & sdfat::O_WRITE) ? true : false; - bool append = (mode & sdfat::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) { @@ -183,10 +204,6 @@ class SDClass { }; -// Expose FatStructs.h helpers for MSDOS date/time for use with dateTimeCallback -static inline uint16_t FAT_DATE(uint16_t year, uint8_t month, uint8_t day) { - return (year - 1980) << 9 | month << 5 | day; -} static inline uint16_t FAT_YEAR(uint16_t fatDate) { return 1980 + (fatDate >> 9); } @@ -196,9 +213,6 @@ static inline uint8_t FAT_MONTH(uint16_t fatDate) { static inline uint8_t FAT_DAY(uint16_t fatDate) { return fatDate & 0X1F; } -static inline uint16_t FAT_TIME(uint8_t hour, uint8_t minute, uint8_t second) { - return hour << 11 | minute << 5 | second >> 1; -} static inline uint8_t FAT_HOUR(uint16_t fatTime) { return fatTime >> 11; } diff --git a/libraries/SDFS/src/SDFS.cpp b/libraries/SDFS/src/SDFS.cpp index e7a891a08a..5725a6ae08 100644 --- a/libraries/SDFS/src/SDFS.cpp +++ b/libraries/SDFS/src/SDFS.cpp @@ -64,13 +64,13 @@ FileImplPtr SDFSImpl::open(const char* path, OpenMode openMode, AccessMode acces } free(pathStr); } - sdfat::File32 fd = _fs.open(path, flags); + File32 fd = _fs.open(path, flags); if (!fd) { DEBUGV("SDFSImpl::openFile: fd=%p path=`%s` openMode=%d accessMode=%d", &fd, path, openMode, accessMode); return FileImplPtr(); } - auto sharedFd = std::make_shared(fd); + auto sharedFd = std::make_shared(fd); return std::make_shared(this, sharedFd, path); } @@ -90,14 +90,14 @@ DirImplPtr SDFSImpl::openDir(const char* path) } // At this point we have a name of "/blah/blah/blah" or "blah" or "" // If that references a directory, just open it and we're done. - sdfat::File32 dirFile; + File32 dirFile; const char *filter = ""; if (!pathStr[0]) { // openDir("") === openDir("/") - dirFile = _fs.open("/", sdfat::O_RDONLY); + dirFile = _fs.open("/", O_RDONLY); filter = ""; } else if (_fs.exists(pathStr)) { - dirFile = _fs.open(pathStr, sdfat::O_RDONLY); + dirFile = _fs.open(pathStr, O_RDONLY); if (dirFile.isDir()) { // Easy peasy, path specifies an existing dir! filter = ""; @@ -107,12 +107,12 @@ DirImplPtr SDFSImpl::openDir(const char* path) char *ptr = strrchr(pathStr, '/'); if (!ptr) { // No slashes, open the root dir - dirFile = _fs.open("/", sdfat::O_RDONLY); + dirFile = _fs.open("/", O_RDONLY); filter = pathStr; } else { // We've got slashes, open the dir one up *ptr = 0; // Remove slash, truncare string - dirFile = _fs.open(pathStr, sdfat::O_RDONLY); + dirFile = _fs.open(pathStr, O_RDONLY); filter = ptr + 1; } } @@ -122,12 +122,12 @@ DirImplPtr SDFSImpl::openDir(const char* path) char *ptr = strrchr(pathStr, '/'); if (!ptr) { // No slashes, open the root dir - dirFile = _fs.open("/", sdfat::O_RDONLY); + dirFile = _fs.open("/", O_RDONLY); filter = pathStr; } else { // We've got slashes, open the dir one up *ptr = 0; // Remove slash, truncare string - dirFile = _fs.open(pathStr, sdfat::O_RDONLY); + dirFile = _fs.open(pathStr, O_RDONLY); filter = ptr + 1; } } @@ -135,7 +135,7 @@ DirImplPtr SDFSImpl::openDir(const char* path) DEBUGV("SDFSImpl::openDir: path=`%s`\n", path); return DirImplPtr(); } - auto sharedDir = std::make_shared(dirFile); + auto sharedDir = std::make_shared(dirFile); auto ret = std::make_shared(filter, this, sharedDir, pathStr); free(pathStr); return ret; @@ -145,12 +145,12 @@ bool SDFSImpl::format() { if (_mounted) { return false; } - sdfat::SdCardFactory cardFactory; - sdfat::SdCard* card = cardFactory.newCard(sdfat::SdSpiConfig(_cfg._csPin, DEDICATED_SPI, _cfg._spiSettings)); + SdCardFactory cardFactory; + SdCard* card = cardFactory.newCard(SdSpiConfig(_cfg._csPin, DEDICATED_SPI, _cfg._spiSettings)); if (!card || card->errorCode()) { return false; } - sdfat::FatFormatter fatFormatter; + FatFormatter fatFormatter; uint8_t *sectorBuffer = new uint8_t[512]; bool ret = fatFormatter.format(card, sectorBuffer, nullptr); delete[] sectorBuffer; diff --git a/libraries/SDFS/src/SDFS.h b/libraries/SDFS/src/SDFS.h index 0051852a2d..a916e576ad 100644 --- a/libraries/SDFS/src/SDFS.h +++ b/libraries/SDFS/src/SDFS.h @@ -29,20 +29,17 @@ */ #include #include -#include "FS.h" -#include "FSImpl.h" +#include #include "debug.h" #include #include #include -using namespace fs; - namespace sdfs { class SDFSFileImpl; class SDFSDirImpl; -class SDFSConfig : public FSConfig +class SDFSConfig : public fs::FSConfig { public: static constexpr uint32_t FSId = 0x53444653; @@ -72,41 +69,41 @@ class SDFSConfig : public FSConfig uint32_t _spiSettings; }; -class SDFSImpl : public FSImpl +class SDFSImpl : public fs::FSImpl { public: SDFSImpl() : _mounted(false) { } - FileImplPtr open(const char* path, OpenMode openMode, AccessMode accessMode) override; + fs::FileImplPtr open(const char* path, fs::OpenMode openMode, fs::AccessMode accessMode) override; bool exists(const char* path) override { return _mounted ? _fs.exists(path) : false; } - DirImplPtr openDir(const char* path) override; + fs::DirImplPtr openDir(const char* path) override; bool rename(const char* pathFrom, const char* pathTo) override { return _mounted ? _fs.rename(pathFrom, pathTo) : false; } - bool info64(FSInfo64& info) override { + bool info64(fs::FSInfo64& info) override { if (!_mounted) { DEBUGV("SDFS::info: FS not mounted\n"); return false; } info.maxOpenFiles = 999; // TODO - not valid - info.blockSize = _fs.vol()->sectorsPerCluster() * _fs.vol()->bytesPerSector(); + info.blockSize = _fs.vol()->bytesPerCluster(); info.pageSize = 0; // TODO ? info.maxPathLength = 255; // TODO ? info.totalBytes =_fs.vol()->clusterCount() * info.blockSize; - info.usedBytes = info.totalBytes - (_fs.vol()->freeClusterCount() * _fs.vol()->sectorsPerCluster() * _fs.vol()->bytesPerSector()); + info.usedBytes = (_fs.vol()->clusterCount() - _fs.vol()->freeClusterCount()) * info.blockSize; return true; } - bool info(FSInfo& info) override { - FSInfo64 i; + bool info(fs::FSInfo& info) override { + fs::FSInfo64 i; if (!info64(i)) { return false; } @@ -115,9 +112,9 @@ class SDFSImpl : public FSImpl info.maxOpenFiles = i.maxOpenFiles; info.maxPathLength = i.maxPathLength; #ifdef DEBUG_ESP_PORT - if (i.totalBytes > (uint64_t)SIZE_MAX) { + if (i.totalBytes > std::numeric_limits::max()) { // This catches both total and used cases, since used must always be < total. - DEBUG_ESP_PORT.printf_P(PSTR("WARNING: SD card size overflow (%lld>= 4GB). Please update source to use info64().\n"), i.totalBytes); + DEBUG_ESP_PORT.printf_P(PSTR("WARNING: SD card size overflow (%lld >= 4GB). Please update source to use info64().\n"), (long long)i.totalBytes); } #endif info.totalBytes = (size_t)i.totalBytes; @@ -137,7 +134,7 @@ class SDFSImpl : public FSImpl return _mounted ?_fs.rmdir(path) : false; } - bool setConfig(const FSConfig &cfg) override + bool setConfig(const fs::FSConfig &cfg) override { if ((cfg._type != SDFSConfig::FSId) || _mounted) { DEBUGV("SDFS::setConfig: invalid config or already mounted\n"); @@ -149,14 +146,14 @@ class SDFSImpl : public FSImpl bool begin() override { if (_mounted) { - end(); + return true; } _mounted = _fs.begin(_cfg._csPin, _cfg._spiSettings); if (!_mounted && _cfg._autoFormat) { format(); _mounted = _fs.begin(_cfg._csPin, _cfg._spiSettings); } - sdfat::FsDateTime::setCallback(dateTimeCB); + FsDateTime::setCallback(dateTimeCB); return _mounted; } @@ -185,7 +182,7 @@ class SDFSImpl : public FSImpl return (totalClusters() / blocksPerCluster()); } size_t clusterSize() { - return blocksPerCluster() * _fs.vol()->bytesPerSector(); + return _fs.vol()->bytesPerCluster(); } size_t size() { return (clusterSize() * totalClusters()); @@ -229,42 +226,42 @@ class SDFSImpl : public FSImpl friend class SDFileImpl; friend class SDFSDirImpl; - sdfat::SdFat* getFs() - { + SdFat* getFs() { return &_fs; } - static uint8_t _getFlags(OpenMode openMode, AccessMode accessMode) { + static uint8_t _getFlags(fs::OpenMode openMode, fs::AccessMode accessMode) { uint8_t mode = 0; - if (openMode & OM_CREATE) { - mode |= sdfat::O_CREAT; - } - if (openMode & OM_APPEND) { - mode |= sdfat::O_AT_END; + if (openMode & fs::OM_CREATE) { + mode |= O_CREAT; } - if (openMode & OM_TRUNCATE) { - mode |= sdfat::O_TRUNC; + if (openMode & fs::OM_APPEND) { + mode |= O_AT_END; } - if (accessMode & AM_READ) { - mode |= sdfat::O_READ; + if (openMode & fs::OM_TRUNCATE) { + mode |= O_TRUNC; } - if (accessMode & AM_WRITE) { - mode |= sdfat::O_WRITE; + if ((accessMode & (fs::AM_READ | fs::AM_WRITE)) == (fs::AM_READ | fs::AM_WRITE)) { + mode |= O_RDWR; + } else if (accessMode & fs::AM_READ) { + mode |= O_READ; + } else if (accessMode & fs::AM_WRITE) { + mode |= O_WRITE; } return mode; } - sdfat::SdFat _fs; + SdFat _fs; SDFSConfig _cfg; bool _mounted; }; -class SDFSFileImpl : public FileImpl +class SDFSFileImpl : public fs::FileImpl { public: - SDFSFileImpl(SDFSImpl *fs, std::shared_ptr fd, const char *name) + SDFSFileImpl(SDFSImpl *fs, std::shared_ptr fd, const char *name) : _fs(fs), _fd(fd), _opened(true) { _name = std::shared_ptr(new char[strlen(name) + 1], std::default_delete()); @@ -287,7 +284,7 @@ class SDFSFileImpl : public FileImpl return _opened ? _fd->write(buf, size) : -1; } - size_t read(uint8_t* buf, size_t size) override + int read(uint8_t* buf, size_t size) override { return _opened ? _fd->read(buf, size) : -1; } @@ -299,22 +296,22 @@ class SDFSFileImpl : public FileImpl } } - bool seek(uint32_t pos, SeekMode mode) override + bool seek(uint32_t pos, fs::SeekMode mode) override { if (!_opened) { return false; } switch (mode) { - case SeekSet: + case fs::SeekSet: return _fd->seekSet(pos); - case SeekEnd: + case fs::SeekEnd: return _fd->seekEnd(-pos); // TODO again, odd from POSIX - case SeekCur: + case fs::SeekCur: return _fd->seekCur(pos); default: // Should not be hit, we've got an invalid seek mode DEBUGV("SDFSFileImpl::seek: invalid seek mode %d\n", mode); - assert((mode==SeekSet) || (mode==SeekEnd) || (mode==SeekCur)); // Will fail and give meaningful assert message + assert((mode==fs::SeekSet) || (mode==fs::SeekEnd) || (mode==fs::SeekCur)); // Will fail and give meaningful assert message return false; } } @@ -380,7 +377,7 @@ class SDFSFileImpl : public FileImpl time_t getLastWrite() override { time_t ftime = 0; if (_opened && _fd) { - sdfat::DirFat_t tmp; + DirFat_t tmp; if (_fd.get()->dirEntry(&tmp)) { ftime = SDFSImpl::FatToTimeT(*(uint16_t*)tmp.modifyDate, *(uint16_t*)tmp.modifyTime); } @@ -391,7 +388,7 @@ class SDFSFileImpl : public FileImpl time_t getCreationTime() override { time_t ftime = 0; if (_opened && _fd) { - sdfat::DirFat_t tmp; + DirFat_t tmp; if (_fd.get()->dirEntry(&tmp)) { ftime = SDFSImpl::FatToTimeT(*(uint16_t*)tmp.createDate, *(uint16_t*)tmp.createTime); } @@ -400,16 +397,16 @@ class SDFSFileImpl : public FileImpl } protected: - SDFSImpl* _fs; - std::shared_ptr _fd; - std::shared_ptr _name; - bool _opened; + SDFSImpl* _fs; + std::shared_ptr _fd; + std::shared_ptr _name; + bool _opened; }; -class SDFSDirImpl : public DirImpl +class SDFSDirImpl : public fs::DirImpl { public: - SDFSDirImpl(const String& pattern, SDFSImpl* fs, std::shared_ptr dir, const char *dirPath = nullptr) + SDFSDirImpl(const String& pattern, SDFSImpl* fs, std::shared_ptr dir, const char *dirPath = nullptr) : _pattern(pattern), _fs(fs), _dir(dir), _valid(false), _dirPath(nullptr) { if (dirPath) { @@ -423,10 +420,10 @@ class SDFSDirImpl : public DirImpl _dir->close(); } - FileImplPtr openFile(OpenMode openMode, AccessMode accessMode) override + fs::FileImplPtr openFile(fs::OpenMode openMode, fs::AccessMode accessMode) override { if (!_valid) { - return FileImplPtr(); + return fs::FileImplPtr(); } // MAX_PATH on FAT32 is potentially 260 bytes per most implementations char tmpName[260]; @@ -484,14 +481,14 @@ class SDFSDirImpl : public DirImpl { const int n = _pattern.length(); do { - sdfat::File32 file; - file.openNext(_dir.get(), sdfat::O_READ); + File32 file; + file.openNext(_dir.get(), O_READ); if (file) { _valid = 1; _size = file.fileSize(); _isFile = file.isFile(); _isDirectory = file.isDir(); - sdfat::DirFat_t tmp; + DirFat_t tmp; if (file.dirEntry(&tmp)) { _time = SDFSImpl::FatToTimeT(*(uint16_t*)tmp.modifyDate, *(uint16_t*)tmp.modifyTime); _creation = SDFSImpl::FatToTimeT(*(uint16_t*)tmp.createDate, *(uint16_t*)tmp.createTime); @@ -516,23 +513,23 @@ class SDFSDirImpl : public DirImpl } protected: - String _pattern; - SDFSImpl* _fs; - std::shared_ptr _dir; - bool _valid; - char _lfn[64]; - time_t _time; - time_t _creation; - std::shared_ptr _dirPath; - uint32_t _size; - bool _isFile; - bool _isDirectory; + String _pattern; + SDFSImpl* _fs; + std::shared_ptr _dir; + bool _valid; + char _lfn[64]; + time_t _time; + time_t _creation; + std::shared_ptr _dirPath; + uint32_t _size; + bool _isFile; + bool _isDirectory; }; }; // namespace sdfs #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SDFS) -extern FS SDFS; +extern fs::FS SDFS; using sdfs::SDFSConfig; #endif 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/SPISlave/examples/SPISlave_Master/SPISlave_Master.ino b/libraries/SPISlave/examples/SPISlave_Master/SPISlave_Master.ino index 528e52ed81..5e97614366 100644 --- a/libraries/SPISlave/examples/SPISlave_Master/SPISlave_Master.ino +++ b/libraries/SPISlave/examples/SPISlave_Master/SPISlave_Master.ino @@ -17,73 +17,68 @@ #include class ESPMaster { - private: - uint8_t _ss_pin; +private: + uint8_t _ss_pin; - public: - ESPMaster(uint8_t pin): _ss_pin(pin) {} - void begin() { - pinMode(_ss_pin, OUTPUT); - digitalWrite(_ss_pin, HIGH); - } +public: + ESPMaster(uint8_t pin) + : _ss_pin(pin) {} + void begin() { + pinMode(_ss_pin, OUTPUT); + digitalWrite(_ss_pin, HIGH); + } - uint32_t readStatus() { - digitalWrite(_ss_pin, LOW); - SPI.transfer(0x04); - uint32_t status = (SPI.transfer(0) | ((uint32_t)(SPI.transfer(0)) << 8) | ((uint32_t)(SPI.transfer(0)) << 16) | ((uint32_t)(SPI.transfer(0)) << 24)); - digitalWrite(_ss_pin, HIGH); - return status; - } + uint32_t readStatus() { + digitalWrite(_ss_pin, LOW); + SPI.transfer(0x04); + uint32_t status = (SPI.transfer(0) | ((uint32_t)(SPI.transfer(0)) << 8) | ((uint32_t)(SPI.transfer(0)) << 16) | ((uint32_t)(SPI.transfer(0)) << 24)); + digitalWrite(_ss_pin, HIGH); + return status; + } - void writeStatus(uint32_t status) { - digitalWrite(_ss_pin, LOW); - SPI.transfer(0x01); - SPI.transfer(status & 0xFF); - SPI.transfer((status >> 8) & 0xFF); - SPI.transfer((status >> 16) & 0xFF); - SPI.transfer((status >> 24) & 0xFF); - digitalWrite(_ss_pin, HIGH); - } + void writeStatus(uint32_t status) { + digitalWrite(_ss_pin, LOW); + SPI.transfer(0x01); + SPI.transfer(status & 0xFF); + SPI.transfer((status >> 8) & 0xFF); + SPI.transfer((status >> 16) & 0xFF); + SPI.transfer((status >> 24) & 0xFF); + digitalWrite(_ss_pin, HIGH); + } - void readData(uint8_t * data) { - digitalWrite(_ss_pin, LOW); - SPI.transfer(0x03); - SPI.transfer(0x00); - for (uint8_t i = 0; i < 32; i++) { - data[i] = SPI.transfer(0); - } - digitalWrite(_ss_pin, HIGH); - } + void readData(uint8_t *data) { + digitalWrite(_ss_pin, LOW); + SPI.transfer(0x03); + SPI.transfer(0x00); + for (uint8_t i = 0; i < 32; i++) { data[i] = SPI.transfer(0); } + digitalWrite(_ss_pin, HIGH); + } - void writeData(uint8_t * data, size_t len) { - uint8_t i = 0; - digitalWrite(_ss_pin, LOW); - SPI.transfer(0x02); - SPI.transfer(0x00); - while (len-- && i < 32) { - SPI.transfer(data[i++]); - } - while (i++ < 32) { - SPI.transfer(0); - } - digitalWrite(_ss_pin, HIGH); - } + void writeData(uint8_t *data, size_t len) { + uint8_t i = 0; + digitalWrite(_ss_pin, LOW); + SPI.transfer(0x02); + SPI.transfer(0x00); + while (len-- && i < 32) { SPI.transfer(data[i++]); } + while (i++ < 32) { SPI.transfer(0); } + digitalWrite(_ss_pin, HIGH); + } - String readData() { - char data[33]; - data[32] = 0; - readData((uint8_t *)data); - return String(data); - } + String readData() { + char data[33]; + data[32] = 0; + readData((uint8_t *)data); + return String(data); + } - void writeData(const char * data) { - writeData((uint8_t *)data, strlen(data)); - } + void writeData(const char *data) { + writeData((uint8_t *)data, strlen(data)); + } }; ESPMaster esp(SS); -void send(const char * message) { +void send(const char *message) { Serial.print("Master: "); Serial.println(message); esp.writeData(message); diff --git a/libraries/SPISlave/examples/SPISlave_SafeMaster/SPISlave_SafeMaster.ino b/libraries/SPISlave/examples/SPISlave_SafeMaster/SPISlave_SafeMaster.ino index 04c23c4502..95d407f271 100644 --- a/libraries/SPISlave/examples/SPISlave_SafeMaster/SPISlave_SafeMaster.ino +++ b/libraries/SPISlave/examples/SPISlave_SafeMaster/SPISlave_SafeMaster.ino @@ -18,77 +18,73 @@ #include class ESPSafeMaster { - private: - uint8_t _ss_pin; - void _pulseSS() { - digitalWrite(_ss_pin, HIGH); - delayMicroseconds(5); - digitalWrite(_ss_pin, LOW); - } - public: - ESPSafeMaster(uint8_t pin): _ss_pin(pin) {} - void begin() { - pinMode(_ss_pin, OUTPUT); - _pulseSS(); - } +private: + uint8_t _ss_pin; + void _pulseSS() { + digitalWrite(_ss_pin, HIGH); + delayMicroseconds(5); + digitalWrite(_ss_pin, LOW); + } - uint32_t readStatus() { - _pulseSS(); - SPI.transfer(0x04); - uint32_t status = (SPI.transfer(0) | ((uint32_t)(SPI.transfer(0)) << 8) | ((uint32_t)(SPI.transfer(0)) << 16) | ((uint32_t)(SPI.transfer(0)) << 24)); - _pulseSS(); - return status; - } +public: + ESPSafeMaster(uint8_t pin) + : _ss_pin(pin) {} + void begin() { + pinMode(_ss_pin, OUTPUT); + _pulseSS(); + } - void writeStatus(uint32_t status) { - _pulseSS(); - SPI.transfer(0x01); - SPI.transfer(status & 0xFF); - SPI.transfer((status >> 8) & 0xFF); - SPI.transfer((status >> 16) & 0xFF); - SPI.transfer((status >> 24) & 0xFF); - _pulseSS(); - } + uint32_t readStatus() { + _pulseSS(); + SPI.transfer(0x04); + uint32_t status = (SPI.transfer(0) | ((uint32_t)(SPI.transfer(0)) << 8) | ((uint32_t)(SPI.transfer(0)) << 16) | ((uint32_t)(SPI.transfer(0)) << 24)); + _pulseSS(); + return status; + } - void readData(uint8_t * data) { - _pulseSS(); - SPI.transfer(0x03); - SPI.transfer(0x00); - for (uint8_t i = 0; i < 32; i++) { - data[i] = SPI.transfer(0); - } - _pulseSS(); - } + void writeStatus(uint32_t status) { + _pulseSS(); + SPI.transfer(0x01); + SPI.transfer(status & 0xFF); + SPI.transfer((status >> 8) & 0xFF); + SPI.transfer((status >> 16) & 0xFF); + SPI.transfer((status >> 24) & 0xFF); + _pulseSS(); + } - void writeData(uint8_t * data, size_t len) { - uint8_t i = 0; - _pulseSS(); - SPI.transfer(0x02); - SPI.transfer(0x00); - while (len-- && i < 32) { - SPI.transfer(data[i++]); - } - while (i++ < 32) { - SPI.transfer(0); - } - _pulseSS(); - } + void readData(uint8_t *data) { + _pulseSS(); + SPI.transfer(0x03); + SPI.transfer(0x00); + for (uint8_t i = 0; i < 32; i++) { data[i] = SPI.transfer(0); } + _pulseSS(); + } - String readData() { - char data[33]; - data[32] = 0; - readData((uint8_t *)data); - return String(data); - } + void writeData(uint8_t *data, size_t len) { + uint8_t i = 0; + _pulseSS(); + SPI.transfer(0x02); + SPI.transfer(0x00); + while (len-- && i < 32) { SPI.transfer(data[i++]); } + while (i++ < 32) { SPI.transfer(0); } + _pulseSS(); + } - void writeData(const char * data) { - writeData((uint8_t *)data, strlen(data)); - } + String readData() { + char data[33]; + data[32] = 0; + readData((uint8_t *)data); + return String(data); + } + + void writeData(const char *data) { + writeData((uint8_t *)data, strlen(data)); + } }; ESPSafeMaster esp(SS); -void send(const char * message) { +void send(const char *message) { Serial.print("Master: "); Serial.println(message); esp.writeData(message); diff --git a/libraries/SPISlave/examples/SPISlave_Test/SPISlave_Test.ino b/libraries/SPISlave/examples/SPISlave_Test/SPISlave_Test.ino index 0be83fd298..3b8bf0e01c 100644 --- a/libraries/SPISlave/examples/SPISlave_Test/SPISlave_Test.ino +++ b/libraries/SPISlave/examples/SPISlave_Test/SPISlave_Test.ino @@ -24,9 +24,9 @@ void setup() { // data has been received from the master. Beware that len is always 32 // and the buffer is autofilled with zeroes if data is less than 32 bytes long // It's up to the user to implement protocol for handling data length - SPISlave.onData([](uint8_t * data, size_t len) { + SPISlave.onData([](uint8_t *data, size_t len) { String message = String((char *)data); - (void) len; + (void)len; if (message.equals("Hello Slave!")) { SPISlave.setData("Hello Master!"); } else if (message.equals("Are you alive?")) { @@ -50,7 +50,7 @@ void setup() { // Can be used to exchange small data or status information SPISlave.onStatus([](uint32_t data) { Serial.printf("Status: %u\n", data); - SPISlave.setStatus(millis()); //set next status + SPISlave.setStatus(millis()); // set next status }); // The master has read the status register diff --git a/libraries/SPISlave/src/hspi_slave.c b/libraries/SPISlave/src/hspi_slave.c index 8462b84146..c9df941dcd 100644 --- a/libraries/SPISlave/src/hspi_slave.c +++ b/libraries/SPISlave/src/hspi_slave.c @@ -28,7 +28,7 @@ static void (*_hspi_slave_rx_status_cb)(void * arg, uint32_t data) = NULL; static void (*_hspi_slave_tx_status_cb)(void * arg) = NULL; static uint8_t _hspi_slave_buffer[33]; -void ICACHE_RAM_ATTR _hspi_slave_isr_handler(void *arg, void *frame) +void IRAM_ATTR _hspi_slave_isr_handler(void *arg, void *frame) { (void) frame; uint32_t status; @@ -124,7 +124,7 @@ void hspi_slave_end() SPI1P = B110; } -void ICACHE_RAM_ATTR hspi_slave_setStatus(uint32_t status) +void IRAM_ATTR hspi_slave_setStatus(uint32_t status) { SPI1WS = status; } diff --git a/libraries/Servo/examples/Sweep/Sweep.ino b/libraries/Servo/examples/Sweep/Sweep.ino index 5a6bd6ef40..9149366df2 100644 --- a/libraries/Servo/examples/Sweep/Sweep.ino +++ b/libraries/Servo/examples/Sweep/Sweep.ino @@ -23,14 +23,13 @@ void setup() { void loop() { int pos; - for (pos = 0; pos <= 180; pos += 1) { // goes from 0 degrees to 180 degrees + for (pos = 0; pos <= 180; pos += 1) { // goes from 0 degrees to 180 degrees // in steps of 1 degree - myservo.write(pos); // tell servo to go to position in variable 'pos' - delay(15); // waits 15ms for the servo to reach the position + myservo.write(pos); // tell servo to go to position in variable 'pos' + delay(15); // waits 15ms for the servo to reach the position } - for (pos = 180; pos >= 0; pos -= 1) { // goes from 180 degrees to 0 degrees - myservo.write(pos); // tell servo to go to position in variable 'pos' - delay(15); // waits 15ms for the servo to reach the position + for (pos = 180; pos >= 0; pos -= 1) { // goes from 180 degrees to 0 degrees + myservo.write(pos); // tell servo to go to position in variable 'pos' + delay(15); // waits 15ms for the servo to reach the position } } - diff --git a/libraries/Servo/library.properties b/libraries/Servo/library.properties index df32b66167..4ac805852b 100644 --- a/libraries/Servo/library.properties +++ b/libraries/Servo/library.properties @@ -1,4 +1,4 @@ -name=Servo(esp8266) +name=Servo version=1.0.2 author=Michael C. Miller maintainer=GitHub/esp8266/arduino diff --git a/libraries/Servo/src/Servo.cpp b/libraries/Servo/src/Servo.cpp index d24ea33799..1d19f62683 100644 --- a/libraries/Servo/src/Servo.cpp +++ b/libraries/Servo/src/Servo.cpp @@ -26,14 +26,14 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA uint32_t Servo::_servoMap = 0; -// similiar to map but will have increased accuracy that provides a more +// similar to map but will have increased accuracy that provides a more // symmetrical api (call it and use result to reverse will provide the original value) int improved_map(int value, int minIn, int maxIn, int minOut, int maxOut) { const int rangeIn = maxIn - minIn; const int rangeOut = maxOut - minOut; const int deltaIn = value - minIn; - // fixed point math constants to improve accurancy of divide and rounding + // fixed point math constants to improve accuracy of divide and rounding constexpr int fixedHalfDecimal = 1; constexpr int fixedDecimal = fixedHalfDecimal * 2; @@ -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) @@ -69,13 +69,8 @@ uint8_t Servo::attach(int pin, uint16_t minUs, uint16_t maxUs) uint8_t Servo::attach(int pin, uint16_t minUs, uint16_t maxUs, int value) { if (!_attached) { -#ifdef WAVEFORM_LOCKED_PHASE pinMode(pin, OUTPUT); digitalWrite(pin, LOW); -#else - digitalWrite(pin, LOW); - pinMode(pin, OUTPUT); -#endif _pin = pin; _attached = true; } @@ -95,15 +90,10 @@ void Servo::detach() { if (_attached) { _servoMap &= ~(1 << _pin); -#ifdef WAVEFORM_LOCKED_PHASE startWaveform(_pin, 0, REFRESH_INTERVAL, 1); -#else - // TODO - timeHigh == 0 is illegal in _PWM code branch. Do nothing for now. -#endif delay(REFRESH_INTERVAL / 1000); // long enough to complete active period under all circumstances. stopWaveform(_pin); _attached = false; - _valueUs = DEFAULT_NEUTRAL_PULSE_WIDTH; } } @@ -124,13 +114,9 @@ void Servo::writeMicroseconds(int value) _valueUs = value; if (_attached) { _servoMap &= ~(1 << _pin); -#ifdef WAVEFORM_LOCKED_PHASE // Find the first GPIO being generated by checking GCC's find-first-set (returns 1 + the bit of the first 1 in an int32_t) int phaseReference = __builtin_ffs(_servoMap) - 1; if (startWaveform(_pin, _valueUs, REFRESH_INTERVAL - _valueUs, 0, phaseReference)) -#else - if (startWaveform(_pin, _valueUs, REFRESH_INTERVAL - _valueUs, 0)) -#endif { _servoMap |= (1 << _pin); } diff --git a/libraries/Servo/src/Servo.h b/libraries/Servo/src/Servo.h index 38d78709a4..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 @@ -47,7 +48,7 @@ // The following values are in us (microseconds). // Since the defaults can be overwritten in the new attach() member function, // they were modified from the Arduino AVR defaults to be in the safe range -// of publically available specifications. While this implies that many 180° +// of publicly available specifications. While this implies that many 180° // servos do not operate the full 0° to 180° sweep using these, it also prevents // unsuspecting damage. For Arduino AVR, the same change is being discussed. #define DEFAULT_MIN_PULSE_WIDTH 1000 // uncalibrated default, the shortest duty cycle sent to a servo diff --git a/libraries/SoftwareSerial b/libraries/SoftwareSerial index 1fa01d7608..bcfd6d10e6 160000 --- a/libraries/SoftwareSerial +++ b/libraries/SoftwareSerial @@ -1 +1 @@ -Subproject commit 1fa01d7608beb5b65c177dc6a8cc9626109fa5a3 +Subproject commit bcfd6d10e6a45a0d07705d08728f293defe9cc1d diff --git a/libraries/TFT_Touch_Shield_V2/examples/drawCircle/drawCircle.ino b/libraries/TFT_Touch_Shield_V2/examples/drawCircle/drawCircle.ino index fbc22a861c..6c2d2171de 100644 --- a/libraries/TFT_Touch_Shield_V2/examples/drawCircle/drawCircle.ino +++ b/libraries/TFT_Touch_Shield_V2/examples/drawCircle/drawCircle.ino @@ -8,22 +8,20 @@ #include void setup() { - TFT_BL_ON; //turn on the background light + TFT_BL_ON; // turn on the background light - Tft.TFTinit(); //init TFT library + Tft.TFTinit(); // init TFT library - Tft.drawCircle(100, 100, 30, YELLOW); //center: (100, 100), r = 30 ,color : YELLOW + Tft.drawCircle(100, 100, 30, YELLOW); // center: (100, 100), r = 30 ,color : YELLOW - Tft.drawCircle(100, 200, 40, CYAN); // center: (100, 200), r = 10 ,color : CYAN + Tft.drawCircle(100, 200, 40, CYAN); // center: (100, 200), r = 10 ,color : CYAN - Tft.fillCircle(200, 100, 30, RED); //center: (200, 100), r = 30 ,color : RED + Tft.fillCircle(200, 100, 30, RED); // center: (200, 100), r = 30 ,color : RED - Tft.fillCircle(200, 200, 30, BLUE); //center: (200, 200), r = 30 ,color : BLUE + Tft.fillCircle(200, 200, 30, BLUE); // center: (200, 200), r = 30 ,color : BLUE } -void loop() { - -} +void loop() {} /********************************************************************************************************* END FILE diff --git a/libraries/TFT_Touch_Shield_V2/examples/drawLines/drawLines.ino b/libraries/TFT_Touch_Shield_V2/examples/drawLines/drawLines.ino index a777044d3e..9a2b02d3e0 100644 --- a/libraries/TFT_Touch_Shield_V2/examples/drawLines/drawLines.ino +++ b/libraries/TFT_Touch_Shield_V2/examples/drawLines/drawLines.ino @@ -8,21 +8,19 @@ #include #include void setup() { - TFT_BL_ON; // turn on the background light - Tft.TFTinit(); //init TFT library + TFT_BL_ON; // turn on the background light + Tft.TFTinit(); // init TFT library - Tft.drawLine(0, 0, 239, 319, RED); //start: (0, 0) end: (239, 319), color : RED + Tft.drawLine(0, 0, 239, 319, RED); // start: (0, 0) end: (239, 319), color : RED Tft.drawVerticalLine(60, 100, 100, GREEN); // Draw a vertical line // start: (60, 100) length: 100 color: green - Tft.drawHorizontalLine(30, 60, 150, BLUE); //Draw a horizontal line - //start: (30, 60), high: 150, color: blue + Tft.drawHorizontalLine(30, 60, 150, BLUE); // Draw a horizontal line + // start: (30, 60), high: 150, color: blue } -void loop() { - -} +void loop() {} /********************************************************************************************************* END FILE diff --git a/libraries/TFT_Touch_Shield_V2/examples/drawNumber/drawNumber.ino b/libraries/TFT_Touch_Shield_V2/examples/drawNumber/drawNumber.ino index ed62233796..35dfc3d6e0 100644 --- a/libraries/TFT_Touch_Shield_V2/examples/drawNumber/drawNumber.ino +++ b/libraries/TFT_Touch_Shield_V2/examples/drawNumber/drawNumber.ino @@ -9,29 +9,26 @@ #include void setup() { - TFT_BL_ON; // turn on the background light + TFT_BL_ON; // turn on the background light - Tft.TFTinit(); // init TFT library + Tft.TFTinit(); // init TFT library - Tft.drawNumber(1024, 0, 0, 1, RED); // draw a integer: 1024, Location: (0, 0), size: 1, color: RED + Tft.drawNumber(1024, 0, 0, 1, RED); // draw a integer: 1024, Location: (0, 0), size: 1, color: RED - Tft.drawNumber(1024, 0, 20, 2, BLUE); // draw a integer: 1024, Location: (0, 20), size: 2, color: BLUE + Tft.drawNumber(1024, 0, 20, 2, BLUE); // draw a integer: 1024, Location: (0, 20), size: 2, color: BLUE - Tft.drawNumber(1024, 0, 50, 3, GREEN); // draw a integer: 1024, Location: (0, 50), size: 3, color: GREEN + Tft.drawNumber(1024, 0, 50, 3, GREEN); // draw a integer: 1024, Location: (0, 50), size: 3, color: GREEN - Tft.drawNumber(1024, 0, 90, 4, BLUE); // draw a integer: 1024, Location: (0, 90), size:4, color: BLUE + Tft.drawNumber(1024, 0, 90, 4, BLUE); // draw a integer: 1024, Location: (0, 90), size:4, color: BLUE - Tft.drawFloat(1.2345, 0, 150, 4, YELLOW); // draw a float number: 1.2345, Location: (0, 150), size: 4, color: YELLOW + Tft.drawFloat(1.2345, 0, 150, 4, YELLOW); // draw a float number: 1.2345, Location: (0, 150), size: 4, color: YELLOW - Tft.drawFloat(1.2345, 2, 0, 200, 4, BLUE); // draw a float number: 1.2345: Location: (0, 200), size: 4, decimal: 2, color: BLUE - - Tft.drawFloat(1.2345, 4, 0, 250, 4, RED); // draw a float number: 1.2345 Location: (0, 250), size: 4, decimal: 4, color: RED + Tft.drawFloat(1.2345, 2, 0, 200, 4, BLUE); // draw a float number: 1.2345: Location: (0, 200), size: 4, decimal: 2, color: BLUE + Tft.drawFloat(1.2345, 4, 0, 250, 4, RED); // draw a float number: 1.2345 Location: (0, 250), size: 4, decimal: 4, color: RED } -void loop() { - -} +void loop() {} /********************************************************************************************************* END FILE diff --git a/libraries/TFT_Touch_Shield_V2/examples/drawRectangle/drawRectangle.ino b/libraries/TFT_Touch_Shield_V2/examples/drawRectangle/drawRectangle.ino index a2d7af8709..0819c77c9b 100644 --- a/libraries/TFT_Touch_Shield_V2/examples/drawRectangle/drawRectangle.ino +++ b/libraries/TFT_Touch_Shield_V2/examples/drawRectangle/drawRectangle.ino @@ -16,9 +16,7 @@ void setup() { Tft.drawRectangle(100, 170, 120, 60, BLUE); } -void loop() { - -} +void loop() {} /********************************************************************************************************* END FILE *********************************************************************************************************/ \ No newline at end of file diff --git a/libraries/TFT_Touch_Shield_V2/examples/paint/paint.ino b/libraries/TFT_Touch_Shield_V2/examples/paint/paint.ino index b6a1310c4a..39826866b9 100644 --- a/libraries/TFT_Touch_Shield_V2/examples/paint/paint.ino +++ b/libraries/TFT_Touch_Shield_V2/examples/paint/paint.ino @@ -5,29 +5,27 @@ #include int ColorPaletteHigh = 30; -int color = WHITE; //Paint brush color -unsigned int colors[8] = {BLACK, RED, GREEN, BLUE, CYAN, YELLOW, WHITE, GRAY1}; +int color = WHITE; // Paint brush color +unsigned int colors[8] = { BLACK, RED, GREEN, BLUE, CYAN, YELLOW, WHITE, GRAY1 }; // For better pressure precision, we need to know the resistance // between X+ and X- Use any multimeter to read it // The 2.8" TFT Touch shield has 300 ohms across the X plate -TouchScreen ts = TouchScreen(XP, YP, XM, YM); //init TouchScreen port pins +TouchScreen ts = TouchScreen(XP, YP, XM, YM); // init TouchScreen port pins void setup() { - Tft.TFTinit(); //init TFT library + Tft.TFTinit(); // init TFT library Serial.begin(115200); - //Draw the pallet - for (int i = 0; i < 8; i++) { - Tft.fillRectangle(i * 30, 0, 30, ColorPaletteHigh, colors[i]); - } + // Draw the pallet + for (int i = 0; i < 8; i++) { Tft.fillRectangle(i * 30, 0, 30, ColorPaletteHigh, colors[i]); } } void loop() { // a point object holds x y and z coordinates. Point p = ts.getPoint(); - //map the ADC value read to into pixel co-ordinates + // map the ADC value read to into pixel coordinates p.x = map(p.x, TS_MINX, TS_MAXX, 0, 240); p.y = map(p.y, TS_MINY, TS_MAXY, 0, 320); diff --git a/libraries/TFT_Touch_Shield_V2/examples/shapes/shapes.ino b/libraries/TFT_Touch_Shield_V2/examples/shapes/shapes.ino index 6607a01cff..5a1e960c9c 100644 --- a/libraries/TFT_Touch_Shield_V2/examples/shapes/shapes.ino +++ b/libraries/TFT_Touch_Shield_V2/examples/shapes/shapes.ino @@ -5,13 +5,13 @@ #include void setup() { - TFT_BL_ON; // turn on the background light - Tft.TFTinit(); // init TFT library + TFT_BL_ON; // turn on the background light + Tft.TFTinit(); // init TFT library } void loop() { - for (int r = 0; r < 115; r = r + 2) { //set r : 0--115 - Tft.drawCircle(119, 160, r, random(0xFFFF)); //draw circle, center:(119, 160), color: random + for (int r = 0; r < 115; r = r + 2) { // set r : 0--115 + Tft.drawCircle(119, 160, r, random(0xFFFF)); // draw circle, center:(119, 160), color: random } delay(10); } diff --git a/libraries/TFT_Touch_Shield_V2/examples/text/text.ino b/libraries/TFT_Touch_Shield_V2/examples/text/text.ino index 03308f6891..b02dedbe98 100644 --- a/libraries/TFT_Touch_Shield_V2/examples/text/text.ino +++ b/libraries/TFT_Touch_Shield_V2/examples/text/text.ino @@ -11,23 +11,19 @@ void setup() { TFT_BL_ON; // turn on the background light Tft.TFTinit(); // init TFT library - Tft.drawChar('S', 0, 0, 1, RED); // draw char: 'S', (0, 0), size: 1, color: RED + Tft.drawChar('S', 0, 0, 1, RED); // draw char: 'S', (0, 0), size: 1, color: RED - Tft.drawChar('E', 10, 10, 2, BLUE); // draw char: 'E', (10, 10), size: 2, color: BLUE + Tft.drawChar('E', 10, 10, 2, BLUE); // draw char: 'E', (10, 10), size: 2, color: BLUE - Tft.drawChar('E', 20, 40, 3, GREEN); // draw char: 'E', (20, 40), size: 3, color: GREEN + Tft.drawChar('E', 20, 40, 3, GREEN); // draw char: 'E', (20, 40), size: 3, color: GREEN - Tft.drawChar('E', 30, 80, 4, YELLOW); // draw char: 'E', (30, 80), size: 4, color: YELLOW + Tft.drawChar('E', 30, 80, 4, YELLOW); // draw char: 'E', (30, 80), size: 4, color: YELLOW - Tft.drawChar('D', 40, 120, 4, YELLOW); // draw char: 'D', (40, 120), size: 4, color: YELLOW - - Tft.drawString("Hello", 0, 180, 3, CYAN); // draw string: "hello", (0, 180), size: 3, color: CYAN - - Tft.drawString("World!!", 60, 220, 4, WHITE); // draw string: "world!!", (80, 230), size: 4, color: WHITE + Tft.drawChar('D', 40, 120, 4, YELLOW); // draw char: 'D', (40, 120), size: 4, color: YELLOW + Tft.drawString("Hello", 0, 180, 3, CYAN); // draw string: "hello", (0, 180), size: 3, color: CYAN + Tft.drawString("World!!", 60, 220, 4, WHITE); // draw string: "world!!", (80, 230), size: 4, color: WHITE } -void loop() { - -} +void loop() {} diff --git a/libraries/TFT_Touch_Shield_V2/examples/tftbmp/tftbmp.ino b/libraries/TFT_Touch_Shield_V2/examples/tftbmp/tftbmp.ino index 4176de59dc..55e4981df9 100644 --- a/libraries/TFT_Touch_Shield_V2/examples/tftbmp/tftbmp.ino +++ b/libraries/TFT_Touch_Shield_V2/examples/tftbmp/tftbmp.ino @@ -16,21 +16,22 @@ #include "TFTv2.h" -#define MAX_BMP 10 // bmp file num -#define FILENAME_LEN 20 // max file name length +#define MAX_BMP 10 // bmp file num +#define FILENAME_LEN 20 // max file name length -const int PIN_SD_CS = 4; // pin of sd card +const int PIN_SD_CS = 4; // pin of sd card -const int __Gnbmp_height = 320; // bmp hight -const int __Gnbmp_width = 240; // bmp width +const int __Gnbmp_height = 320; // bmp height +const int __Gnbmp_width = 240; // bmp width -unsigned char __Gnbmp_image_offset = 0; // offset +unsigned char __Gnbmp_image_offset = 0; // offset -int __Gnfile_num = 3; // num of file +int __Gnfile_num = 3; // num of file -char __Gsbmp_files[3][FILENAME_LEN] = { // add file name here +char __Gsbmp_files[3][FILENAME_LEN] = { + // add file name here "flower.BMP", "hibiscus.bmp", "test.bmp", @@ -54,7 +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!"); @@ -65,12 +66,12 @@ void setup() { void loop() { for (unsigned char i = 0; i < __Gnfile_num; i++) { bmpFile = SD.open(__Gsbmp_files[i]); - if (! bmpFile) { - Serial.println("didnt find image"); + if (!bmpFile) { + Serial.println("didn't find image"); while (1); } - if (! bmpReadHeader(bmpFile)) { + if (!bmpReadHeader(bmpFile)) { Serial.println("bad bmp"); return; } @@ -80,25 +81,24 @@ void loop() { delay(1000); } - } /*********************************************/ // This procedure reads a bitmap and draws it to the screen // its sped up by reading many pixels worth of data at a time -// instead of just one pixel at a time. increading the buffer takes +// instead of just one pixel at a time. increasing the buffer takes // more RAM but makes the drawing a little faster. 20 pixels' worth // is probably a good place -#define BUFFPIXEL 60 // must be a divisor of 240 -#define BUFFPIXEL_X3 180 // BUFFPIXELx3 +#define BUFFPIXEL 60 // must be a divisor of 240 +#define BUFFPIXEL_X3 180 // BUFFPIXELx3 void bmpdraw(File f, int x, int y) { bmpFile.seek(__Gnbmp_image_offset); uint32_t time = millis(); - uint8_t sdbuffer[BUFFPIXEL_X3]; // 3 * pixels to buffer + uint8_t sdbuffer[BUFFPIXEL_X3]; // 3 * pixels to buffer for (int i = 0; i < __Gnbmp_height; i++) { @@ -110,9 +110,9 @@ void bmpdraw(File f, int x, int y) { unsigned int __color[BUFFPIXEL]; for (int k = 0; k < BUFFPIXEL; k++) { - __color[k] = sdbuffer[buffidx + 2] >> 3; // read - __color[k] = __color[k] << 6 | (sdbuffer[buffidx + 1] >> 2); // green - __color[k] = __color[k] << 5 | (sdbuffer[buffidx + 0] >> 3); // blue + __color[k] = sdbuffer[buffidx + 2] >> 3; // read + __color[k] = __color[k] << 6 | (sdbuffer[buffidx + 1] >> 2); // green + __color[k] = __color[k] << 5 | (sdbuffer[buffidx + 0] >> 3); // blue buffidx += 3; } @@ -131,7 +131,6 @@ void bmpdraw(File f, int x, int y) { TFT_CS_HIGH; } - } Serial.print(millis() - time, DEC); @@ -169,13 +168,11 @@ boolean bmpReadHeader(File f) { int bmp_width = read32(f); int bmp_height = read32(f); - if (bmp_width != __Gnbmp_width || bmp_height != __Gnbmp_height) { // if image is not 320x240, return false + if (bmp_width != __Gnbmp_width || bmp_height != __Gnbmp_height) { // if image is not 320x240, return false return false; } - if (read16(f) != 1) { - return false; - } + if (read16(f) != 1) { return false; } bmpDepth = read16(f); Serial.print("bitdepth "); diff --git a/libraries/TFT_Touch_Shield_V2/examples/tftbmp2/tftbmp2.ino b/libraries/TFT_Touch_Shield_V2/examples/tftbmp2/tftbmp2.ino index bc8fb45c42..8e24600481 100644 --- a/libraries/TFT_Touch_Shield_V2/examples/tftbmp2/tftbmp2.ino +++ b/libraries/TFT_Touch_Shield_V2/examples/tftbmp2/tftbmp2.ino @@ -18,18 +18,19 @@ #include "TFTv2.h" -#define MAX_BMP 10 // bmp file num -#define FILENAME_LEN 20 // max file name length +#define MAX_BMP 10 // bmp file num +#define FILENAME_LEN 20 // max file name length -const int PIN_SD_CS = 4; // pin of sd card +const int PIN_SD_CS = 4; // pin of sd card -const long __Gnbmp_height = 320; // bmp hight -const long __Gnbmp_width = 240; // bmp width +const long __Gnbmp_height = 320; // bmp height +const long __Gnbmp_width = 240; // bmp width -long __Gnbmp_image_offset = 0;; +long __Gnbmp_image_offset = 0; +; -int __Gnfile_num = 0; // num of file -char __Gsbmp_files[MAX_BMP][FILENAME_LEN]; // file name +int __Gnfile_num = 0; // num of file +char __Gsbmp_files[MAX_BMP][FILENAME_LEN]; // file name File bmpFile; @@ -37,44 +38,30 @@ File bmpFile; bool checkBMP(char *_name, char r_name[]) { int len = 0; - if (NULL == _name) { - return false; - } + if (NULL == _name) { return false; } while (*_name) { r_name[len++] = *(_name++); - if (len > FILENAME_LEN) { - return false; - } + if (len > FILENAME_LEN) { return false; } } r_name[len] = '\0'; - if (len < 5) { - return false; - } + if (len < 5) { return false; } // if xxx.bmp or xxx.BMP - if (r_name[len - 4] == '.' \ - && (r_name[len - 3] == 'b' || (r_name[len - 3] == 'B')) \ - && (r_name[len - 2] == 'm' || (r_name[len - 2] == 'M')) \ - && (r_name[len - 1] == 'p' || (r_name[len - 1] == 'P'))) { - return true; - } + if (r_name[len - 4] == '.' && (r_name[len - 3] == 'b' || (r_name[len - 3] == 'B')) && (r_name[len - 2] == 'm' || (r_name[len - 2] == 'M')) && (r_name[len - 1] == 'p' || (r_name[len - 1] == 'P'))) { return true; } return false; - } // search root to find bmp file void searchDirectory() { - File root = SD.open("/"); // root + File root = SD.open("/"); // root while (true) { - File entry = root.openNextFile(); + File entry = root.openNextFile(); - if (! entry) { - break; - } + if (!entry) { break; } if (!entry.isDirectory()) { char *ptmp = entry.name(); @@ -94,9 +81,7 @@ void searchDirectory() { Serial.print(__Gnfile_num); Serial.println(" file: "); - for (int i = 0; i < __Gnfile_num; i++) { - Serial.println(__Gsbmp_files[i]); - } + for (int i = 0; i < __Gnfile_num; i++) { Serial.println(__Gsbmp_files[i]); } } @@ -114,7 +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!"); @@ -132,7 +117,7 @@ void loop() { bmpFile = SD.open(__Gsbmp_files[i]); if (! bmpFile) { - Serial.println("didnt find image"); + Serial.println("didn'`t find image"); while (1); } @@ -153,12 +138,12 @@ void loop() { bmpFile = SD.open("pfvm_1.bmp"); - if (! bmpFile) { - Serial.println("didnt find image"); + if (!bmpFile) { + Serial.println("didn't find image"); while (1); } - if (! bmpReadHeader(bmpFile)) { + if (!bmpReadHeader(bmpFile)) { Serial.println("bad bmp"); return; } @@ -172,16 +157,16 @@ void loop() { /*********************************************/ // This procedure reads a bitmap and draws it to the screen // its sped up by reading many pixels worth of data at a time -// instead of just one pixel at a time. increading the buffer takes +// instead of just one pixel at a time. increasing the buffer takes // more RAM but makes the drawing a little faster. 20 pixels' worth // is probably a good place -#define BUFFPIXEL 60 // must be a divisor of 240 -#define BUFFPIXEL_X3 180 // BUFFPIXELx3 +#define BUFFPIXEL 60 // must be a divisor of 240 +#define BUFFPIXEL_X3 180 // BUFFPIXELx3 -#define UP_DOWN 1 -#define DOWN_UP 0 +#define UP_DOWN 1 +#define DOWN_UP 0 // dir - 1: up to down // dir - 2: down to up @@ -194,12 +179,10 @@ void bmpdraw(File f, int x, int y, int dir) { uint32_t time = millis(); - uint8_t sdbuffer[BUFFPIXEL_X3]; // 3 * pixels to buffer + uint8_t sdbuffer[BUFFPIXEL_X3]; // 3 * pixels to buffer for (int i = 0; i < __Gnbmp_height; i++) { - if (dir) { - bmpFile.seek(__Gnbmp_image_offset + (__Gnbmp_height - 1 - i) * 240 * 3); - } + if (dir) { bmpFile.seek(__Gnbmp_image_offset + (__Gnbmp_height - 1 - i) * 240 * 3); } for (int j = 0; j < (240 / BUFFPIXEL); j++) { @@ -210,9 +193,9 @@ void bmpdraw(File f, int x, int y, int dir) { unsigned int __color[BUFFPIXEL]; for (int k = 0; k < BUFFPIXEL; k++) { - __color[k] = sdbuffer[buffidx + 2] >> 3; // read - __color[k] = __color[k] << 6 | (sdbuffer[buffidx + 1] >> 2); // green - __color[k] = __color[k] << 5 | (sdbuffer[buffidx + 0] >> 3); // blue + __color[k] = sdbuffer[buffidx + 2] >> 3; // read + __color[k] = __color[k] << 6 | (sdbuffer[buffidx + 1] >> 2); // green + __color[k] = __color[k] << 5 | (sdbuffer[buffidx + 0] >> 3); // blue buffidx += 3; } @@ -239,7 +222,6 @@ void bmpdraw(File f, int x, int y, int dir) { TFT_CS_HIGH; } - } Serial.print(millis() - time, DEC); @@ -276,13 +258,11 @@ boolean bmpReadHeader(File f) { int bmp_width = read32(f); int bmp_height = read32(f); - if (bmp_width != __Gnbmp_width || bmp_height != __Gnbmp_height) { // if image is not 320x240, return false + if (bmp_width != __Gnbmp_width || bmp_height != __Gnbmp_height) { // if image is not 320x240, return false return false; } - if (read16(f) != 1) { - return false; - } + if (read16(f) != 1) { return false; } bmpDepth = read16(f); Serial.print("bitdepth "); diff --git a/libraries/Ticker/examples/TickerBasic/TickerBasic.ino b/libraries/Ticker/examples/TickerBasic/TickerBasic.ino index 2fabd98315..4ccbe3a2e6 100644 --- a/libraries/Ticker/examples/TickerBasic/TickerBasic.ino +++ b/libraries/Ticker/examples/TickerBasic/TickerBasic.ino @@ -24,9 +24,7 @@ void flip() { ++count; // when the counter reaches a certain value, start blinking like crazy - if (count == 20) { - flipper.attach(0.1, flip); - } + if (count == 20) { flipper.attach(0.1, flip); } // when the counter reaches yet another value, stop blinking else if (count == 120) { flipper.detach(); @@ -41,5 +39,4 @@ void setup() { flipper.attach(0.3, flip); } -void loop() { -} +void loop() {} diff --git a/libraries/Ticker/examples/TickerFunctional/TickerFunctional.ino b/libraries/Ticker/examples/TickerFunctional/TickerFunctional.ino index 33c9435982..2d0673a17a 100644 --- a/libraries/Ticker/examples/TickerFunctional/TickerFunctional.ino +++ b/libraries/Ticker/examples/TickerFunctional/TickerFunctional.ino @@ -1,27 +1,30 @@ #include "Arduino.h" #include "Ticker.h" -#define LED1 2 -#define LED2 4 -#define LED3 12 -#define LED4 14 -#define LED5 15 +#define LED1 2 +#define LED2 4 +#define LED3 12 +#define LED4 14 +#define LED5 15 class ExampleClass { - public: - ExampleClass(int pin, int duration) : _pin(pin), _duration(duration) { - pinMode(_pin, OUTPUT); - _myTicker.attach_ms(_duration, std::bind(&ExampleClass::classBlink, this)); - } - ~ExampleClass() {}; - - int _pin, _duration; - Ticker _myTicker; - - void classBlink() { - digitalWrite(_pin, !digitalRead(_pin)); - } +public: + ExampleClass(int pin, int duration) + : _pin(pin), _duration(duration) { + pinMode(_pin, OUTPUT); + _myTicker.attach_ms(_duration, + [this]() { + classBlink(); + }); + } + + int _pin, _duration; + Ticker _myTicker; + + void classBlink() { + digitalWrite(_pin, !digitalRead(_pin)); + } }; void staticBlink() { @@ -52,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, []() { @@ -60,5 +63,4 @@ void setup() { }); } -void loop() { -} +void loop() {} diff --git a/libraries/Ticker/examples/TickerParameter/TickerParameter.ino b/libraries/Ticker/examples/TickerParameter/TickerParameter.ino index 066905d098..0c49eaf4ff 100644 --- a/libraries/Ticker/examples/TickerParameter/TickerParameter.ino +++ b/libraries/Ticker/examples/TickerParameter/TickerParameter.ino @@ -1,5 +1,5 @@ /* - Passing paramters to Ticker callbacks + Passing parameters to Ticker callbacks Apart from void(void) functions, the Ticker library supports functions taking one argument. This argument's size has to be less or @@ -46,5 +46,4 @@ void setup() { tickerSetChar.attach_ms(26, setPinChar, (char)1); } -void loop() { -} +void loop() {} 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/Wire/Wire.cpp b/libraries/Wire/Wire.cpp index 3aa3604ab0..b2d9a6affc 100644 --- a/libraries/Wire/Wire.cpp +++ b/libraries/Wire/Wire.cpp @@ -22,7 +22,8 @@ Modified January 2017 by Bjorn Hammarberg (bjoham@esp8266.com) - i2c slave support */ -extern "C" { +extern "C" +{ #include #include #include @@ -31,23 +32,22 @@ extern "C" { #include "twi.h" #include "Wire.h" - -//Some boards don't have these pins available, and hence don't support Wire. -//Check here for compile-time error. +// Some boards don't have these pins available, and hence don't support Wire. +// Check here for compile-time error. #if !defined(PIN_WIRE_SDA) || !defined(PIN_WIRE_SCL) #error Wire library is not supported on this board #endif // Initialize Class Variables ////////////////////////////////////////////////// -uint8_t TwoWire::rxBuffer[BUFFER_LENGTH]; -uint8_t TwoWire::rxBufferIndex = 0; -uint8_t TwoWire::rxBufferLength = 0; +uint8_t TwoWire::rxBuffer[I2C_BUFFER_LENGTH]; +size_t TwoWire::rxBufferIndex = 0; +size_t TwoWire::rxBufferLength = 0; uint8_t TwoWire::txAddress = 0; -uint8_t TwoWire::txBuffer[BUFFER_LENGTH]; -uint8_t TwoWire::txBufferIndex = 0; -uint8_t TwoWire::txBufferLength = 0; +uint8_t TwoWire::txBuffer[I2C_BUFFER_LENGTH]; +size_t TwoWire::txBufferIndex = 0; +size_t TwoWire::txBufferLength = 0; uint8_t TwoWire::transmitting = 0; void (*TwoWire::user_onRequest)(void); @@ -58,7 +58,7 @@ static int default_scl_pin = SCL; // Constructors //////////////////////////////////////////////////////////////// -TwoWire::TwoWire() {} +TwoWire::TwoWire() { } // Public Methods ////////////////////////////////////////////////////////////// @@ -122,12 +122,12 @@ void TwoWire::setClockStretchLimit(uint32_t limit) size_t TwoWire::requestFrom(uint8_t address, size_t size, bool sendStop) { - if (size > BUFFER_LENGTH) + if (size > I2C_BUFFER_LENGTH) { - size = BUFFER_LENGTH; + size = I2C_BUFFER_LENGTH; } - size_t read = (twi_readFrom(address, rxBuffer, size, sendStop) == 0) ? size : 0; - rxBufferIndex = 0; + size_t read = (twi_readFrom(address, rxBuffer, size, sendStop) == 0) ? size : 0; + rxBufferIndex = 0; rxBufferLength = read; return read; } @@ -149,14 +149,15 @@ uint8_t TwoWire::requestFrom(int address, int quantity) uint8_t TwoWire::requestFrom(int address, int quantity, int sendStop) { - return requestFrom(static_cast(address), static_cast(quantity), static_cast(sendStop)); + return requestFrom(static_cast(address), static_cast(quantity), + static_cast(sendStop)); } void TwoWire::beginTransmission(uint8_t address) { - transmitting = 1; - txAddress = address; - txBufferIndex = 0; + transmitting = 1; + txAddress = address; + txBufferIndex = 0; txBufferLength = 0; } @@ -167,10 +168,10 @@ void TwoWire::beginTransmission(int address) uint8_t TwoWire::endTransmission(uint8_t sendStop) { - int8_t ret = twi_writeTo(txAddress, txBuffer, txBufferLength, sendStop); - txBufferIndex = 0; + int8_t ret = twi_writeTo(txAddress, txBuffer, txBufferLength, sendStop); + txBufferIndex = 0; txBufferLength = 0; - transmitting = 0; + transmitting = 0; return ret; } @@ -183,7 +184,7 @@ size_t TwoWire::write(uint8_t data) { if (transmitting) { - if (txBufferLength >= BUFFER_LENGTH) + if (txBufferLength >= I2C_BUFFER_LENGTH) { setWriteError(); return 0; @@ -199,7 +200,7 @@ size_t TwoWire::write(uint8_t data) return 1; } -size_t TwoWire::write(const uint8_t *data, size_t quantity) +size_t TwoWire::write(const uint8_t* data, size_t quantity) { if (transmitting) { @@ -255,9 +256,9 @@ int TwoWire::peek(void) void TwoWire::flush(void) { - rxBufferIndex = 0; + rxBufferIndex = 0; rxBufferLength = 0; - txBufferIndex = 0; + txBufferIndex = 0; txBufferLength = 0; } @@ -283,7 +284,7 @@ void TwoWire::onReceiveService(uint8_t* inBytes, size_t numBytes) } // set rx iterator vars - rxBufferIndex = 0; + rxBufferIndex = 0; rxBufferLength = numBytes; // alert user program @@ -300,7 +301,7 @@ void TwoWire::onRequestService(void) // reset tx buffer iterator vars // !!! this will kill any pending pre-master sendTo() activity - txBufferIndex = 0; + txBufferIndex = 0; txBufferLength = 0; // alert user program @@ -312,7 +313,7 @@ void TwoWire::onReceive(void (*function)(int)) // arduino api compatibility fixer: // really hope size parameter will not exceed 2^31 :) static_assert(sizeof(int) == sizeof(size_t), "something is wrong in Arduino kingdom"); - user_onReceive = reinterpret_cast(function); + user_onReceive = reinterpret_cast(function); } void TwoWire::onReceive(void (*function)(size_t)) diff --git a/libraries/Wire/Wire.h b/libraries/Wire/Wire.h index 5d6b36457e..0ff796c7ef 100644 --- a/libraries/Wire/Wire.h +++ b/libraries/Wire/Wire.h @@ -27,42 +27,45 @@ #include #include "Stream.h" - - +#ifndef I2C_BUFFER_LENGTH +// DEPRECATED: Do not use BUFFER_LENGTH, prefer I2C_BUFFER_LENGTH #define BUFFER_LENGTH 128 +#define I2C_BUFFER_LENGTH BUFFER_LENGTH +#endif -class TwoWire : public Stream +class TwoWire: public Stream { private: static uint8_t rxBuffer[]; - static uint8_t rxBufferIndex; - static uint8_t rxBufferLength; + static size_t rxBufferIndex; + static size_t rxBufferLength; static uint8_t txAddress; static uint8_t txBuffer[]; - static uint8_t txBufferIndex; - static uint8_t txBufferLength; + static size_t txBufferIndex; + static size_t txBufferLength; static uint8_t transmitting; static void (*user_onRequest)(void); static void (*user_onReceive)(size_t); static void onRequestService(void); static void onReceiveService(uint8_t*, size_t); + public: TwoWire(); - void begin(int sda, int scl); - void begin(int sda, int scl, uint8_t address); - void pins(int sda, int scl) __attribute__((deprecated)); // use begin(sda, scl) in new code - void begin(); - void begin(uint8_t); - void begin(int); - void setClock(uint32_t); - void setClockStretchLimit(uint32_t); - void beginTransmission(uint8_t); - void beginTransmission(int); + void begin(int sda, int scl); + void begin(int sda, int scl, uint8_t address); + void pins(int sda, int scl) __attribute__((deprecated)); // use begin(sda, scl) in new code + void begin(); + void begin(uint8_t); + void begin(int); + void setClock(uint32_t); + void setClockStretchLimit(uint32_t); + void beginTransmission(uint8_t); + void beginTransmission(int); uint8_t endTransmission(void); uint8_t endTransmission(uint8_t); - size_t requestFrom(uint8_t address, size_t size, bool sendStop); + size_t requestFrom(uint8_t address, size_t size, bool sendStop); uint8_t status(); uint8_t requestFrom(uint8_t, uint8_t); @@ -71,14 +74,14 @@ class TwoWire : public Stream uint8_t requestFrom(int, int, int); virtual size_t write(uint8_t); - virtual size_t write(const uint8_t *, size_t); - virtual int available(void); - virtual int read(void); - virtual int peek(void); - virtual void flush(void); - void onReceive(void (*)(int)); // arduino api - void onReceive(void (*)(size_t)); // legacy esp8266 backward compatibility - void onRequest(void (*)(void)); + virtual size_t write(const uint8_t*, size_t); + virtual int available(void); + virtual int read(void); + virtual int peek(void); + virtual void flush(void); + void onReceive(void (*)(int)); // arduino api + void onReceive(void (*)(size_t)); // legacy esp8266 backward compatibility + void onRequest(void (*)(void)); using Print::write; }; @@ -88,4 +91,3 @@ extern TwoWire Wire; #endif #endif - diff --git a/libraries/Wire/examples/master_reader/master_reader.ino b/libraries/Wire/examples/master_reader/master_reader.ino index 323830f847..a6de2b2d8e 100644 --- a/libraries/Wire/examples/master_reader/master_reader.ino +++ b/libraries/Wire/examples/master_reader/master_reader.ino @@ -18,8 +18,8 @@ const int16_t I2C_MASTER = 0x42; const int16_t I2C_SLAVE = 0x08; void setup() { - Serial.begin(115200); // start serial for output - Wire.begin(SDA_PIN, SCL_PIN, I2C_MASTER); // join i2c bus (address optional for master) + Serial.begin(115200); // start serial for output + Wire.begin(SDA_PIN, SCL_PIN, I2C_MASTER); // join i2c bus (address optional for master) } void loop() { @@ -27,11 +27,11 @@ void loop() { static periodic nextPing(1000); if (nextPing) { - Wire.requestFrom(I2C_SLAVE, 6); // request 6 bytes from slave device #8 + Wire.requestFrom(I2C_SLAVE, 6); // request 6 bytes from slave device #8 - while (Wire.available()) { // slave may send less than requested - char c = Wire.read(); // receive a byte as character - Serial.print(c); // print the character + while (Wire.available()) { // slave may send less than requested + char c = Wire.read(); // receive a byte as character + Serial.print(c); // print the character } } } diff --git a/libraries/Wire/examples/master_writer/master_writer.ino b/libraries/Wire/examples/master_writer/master_writer.ino index 1e9719e23c..4ca0d64db2 100644 --- a/libraries/Wire/examples/master_writer/master_writer.ino +++ b/libraries/Wire/examples/master_writer/master_writer.ino @@ -18,7 +18,7 @@ const int16_t I2C_MASTER = 0x42; const int16_t I2C_SLAVE = 0x08; void setup() { - Wire.begin(SDA_PIN, SCL_PIN, I2C_MASTER); // join i2c bus (address optional for master) + Wire.begin(SDA_PIN, SCL_PIN, I2C_MASTER); // join i2c bus (address optional for master) } byte x = 0; @@ -28,10 +28,10 @@ void loop() { static periodic nextPing(1000); if (nextPing) { - Wire.beginTransmission(I2C_SLAVE); // transmit to device #8 - Wire.write("x is "); // sends five bytes - Wire.write(x); // sends one byte - Wire.endTransmission(); // stop transmitting + Wire.beginTransmission(I2C_SLAVE); // transmit to device #8 + Wire.write("x is "); // sends five bytes + Wire.write(x); // sends one byte + Wire.endTransmission(); // stop transmitting x++; } diff --git a/libraries/Wire/examples/slave_receiver/slave_receiver.ino b/libraries/Wire/examples/slave_receiver/slave_receiver.ino index 270cc43129..60115e21b4 100644 --- a/libraries/Wire/examples/slave_receiver/slave_receiver.ino +++ b/libraries/Wire/examples/slave_receiver/slave_receiver.ino @@ -18,24 +18,23 @@ const int16_t I2C_MASTER = 0x42; const int16_t I2C_SLAVE = 0x08; void setup() { - Serial.begin(115200); // start serial for output + Serial.begin(115200); // start serial for output - Wire.begin(SDA_PIN, SCL_PIN, I2C_SLAVE); // new syntax: join i2c bus (address required for slave) - Wire.onReceive(receiveEvent); // register event + Wire.begin(SDA_PIN, SCL_PIN, I2C_SLAVE); // new syntax: join i2c bus (address required for slave) + Wire.onReceive(receiveEvent); // register event } -void loop() { -} +void loop() {} // function that executes whenever data is received from master // this function is registered as an event, see setup() void receiveEvent(size_t howMany) { - (void) howMany; - while (1 < Wire.available()) { // loop through all but the last - char c = Wire.read(); // receive byte as a character - Serial.print(c); // print the character + (void)howMany; + while (1 < Wire.available()) { // loop through all but the last + char c = Wire.read(); // receive byte as a character + Serial.print(c); // print the character } - int x = Wire.read(); // receive byte as an integer - Serial.println(x); // print the integer + int x = Wire.read(); // receive byte as an integer + Serial.println(x); // print the integer } diff --git a/libraries/Wire/examples/slave_sender/slave_sender.ino b/libraries/Wire/examples/slave_sender/slave_sender.ino index e177be8538..00137e38ac 100644 --- a/libraries/Wire/examples/slave_sender/slave_sender.ino +++ b/libraries/Wire/examples/slave_sender/slave_sender.ino @@ -17,16 +17,15 @@ const int16_t I2C_MASTER = 0x42; const int16_t I2C_SLAVE = 0x08; void setup() { - Wire.begin(SDA_PIN, SCL_PIN, I2C_SLAVE); // join i2c bus with address #8 - Wire.onRequest(requestEvent); // register event + Wire.begin(SDA_PIN, SCL_PIN, I2C_SLAVE); // join i2c bus with address #8 + Wire.onRequest(requestEvent); // register event } -void loop() { -} +void loop() {} // function that executes whenever data is requested by master // this function is registered as an event, see setup() void requestEvent() { - Wire.write("hello\n"); // respond with message of 6 bytes + Wire.write("hello\n"); // respond with message of 6 bytes // as expected by master } diff --git a/libraries/esp8266/examples/Blink/Blink.ino b/libraries/esp8266/examples/Blink/Blink.ino index de23fb519f..d2083049dd 100644 --- a/libraries/esp8266/examples/Blink/Blink.ino +++ b/libraries/esp8266/examples/Blink/Blink.ino @@ -10,12 +10,12 @@ */ void setup() { - pinMode(LED_BUILTIN, OUTPUT); // Initialize the LED_BUILTIN pin as an output + pinMode(LED_BUILTIN, OUTPUT); // Initialize the LED_BUILTIN pin as an output } // the loop function runs over and over again forever void loop() { - digitalWrite(LED_BUILTIN, LOW); // Turn the LED on (Note that LOW is the voltage level + digitalWrite(LED_BUILTIN, LOW); // Turn the LED on (Note that LOW is the voltage level // but actually the LED is on; this is because // it is active low on the ESP-01) delay(1000); // Wait for a second diff --git a/libraries/esp8266/examples/BlinkPolledTimeout/BlinkPolledTimeout.ino b/libraries/esp8266/examples/BlinkPolledTimeout/BlinkPolledTimeout.ino index 11e2aff482..765fd88932 100644 --- a/libraries/esp8266/examples/BlinkPolledTimeout/BlinkPolledTimeout.ino +++ b/libraries/esp8266/examples/BlinkPolledTimeout/BlinkPolledTimeout.ino @@ -26,7 +26,7 @@ #include void ledOn() { - digitalWrite(LED_BUILTIN, LOW); // Turn the LED on (Note that LOW is the voltage level + digitalWrite(LED_BUILTIN, LOW); // Turn the LED on (Note that LOW is the voltage level } void ledOff() { @@ -38,7 +38,7 @@ void ledToggle() { } -esp8266::polledTimeout::periodicFastUs halfPeriod(500000); //use fully qualified type and avoid importing all ::esp8266 namespace to the global namespace +esp8266::polledTimeout::periodicFastUs halfPeriod(500000); // use fully qualified type and avoid importing all ::esp8266 namespace to the global namespace // the setup function runs only once at start void setup() { @@ -50,43 +50,37 @@ void setup() { Serial.printf("periodic/oneShotFastUs::timeMax() = %u us\n", (uint32_t)esp8266::polledTimeout::periodicFastUs::timeMax()); Serial.printf("periodic/oneShotFastNs::timeMax() = %u ns\n", (uint32_t)esp8266::polledTimeout::periodicFastNs::timeMax()); -#if 0 // 1 for debugging polledTimeout +#if 0 // 1 for debugging polledTimeout Serial.printf("periodic/oneShotMs::rangeCompensate = %u\n", (uint32_t)esp8266::polledTimeout::periodicMs::rangeCompensate); Serial.printf("periodic/oneShotFastMs::rangeCompensate = %u\n", (uint32_t)esp8266::polledTimeout::periodicFastMs::rangeCompensate); Serial.printf("periodic/oneShotFastUs::rangeCompensate = %u\n", (uint32_t)esp8266::polledTimeout::periodicFastUs::rangeCompensate); Serial.printf("periodic/oneShotFastNs::rangeCompensate = %u\n", (uint32_t)esp8266::polledTimeout::periodicFastNs::rangeCompensate); #endif - pinMode(LED_BUILTIN, OUTPUT); // Initialize the LED_BUILTIN pin as an output + pinMode(LED_BUILTIN, OUTPUT); // Initialize the LED_BUILTIN pin as an output - using esp8266::polledTimeout::oneShotMs; //import the type to the local namespace + using esp8266::polledTimeout::oneShotMs; // import the type to the local namespace - //STEP1; turn the led ON + // STEP1; turn the led ON ledOn(); - //STEP2: wait for ON timeout + // STEP2: wait for ON timeout oneShotMs timeoutOn(2000); - while (!timeoutOn) { - yield(); - } + while (!timeoutOn) { yield(); } - //STEP3: turn the led OFF + // STEP3: turn the led OFF ledOff(); - //STEP4: wait for OFF timeout to assure the led is kept off for this time before exiting setup + // STEP4: wait for OFF timeout to assure the led is kept off for this time before exiting setup oneShotMs timeoutOff(2000); - while (!timeoutOff) { - yield(); - } + while (!timeoutOff) { yield(); } - //Done with STEPs, do other stuff - halfPeriod.reset(); //halfPeriod is global, so it gets inited on sketch start. Clear it here to make it ready for loop, where it's actually used. + // Done with STEPs, do other stuff + halfPeriod.reset(); // halfPeriod is global, so it gets inited on sketch start. Clear it here to make it ready for loop, where it's actually used. } // the loop function runs over and over again forever void loop() { - if (halfPeriod) { - ledToggle(); - } + if (halfPeriod) { ledToggle(); } } diff --git a/libraries/esp8266/examples/CallBackList/CallBackGeneric.ino b/libraries/esp8266/examples/CallBackList/CallBackGeneric.ino index ce4dac2470..e77b35ef66 100644 --- a/libraries/esp8266/examples/CallBackList/CallBackGeneric.ino +++ b/libraries/esp8266/examples/CallBackList/CallBackGeneric.ino @@ -5,25 +5,25 @@ using namespace experimental::CBListImplentation; class exampleClass { - public: - exampleClass() {}; +public: + exampleClass(){}; - using exCallBack = std::function; - using exHandler = CallBackList::CallBackHandler; + using exCallBack = std::function; + using exHandler = CallBackList::CallBackHandler; - CallBackList myHandlers; + CallBackList myHandlers; - exHandler setHandler(exCallBack cb) { - return myHandlers.add(cb); - } + exHandler setHandler(exCallBack cb) { + return myHandlers.add(cb); + } - void removeHandler(exHandler hnd) { - myHandlers.remove(hnd); - } + void removeHandler(exHandler hnd) { + myHandlers.remove(hnd); + } - void trigger(int t) { - myHandlers.execute(t); - } + void trigger(int t) { + myHandlers.execute(t); + } }; exampleClass myExample; @@ -60,5 +60,4 @@ void setup() { }); } -void loop() { -} +void loop() {} diff --git a/libraries/esp8266/examples/CheckFlashCRC/CheckFlashCRC.ino b/libraries/esp8266/examples/CheckFlashCRC/CheckFlashCRC.ino index b00194a46b..f4bdfb022d 100644 --- a/libraries/esp8266/examples/CheckFlashCRC/CheckFlashCRC.ino +++ b/libraries/esp8266/examples/CheckFlashCRC/CheckFlashCRC.ino @@ -8,7 +8,7 @@ extern "C" { #include "spi_flash.h" } -// Artificially create a space in PROGMEM that fills multipe sectors so +// Artificially create a space in PROGMEM that fills multiple sectors so // we can corrupt one without crashing the system const int corruptme[SPI_FLASH_SEC_SIZE * 4] PROGMEM = { 0 }; @@ -21,25 +21,24 @@ void setup() { uint32_t ptr = (uint32_t)corruptme; // Find a page aligned spot inside the array ptr += 2 * SPI_FLASH_SEC_SIZE; - ptr &= ~(SPI_FLASH_SEC_SIZE - 1); // Sectoralign + ptr &= ~(SPI_FLASH_SEC_SIZE - 1); // Sectoralign uint32_t sector = ((((uint32_t)ptr - 0x40200000) / SPI_FLASH_SEC_SIZE)); // Create a sector with 1 bit set (i.e. fake corruption) - uint32_t *space = (uint32_t*)calloc(SPI_FLASH_SEC_SIZE, 1); + uint32_t *space = (uint32_t *)calloc(SPI_FLASH_SEC_SIZE, 1); space[42] = 64; // Write it into flash at the spot in question spi_flash_erase_sector(sector); - spi_flash_write(sector * SPI_FLASH_SEC_SIZE, (uint32_t*)space, SPI_FLASH_SEC_SIZE); + spi_flash_write(sector * SPI_FLASH_SEC_SIZE, (uint32_t *)space, SPI_FLASH_SEC_SIZE); Serial.printf("CRC check: %s\n", ESP.checkFlashCRC() ? "OK" : "ERROR"); Serial.printf("...Correcting the flash...\n"); memset(space, 0, SPI_FLASH_SEC_SIZE); spi_flash_erase_sector(sector); - spi_flash_write(sector * SPI_FLASH_SEC_SIZE, (uint32_t*)space, SPI_FLASH_SEC_SIZE); + spi_flash_write(sector * SPI_FLASH_SEC_SIZE, (uint32_t *)space, SPI_FLASH_SEC_SIZE); Serial.printf("CRC check: %s\n", ESP.checkFlashCRC() ? "OK" : "ERROR"); } -void loop() { -} +void loop() {} diff --git a/libraries/esp8266/examples/CheckFlashConfig/CheckFlashConfig.ino b/libraries/esp8266/examples/CheckFlashConfig/CheckFlashConfig.ino index 9c3a54d7da..5d958b8f5b 100644 --- a/libraries/esp8266/examples/CheckFlashConfig/CheckFlashConfig.ino +++ b/libraries/esp8266/examples/CheckFlashConfig/CheckFlashConfig.ino @@ -20,7 +20,10 @@ void loop() { Serial.printf("Flash ide size: %u bytes\n", ideSize); Serial.printf("Flash ide speed: %u Hz\n", ESP.getFlashChipSpeed()); - Serial.printf("Flash ide mode: %s\n", (ideMode == FM_QIO ? "QIO" : ideMode == FM_QOUT ? "QOUT" : ideMode == FM_DIO ? "DIO" : ideMode == FM_DOUT ? "DOUT" : "UNKNOWN")); + Serial.printf("Flash ide mode: %s\n", (ideMode == FM_QIO ? "QIO" : ideMode == FM_QOUT ? "QOUT" + : ideMode == FM_DIO ? "DIO" + : ideMode == FM_DOUT ? "DOUT" + : "UNKNOWN")); if (ideSize != realSize) { Serial.println("Flash Chip configuration wrong!\n"); diff --git a/libraries/esp8266/examples/ConfigFile/ConfigFile.ino b/libraries/esp8266/examples/ConfigFile/ConfigFile.ino index cb1d25be07..d0406a3882 100644 --- a/libraries/esp8266/examples/ConfigFile/ConfigFile.ino +++ b/libraries/esp8266/examples/ConfigFile/ConfigFile.ino @@ -11,6 +11,9 @@ #include "FS.h" #include +// more and possibly updated information can be found at: +// https://arduinojson.org/v6/example/config/ + bool loadConfig() { File configFile = LittleFS.open("/config.json", "r"); if (!configFile) { @@ -18,22 +21,8 @@ bool loadConfig() { return false; } - size_t size = configFile.size(); - if (size > 1024) { - Serial.println("Config file size is too large"); - return false; - } - - // Allocate a buffer to store contents of the file. - std::unique_ptr buf(new char[size]); - - // We don't use String here because ArduinoJson library requires the input - // buffer to be mutable. If you don't use ArduinoJson, you may as well - // use configFile.readString instead. - configFile.readBytes(buf.get(), size); - StaticJsonDocument<200> doc; - auto error = deserializeJson(doc, buf.get()); + auto error = deserializeJson(doc, configFile); if (error) { Serial.println("Failed to parse config file"); return false; @@ -92,5 +81,4 @@ void setup() { } } -void loop() { -} +void loop() {} diff --git a/libraries/esp8266/examples/FadePolledTimeout/FadePolledTimeout.ino b/libraries/esp8266/examples/FadePolledTimeout/FadePolledTimeout.ino new file mode 100644 index 0000000000..5ad731db3c --- /dev/null +++ b/libraries/esp8266/examples/FadePolledTimeout/FadePolledTimeout.ino @@ -0,0 +1,70 @@ +/* + ESP8266 LED fade with polledTimeout and locked phase PWM + + Modified from an BlinkPolledTimeout.ino, + Copyright (c) 2018 Daniel Salazar. 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 + + + Note that this sketch uses LED_BUILTIN to find the pin with the internal LED +*/ + +#include + +esp8266::polledTimeout::periodicFastUs stepPeriod(50000); + +// the setup function runs only once at start +void setup() { + Serial.begin(115200); + Serial.println(); + + // This next line will cause the code to use the Phase-Locked waveform generator + // instead of the default PWM-Locked one. Comment it out to try the default version. + // For more information on choosing between the two options, see the following pull requests: + // Phase-Locked generator: https://github.com/esp8266/Arduino/pull/7022 + // PWM-Locked generator: https://github.com/esp8266/Arduino/pull/7231 + enablePhaseLockedWaveform(); + + pinMode(LED_BUILTIN, OUTPUT); // Initialize the LED_BUILTIN pin as an output + analogWriteRange(1000); + + using esp8266::polledTimeout::oneShotMs; // import the type to the local namespace + + digitalWrite(LED_BUILTIN, LOW); // Turn the LED on (Note that LOW is the voltage level + + oneShotMs timeoutOn(2000); + while (!timeoutOn) { yield(); } + + stepPeriod.reset(); +} + + +void loop() { + static int val = 0; + static int delta = 100; + if (stepPeriod) { + val += delta; + if (val < 0) { + val = 100; + delta = 100; + } else if (val > 1000) { + val = 900; + delta = -100; + } + analogWrite(LED_BUILTIN, val); + } +} 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/HeapMetric/HeapMetric.ino b/libraries/esp8266/examples/HeapMetric/HeapMetric.ino index 3119042cac..4df382134a 100644 --- a/libraries/esp8266/examples/HeapMetric/HeapMetric.ino +++ b/libraries/esp8266/examples/HeapMetric/HeapMetric.ino @@ -10,11 +10,11 @@ void stats(const char* what) { // we could use getFreeHeap() getMaxFreeBlockSize() and getHeapFragmentation() // or all at once: uint32_t free; - uint16_t max; + uint32_t max; uint8_t frag; ESP.getHeapStats(&free, &max, &frag); - Serial.printf("free: %5d - max: %5d - frag: %3d%% <- ", free, max, frag); + Serial.printf("free: %7u - max: %7u - frag: %3d%% <- ", free, max, frag); // %s requires a malloc that could fail, using println instead: Serial.println(what); } @@ -52,53 +52,40 @@ void tryit(int blocksize) { calculation of multiple elements combined with the rounding up for the 8-byte alignment of each allocation can make for some tricky calculations. */ - int rawMemoryMaxFreeBlockSize = ESP.getMaxFreeBlockSize(); + int rawMemoryMaxFreeBlockSize = ESP.getMaxFreeBlockSize(); // Remove the space for overhead component of the blocks*sizeof(void*) array. int maxFreeBlockSize = rawMemoryMaxFreeBlockSize - UMM_OVERHEAD_ADJUST; // Initial estimate to use all of the MaxFreeBlock with multiples of 8 rounding up. - blocks = maxFreeBlockSize / - (((blocksize + UMM_OVERHEAD_ADJUST + 7) & ~7) + sizeof(void*)); + blocks = maxFreeBlockSize / (((blocksize + UMM_OVERHEAD_ADJUST + 7) & ~7) + sizeof(void*)); /* While we allowed for the 8-byte alignment overhead for blocks*blocksize we were unable to compensate in advance for the later 8-byte aligning needed for the blocks*sizeof(void*) allocation. Thus blocks may be off by one count. We now validate the estimate and adjust as needed. */ - int rawMemoryEstimate = - blocks * ((blocksize + UMM_OVERHEAD_ADJUST + 7) & ~7) + - ((blocks * sizeof(void*) + UMM_OVERHEAD_ADJUST + 7) & ~7); - if (rawMemoryMaxFreeBlockSize < rawMemoryEstimate) { - --blocks; - } + int rawMemoryEstimate = blocks * ((blocksize + UMM_OVERHEAD_ADJUST + 7) & ~7) + ((blocks * sizeof(void*) + UMM_OVERHEAD_ADJUST + 7) & ~7); + if (rawMemoryMaxFreeBlockSize < rawMemoryEstimate) { --blocks; } Serial.printf("\nFilling memory with blocks of %d bytes each\n", blocksize); stats("before"); p = (void**)malloc(sizeof(void*) * blocks); - for (int i = 0; i < blocks; i++) { - p[i] = malloc(blocksize); - } + for (int i = 0; i < blocks; i++) { p[i] = malloc(blocksize); } stats("array and blocks allocation"); for (int i = 0; i < blocks; i += 2) { - if (p[i]) { - free(p[i]); - } + if (p[i]) { free(p[i]); } p[i] = nullptr; } stats("freeing every other blocks"); for (int i = 0; i < (blocks - 1); i += 4) { - if (p[i + 1]) { - free(p[i + 1]); - } + if (p[i + 1]) { free(p[i + 1]); } p[i + 1] = nullptr; } stats("freeing every other remaining blocks"); for (int i = 0; i < blocks; i++) { - if (p[i]) { - free(p[i]); - } + if (p[i]) { free(p[i]); } } stats("freeing array"); @@ -109,6 +96,7 @@ void tryit(int blocksize) { void setup() { Serial.begin(115200); WiFi.mode(WIFI_OFF); + delay(50); Serial.printf("\r\nDemo Heap Metrics for DRAM\r\n"); tryit(8000); @@ -135,7 +123,26 @@ void setup() { tryit(15); } #endif +#ifdef MMU_EXTERNAL_HEAP + { + HeapSelect ephemeral = HeapSelect(UMM_HEAP_EXTERNAL); + Serial.printf("\r\nDemo Heap Metrics for External RAM\r\n"); +#if (MMU_EXTERNAL_HEAP > 64) + tryit(64000); + tryit(32000); +#endif + tryit(16000); + tryit(8000); + tryit(4000); + tryit(2000); + tryit(1000); + tryit(500); + tryit(200); + tryit(100); + tryit(50); + tryit(15); + } +#endif } -void loop() { -} +void loop() {} diff --git a/libraries/esp8266/examples/HelloCrypto/HelloCrypto.ino b/libraries/esp8266/examples/HelloCrypto/HelloCrypto.ino index 1fb90134c2..b6af648738 100644 --- a/libraries/esp8266/examples/HelloCrypto/HelloCrypto.ino +++ b/libraries/esp8266/examples/HelloCrypto/HelloCrypto.ino @@ -21,7 +21,7 @@ namespace TypeCast = experimental::TypeConversion; https://github.com/esp8266/Arduino/issues/1143 https://arduino-esp8266.readthedocs.io/en/latest/PROGMEM.html */ -constexpr char masterKey[] PROGMEM = "w86vn@rpfA O+S"; // Use 8 random characters or more +constexpr char masterKey[] PROGMEM = "w86vn@rpfA O+S"; // Use 8 random characters or more void setup() { // Prevents the flash memory from being worn out, see: https://github.com/esp8266/Arduino/issues/1054 . @@ -42,18 +42,18 @@ void loop() { String exampleData = F("Hello Crypto World!"); Serial.println(String(F("This is our example data: ")) + exampleData); - uint8_t resultArray[SHA256::NATURAL_LENGTH] { 0 }; - uint8_t derivedKey[ENCRYPTION_KEY_LENGTH] { 0 }; + uint8_t resultArray[SHA256::NATURAL_LENGTH]{ 0 }; + uint8_t derivedKey[ENCRYPTION_KEY_LENGTH]{ 0 }; static uint32_t encryptionCounter = 0; // Generate the salt to use for HKDF - uint8_t hkdfSalt[16] { 0 }; + uint8_t hkdfSalt[16]{ 0 }; getNonceGenerator()(hkdfSalt, sizeof hkdfSalt); // Generate the key to use for HMAC and encryption - HKDF hkdfInstance(FPSTR(masterKey), (sizeof masterKey) - 1, hkdfSalt, sizeof hkdfSalt); // (sizeof masterKey) - 1 removes the terminating null value of the c-string + HKDF hkdfInstance(FPSTR(masterKey), (sizeof masterKey) - 1, hkdfSalt, sizeof hkdfSalt); // (sizeof masterKey) - 1 removes the terminating null value of the c-string hkdfInstance.produce(derivedKey, sizeof derivedKey); // Hash @@ -71,8 +71,8 @@ void loop() { // Authenticated Encryption with Associated Data (AEAD) String dataToEncrypt = F("This data is not encrypted."); - uint8_t resultingNonce[12] { 0 }; // The nonce is always 12 bytes - uint8_t resultingTag[16] { 0 }; // The tag is always 16 bytes + uint8_t resultingNonce[12]{ 0 }; // The nonce is always 12 bytes + uint8_t resultingTag[16]{ 0 }; // The tag is always 16 bytes Serial.println(String(F("\nThis is the data to encrypt: ")) + dataToEncrypt); diff --git a/libraries/esp8266/examples/HwdtStackDump/HwdtStackDump.ino b/libraries/esp8266/examples/HwdtStackDump/HwdtStackDump.ino new file mode 100644 index 0000000000..f9c692ccf0 --- /dev/null +++ b/libraries/esp8266/examples/HwdtStackDump/HwdtStackDump.ino @@ -0,0 +1,103 @@ +/* + + There is a tool to print a stack dump at boot after a Hardware WDT + reset. To use the Hardware WDT Reset stack dump tool, you can select HWDT or + HWDT_NOEXTRA4K from the Arduino IDE menu "Tools->Debug Level" before + building your sketch. Note, 'Tools->Debug port' selection is not needed or + referenced for printing the HWDT stack dump. + + When the ESP8266 restarts because of a Hardware WDT reset, the serial port + speed defaults to 115200 bps. The HWDT stack dump will always print on port + 'Serial'. + + To demonstrate this tool, this Sketch offers a few options for crashing the + ESP8266 with and without a HWDT reset. + +*/ + +#include +#include +#include +#include +#include // g_pcont - only needed for this debug demo +#include + +#ifndef STASSID +#define STASSID "your-ssid" +#define STAPSK "your-password" +#endif + +const char* ssid = STASSID; +const char* password = STAPSK; + +//////////////////////////////////////////////////////////////////// +// This block is just for putting something on the BearSSL stack +// to show that it has not been zeroed out before HWDT stack dump +// gets to runs. +extern "C" { +#if CORE_MOCK +#define thunk_ets_uart_printf ets_uart_printf + +#else + int thunk_ets_uart_printf(const char* format, ...) __attribute__((format(printf, 1, 2))); + // Second stack thunked helper - this macro creates the global function thunk_ets_uart_printf + make_stack_thunk(ets_uart_printf); +#endif +}; +//////////////////////////////////////////////////////////////////// + +void setup(void) { + Serial.begin(115200); + delay(20); // This delay helps when using the 'Modified Serial monitor' otherwise it is not needed. + Serial.println(); + Serial.println(); + 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 + /* + When a call to disable_extra4k_at_link_time() is made, building with HWDT + selected on the Arduino IDE menu "Tools->Debug Level", will have the same + result as if built with HWDT_NOEXTRA4K selected. + */ + disable_extra4k_at_link_time(); +#endif + + Serial.printf_P(PSTR("This example was built with%s an extra 4K of heap space (g_pcont == 0x%08lX)\r\n"), ((uintptr_t)0x3FFFC000 < (uintptr_t)g_pcont) ? "" : "out", (uintptr_t)g_pcont); +#if defined(DEBUG_ESP_HWDT) || defined(DEBUG_ESP_HWDT_NOEXTRA4K) + Serial.print(F("and with the HWDT")); +#if defined(DEBUG_ESP_HWDT_NOEXTRA4K) + Serial.print(F("_NOEXTRA4K")); +#endif + Serial.println(F(" option specified.")); +#endif + + Serial.println(); + Serial.println(F("The Hardware Watchdog Timer Demo is now available for crashing ...")); + Serial.println(); + processKey(Serial, '?'); +} + + +void loop(void) { + if (Serial.available() > 0) { + int hotKey = Serial.read(); + processKey(Serial, hotKey); + } +} 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/HwdtStackDump/ProcessKey.ino b/libraries/esp8266/examples/HwdtStackDump/ProcessKey.ino new file mode 100644 index 0000000000..4c98899cf4 --- /dev/null +++ b/libraries/esp8266/examples/HwdtStackDump/ProcessKey.ino @@ -0,0 +1,117 @@ +#include +void crashMeIfYouCan(void) __attribute__((weak)); +int divideA_B(int a, int b); + +int* nullPointer = NULL; + +void processKey(Print& out, int hotKey) { + switch (hotKey) { + case 'r': + out.printf_P(PSTR("Reset, ESP.reset(); ...\r\n")); + ESP.reset(); + break; + case 't': + out.printf_P(PSTR("Restart, ESP.restart(); ...\r\n")); + ESP.restart(); + break; + case 's': + { + uint32_t startTime = millis(); + out.printf_P(PSTR("Now crashing with Software WDT. This will take about 3 seconds.\r\n")); + ets_install_putc1(ets_putc); + while (true) { + ets_printf("%9lu\r", (millis() - startTime)); + ets_delay_us(250000); + // stay in an loop blocking other system activity. + } + } + break; + case 'h': + out.printf_P(PSTR("Now crashing with Hardware WDT. This will take about 6 seconds.\r\n")); + asm volatile("mov.n a2, %0\n\t" + "mov.n a3, %1\n\t" + "mov.n a4, %2\n\t" + "mov.n a5, %3\n\t" + "mov.n a6, %4\n\t" + : + : "r"(0xaaaaaaaa), "r"(0xaaaaaaaa), "r"(0xaaaaaaaa), "r"(0xaaaaaaaa), "r"(0xaaaaaaaa) + : "memory"); + // Could not find these in the stack dump, unless interrupts were enabled. + { + uint32_t startTime = millis(); + // Avoid all the Core functions that play nice, so we can hog + // the system and crash. + ets_install_putc1(ets_putc); + xt_rsil(15); + while (true) { + ets_printf("%9lu\r", (millis() - startTime)); + ets_delay_us(250000); + // stay in an loop blocking other system activity. + // + // Note: + // Hardware WDT kicks in if Software WDT is unable to perform. + // With the Hardware WDT, nothing is saved on the stack, that I have seen. + } + } + break; + case 'p': + out.println(F("Time to panic()!")); + panic(); + break; + case 'z': + out.println(F("Crashing by dividing by zero. This should generate an exception(0).")); + out.printf_P(PSTR("This should not print %d\n"), divideA_B(1, 0)); + break; + case 'w': + out.println(F("Now calling: void crashMeIfYouCan(void)__attribute__((weak));")); + out.println(F("This function has a prototype but was missing when the sketch was linked. ...")); + crashMeIfYouCan(); + break; + case 'b': + out.println(F("Executing a break instruction w/o GDB will cause a HWDT reset.")); + asm volatile("break 1, 15;"); + out.println(F("This line will not be printable w/o running GDB")); + break; + case '0': + out.println(F("Crashing at an embedded 'break 1, 15' instruction that was generated")); + out.println(F("by the compiler after detecting a divide by zero.")); + out.printf_P(PSTR("This should not print %d\n"), divideA_B_bp(1, 0)); + break; + case '\r': out.println(); + case '\n': break; + case '?': + out.println(); + out.println(F("Press a key + ")); + out.println(F(" r - Reset, ESP.reset();")); + out.println(F(" t - Restart, ESP.restart();")); + out.println(F(" ? - Print Help")); + out.println(); + out.println(F("Crash with:")); + out.println(F(" s - Software WDT")); + out.println(F(" h - Hardware WDT - looping with interrupts disabled")); + out.println(F(" w - Hardware WDT - calling a missing (weak) function.")); + out.println(F(" 0 - Hardware WDT - a hard coded compiler breakpoint from a compile time detected divide by zero")); + out.println(F(" b - Hardware WDT - a forgotten hard coded 'break 1, 15;' and no GDB running.")); + out.println(F(" z - Divide by zero, exception(0);")); + out.println(F(" p - panic();")); + out.println(); + break; + default: + out.printf_P(PSTR("\"%c\" - Not an option? / ? - help"), hotKey); + out.println(); + processKey(out, '?'); + break; + } +} + +// With the current toolchain 10.1, using this to divide by zero will *not* be +// caught at compile time. +int __attribute__((noinline)) divideA_B(int a, int b) { + return (a / b); +} + +// With the current toolchain 10.1, using this to divide by zero *will* be +// caught at compile time. And a hard coded breakpoint will be inserted. +int divideA_B_bp(int a, int b) { + return (a / b); +} diff --git a/libraries/esp8266/examples/I2SInput/I2SInput.ino b/libraries/esp8266/examples/I2SInput/I2SInput.ino index 0f1ba7e432..6e2e11b07b 100644 --- a/libraries/esp8266/examples/I2SInput/I2SInput.ino +++ b/libraries/esp8266/examples/I2SInput/I2SInput.ino @@ -28,14 +28,14 @@ */ #include -#include +#include void setup() { Serial.begin(115200); WiFi.forceSleepBegin(); delay(500); - i2s_rxtx_begin(true, false); // Enable I2S RX + i2s_rxtx_begin(true, false); // Enable I2S RX i2s_set_rate(11025); delay(1000); diff --git a/libraries/esp8266/examples/I2STransmit/I2STransmit.ino b/libraries/esp8266/examples/I2STransmit/I2STransmit.ino index 0c7a7eb43c..229e34fcd0 100644 --- a/libraries/esp8266/examples/I2STransmit/I2STransmit.ino +++ b/libraries/esp8266/examples/I2STransmit/I2STransmit.ino @@ -10,11 +10,11 @@ #include #include -#include +#include #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif // Set your network here @@ -26,7 +26,7 @@ WiFiUDP udp; const IPAddress listener = { 192, 168, 1, 2 }; const int port = 8266; -int16_t buffer[100][2]; // Temp staging for samples +int16_t buffer[100][2]; // Temp staging for samples void setup() { Serial.begin(115200); @@ -48,7 +48,7 @@ void setup() { Serial.print("My IP: "); Serial.println(WiFi.localIP()); - i2s_rxtx_begin(true, false); // Enable I2S RX + i2s_rxtx_begin(true, false); // Enable I2S RX i2s_set_rate(11025); Serial.print("\nStart the listener on "); @@ -60,21 +60,16 @@ void setup() { udp.beginPacket(listener, port); udp.write("I2S Receiver\r\n"); udp.endPacket(); - } void loop() { - static int cnt = 0; + static uint32_t cnt = 0; // Each loop will send 100 raw samples (400 bytes) // UDP needs to be < TCP_MSS which can be 500 bytes in LWIP2 - for (int i = 0; i < 100; i++) { - i2s_read_sample(&buffer[i][0], &buffer[i][1], true); - } + for (int i = 0; i < 100; i++) { i2s_read_sample(&buffer[i][0], &buffer[i][1], true); } udp.beginPacket(listener, port); - udp.write((uint8_t*)buffer, sizeof(buffer)); + udp.write((uint8_t *)buffer, sizeof(buffer)); udp.endPacket(); cnt++; - if ((cnt % 100) == 0) { - Serial.printf("%d\n", cnt); - } + if ((cnt % 100) == 0) { Serial.printf("%" PRIu32 "\n", cnt); } } diff --git a/libraries/esp8266/examples/IramReserve/IramReserve.ino b/libraries/esp8266/examples/IramReserve/IramReserve.ino index 344622f958..24bea8e012 100644 --- a/libraries/esp8266/examples/IramReserve/IramReserve.ino +++ b/libraries/esp8266/examples/IramReserve/IramReserve.ino @@ -17,6 +17,12 @@ #include #if defined(UMM_HEAP_IRAM) +#if defined(CORE_MOCK) +#define XCHAL_INSTRAM1_VADDR 0x40100000 +#else +#include // For config/core-isa.h +#endif + // durable - as in long life, persisting across reboots. struct durable { uint32_t bootCounter; @@ -30,7 +36,7 @@ struct durable { #define IRAM_RESERVE_SZ ((sizeof(struct durable) + 7UL) & ~7UL) // Position its address just above the reduced 2nd Heap. -#define IRAM_RESERVE (0x40100000UL + 0xC000UL - IRAM_RESERVE_SZ) +#define IRAM_RESERVE ((uintptr_t)XCHAL_INSTRAM1_VADDR + 0xC000UL - IRAM_RESERVE_SZ) // Define a reference with the right properties to make access easier. #define DURABLE ((struct durable *)IRAM_RESERVE) @@ -52,8 +58,7 @@ extern struct rst_info resetInfo; XOR sum on the IRAM data (or just a section of the IRAM data). */ inline bool is_iram_valid(void) { - return (REASON_WDT_RST <= resetInfo.reason && - REASON_SOFT_RESTART >= resetInfo.reason); + return (REASON_WDT_RST <= resetInfo.reason && REASON_SOFT_RESTART >= resetInfo.reason); } @@ -64,16 +69,13 @@ void setup() { delay(10); Serial.printf_P(PSTR("\r\nSetup ...\r\n")); - if (!is_iram_valid()) { - DURABLE->bootCounter = 0; - } + if (!is_iram_valid()) { DURABLE->bootCounter = 0; } DURABLE->bootCounter++; Serial.printf("Number of reboots at %u\r\n", DURABLE->bootCounter); Serial.printf("\r\nSome less than direct, ways to restart:\r\n"); processKey(Serial, '?'); - } void loop(void) { @@ -100,14 +102,11 @@ extern "C" void umm_init_iram(void) { adjustments and checksums. These can affect the persistence of data across reboots. */ - uint32_t sec_heap = (uint32_t)_text_end + 32; + uintptr_t sec_heap = (uintptr_t)_text_end + 32; sec_heap &= ~7; - size_t sec_heap_sz = 0xC000UL - (sec_heap - 0x40100000UL); - sec_heap_sz -= IRAM_RESERVE_SZ; // Shrink IRAM heap - if (0xC000UL > sec_heap_sz) { - - umm_init_iram_ex((void *)sec_heap, sec_heap_sz, true); - } + size_t sec_heap_sz = 0xC000UL - (sec_heap - (uintptr_t)XCHAL_INSTRAM1_VADDR); + sec_heap_sz -= IRAM_RESERVE_SZ; // Shrink IRAM heap + if (0xC000UL > sec_heap_sz) { umm_init_iram_ex((void *)sec_heap, sec_heap_sz, true); } } #else @@ -119,6 +118,5 @@ void setup() { Serial.println("\r\n\r\nThis sketch requires Tools Option: 'MMU: 16KB cache + 48KB IRAM and 2nd Heap (shared)'"); } -void loop(void) { -} +void loop(void) {} #endif diff --git a/libraries/esp8266/examples/IramReserve/ProcessKey.ino b/libraries/esp8266/examples/IramReserve/ProcessKey.ino index 9c12ac7c1c..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)); +void crashMeIfYouCan(void) __attribute__((weak)); int divideA_B(int a, int b); +int divideA_B_bp(int a, int b); int* nullPointer = NULL; @@ -14,7 +15,8 @@ void processKey(Print& out, int hotKey) { out.printf_P(PSTR("Restart, ESP.restart(); ...\r\n")); ESP.restart(); break; - case 's': { + case 's': + { uint32_t startTime = millis(); out.printf_P(PSTR("Now crashing with Software WDT. This will take about 3 seconds.\r\n")); ets_install_putc1(ets_putc); @@ -32,7 +34,9 @@ void processKey(Print& out, int hotKey) { "mov.n a4, %2\n\t" "mov.n a5, %3\n\t" "mov.n a6, %4\n\t" - : : "r"(0xaaaaaaaa), "r"(0xaaaaaaaa), "r"(0xaaaaaaaa), "r"(0xaaaaaaaa), "r"(0xaaaaaaaa) : "memory"); + : + : "r"(0xaaaaaaaa), "r"(0xaaaaaaaa), "r"(0xaaaaaaaa), "r"(0xaaaaaaaa), "r"(0xaaaaaaaa) + : "memory"); // Could not find these in the stack dump, unless interrupts were enabled. { uint32_t startTime = millis(); @@ -70,14 +74,12 @@ void processKey(Print& out, int hotKey) { out.println(F("This line will not be printable w/o running GDB")); break; case '0': - out.println(F("Crashing at an embeded 'break 1, 15' instruction that was generated")); + out.println(F("Crashing at an embedded 'break 1, 15' instruction that was generated")); out.println(F("by the compiler after detecting a divide by zero.")); out.printf_P(PSTR("This should not print %d\n"), divideA_B_bp(1, 0)); break; - case '\r': - out.println(); - case '\n': - break; + case '\r': out.println(); + case '\n': break; case '?': out.println(); out.println(F("Press a key + ")); diff --git a/libraries/esp8266/examples/LowPowerDemo/LowPowerDemo.ino b/libraries/esp8266/examples/LowPowerDemo/LowPowerDemo.ino index 7556c58820..27dbb77184 100644 --- a/libraries/esp8266/examples/LowPowerDemo/LowPowerDemo.ino +++ b/libraries/esp8266/examples/LowPowerDemo/LowPowerDemo.ino @@ -38,14 +38,14 @@ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include -#include // crc32() +#include // crc32() #include -#include // WiFiState structure details +#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) +#define DEBUG_PRINTLN(x) Serial.println(x) +#define DEBUG_PRINT(x) Serial.print(x) #else #define DEBUG_PRINTLN(x) #define DEBUG_PRINT(x) @@ -56,23 +56,23 @@ // 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)! // enter your WiFi configuration below -const char* AP_SSID = "SSID"; // your router's SSID here +const char* AP_SSID = "SSID"; // your router's SSID here const char* AP_PASS = "password"; // your router's password here -IPAddress staticIP(0, 0, 0, 0); // parameters below are for your static IP address, if used +IPAddress staticIP(0, 0, 0, 0); // parameters below are for your static IP address, if used IPAddress gateway(0, 0, 0, 0); IPAddress subnet(0, 0, 0, 0); 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) @@ -85,7 +85,7 @@ uint32_t timeout = 30E3; // 30 second timeout on the WiFi connection // This structure is stored in RTC memory to save the WiFi state and reset count (number of Deep Sleeps), // and it reconnects twice as fast as the first connection; it's used several places in this demo struct nv_s { - WiFiState wss; // core's WiFi save state + WiFiState wss; // core's WiFi save state struct { uint32_t crc32; @@ -94,29 +94,29 @@ struct nv_s { } rtcData; }; -static nv_s* nv = (nv_s*)RTC_USER_MEM; // user RTC RAM area +static nv_s* nv = (nv_s*)RTC_USER_MEM; // user RTC RAM area uint32_t resetCount = 0; // keeps track of the number of Deep Sleep tests / resets -const uint32_t blinkDelay = 100; // fast blink rate for the LED when waiting for the user +const uint32_t blinkDelay = 100; // fast blink rate for the LED when waiting for the user esp8266::polledTimeout::periodicMs blinkLED(blinkDelay); // LED blink delay without delay() -esp8266::polledTimeout::oneShotMs altDelay(blinkDelay); // tight loop to simulate user code -esp8266::polledTimeout::oneShotMs wifiTimeout(timeout); // 30 second timeout on WiFi connection +esp8266::polledTimeout::oneShotMs altDelay(blinkDelay); // tight loop to simulate user code +esp8266::polledTimeout::oneShotMs wifiTimeout(timeout); // 30 second timeout on WiFi connection // use fully qualified type and avoid importing all ::esp8266 namespace to the global namespace void wakeupCallback() { // unlike ISRs, you can do a print() from a callback function - testPoint_LOW; // testPoint tracks latency from WAKE_UP_PIN LOW to testPoint LOW - printMillis(); // show time difference across sleep; millis is wrong as the CPU eventually stops + testPoint_LOW; // testPoint tracks latency from WAKE_UP_PIN LOW to testPoint LOW + printMillis(); // show time difference across sleep; millis is wrong as the CPU eventually stops Serial.println(F("Woke from Light Sleep - this is the callback")); } void setup() { #ifdef TESTPOINT pinMode(testPointPin, OUTPUT); // test point for Light Sleep and Deep Sleep tests - testPoint_LOW; // Deep Sleep reset doesn't clear GPIOs, testPoint LOW shows boot time + testPoint_LOW; // Deep Sleep reset doesn't clear GPIOs, testPoint LOW shows boot time #endif - pinMode(LED, OUTPUT); // activity and status indicator - digitalWrite(LED, LOW); // turn on the LED + pinMode(LED, OUTPUT); // activity and status indicator + digitalWrite(LED, LOW); // turn on the LED pinMode(WAKE_UP_PIN, INPUT_PULLUP); // polled to advance tests, interrupt for Forced Light Sleep Serial.begin(115200); Serial.println(); @@ -124,17 +124,15 @@ void setup() { String resetCause = ESP.getResetReason(); Serial.println(resetCause); resetCount = 0; - if ((resetCause == "External System") || (resetCause == "Power on")) { - Serial.println(F("I'm awake and starting the Low Power tests")); - } + if ((resetCause == "External System") || (resetCause == "Power on")) { Serial.println(F("I'm awake and starting the Low Power tests")); } // 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")) { + uint32_t crcOfData = crc32((uint8_t*)&nv->rtcData.rstCount, sizeof(nv->rtcData.rstCount)); + if ((crcOfData == nv->rtcData.crc32) && (resetCause == "Deep-Sleep Wake")) { resetCount = nv->rtcData.rstCount; // read the previous reset count resetCount++; } - nv->rtcData.rstCount = resetCount; // update the reset count & CRC + nv->rtcData.rstCount = resetCount; // update the reset count & CRC updateRTCcrc(); if (resetCount == 1) { // show that millis() is cleared across the Deep Sleep reset @@ -164,7 +162,7 @@ void loop() { } else if (resetCount == 4) { resetTests(); } -} //end of loop() +} // end of loop() void runTest1() { Serial.println(F("\n1st test - running with WiFi unconfigured")); @@ -181,13 +179,13 @@ void runTest2() { Serial.println(F("The amperage will drop in 7 seconds.")); readVoltage(); // read internal VCC Serial.println(F("press the switch to continue")); - waitPushbutton(true, 90); /* This is using a special feature: below 100 mS blink delay, - the LED blink delay is padding 100 mS time with 'program cycles' to fill the 100 mS. - At 90 mS delay, 90% of the blink time is delay(), and 10% is 'your program running'. - Below 90% you'll see a difference in the average amperage: less delay() = more amperage. - At 100 mS and above it's essentially all delay() time. On an oscilloscope you'll see the - time between beacons at > 67 mA more often with less delay() percentage. You can change - the '90' mS to other values to see the effect it has on Automatic Modem Sleep. */ + waitPushbutton(true, 90); /* This is using a special feature: below 100 mS blink delay, + the LED blink delay is padding 100 mS time with 'program cycles' to fill the 100 mS. + At 90 mS delay, 90% of the blink time is delay(), and 10% is 'your program running'. + Below 90% you'll see a difference in the average amperage: less delay() = more amperage. + At 100 mS and above it's essentially all delay() time. On an oscilloscope you'll see the + time between beacons at > 67 mA more often with less delay() percentage. You can change + the '90' mS to other values to see the effect it has on Automatic Modem Sleep. */ } else { Serial.println(F("no WiFi connection, test skipped")); } @@ -195,16 +193,16 @@ void runTest2() { void runTest3() { Serial.println(F("\n3rd test - Forced Modem Sleep")); - WiFi.mode(WIFI_SHUTDOWN, &nv->wss); // shut the modem down and save the WiFi state for faster reconnection + WiFi.shutdown(nv->wss); // shut the modem down and save the WiFi state for faster reconnection // WiFi.forceSleepBegin(delay_in_uS); // alternate method of Forced Modem Sleep for an optional timed shutdown, // with WiFi.forceSleepBegin(0xFFFFFFF); the modem sleeps until you wake it, with values <= 0xFFFFFFE it's timed // delay(10); // it doesn't always go to sleep unless you delay(10); yield() wasn't reliable readVoltage(); // read internal VCC Serial.println(F("press the switch to continue")); - waitPushbutton(true, 99); /* Using the same < 100 mS feature. If you drop the delay below 100, you - will see the effect of program time vs. delay() time on minimum amperage. Above ~ 97 (97% of the - time in delay) there is little change in amperage, so you need to spend maximum time in delay() - to get minimum amperage.*/ + waitPushbutton(true, 99); /* Using the same < 100 mS feature. If you drop the delay below 100, you + will see the effect of program time vs. delay() time on minimum amperage. Above ~ 97 (97% of the + time in delay) there is little change in amperage, so you need to spend maximum time in delay() + to get minimum amperage.*/ } void runTest4() { @@ -215,13 +213,11 @@ void runTest4() { // and WiFi reconnects after the forceSleepWake more quickly digitalWrite(LED, LOW); // visual cue that we're reconnecting WiFi uint32_t wifiBegin = millis(); - WiFi.forceSleepWake(); // reconnect with previous STA mode and connection settings + WiFi.forceSleepWake(); // reconnect with previous STA mode and connection settings WiFi.setSleepMode(WIFI_LIGHT_SLEEP, 3); // Automatic Light Sleep, DTIM listen interval = 3 // at higher DTIM intervals you'll have a hard time establishing and maintaining a connection wifiTimeout.reset(timeout); - while (((!WiFi.localIP()) || (WiFi.status() != WL_CONNECTED)) && (!wifiTimeout)) { - yield(); - } + while (((!WiFi.localIP()) || (WiFi.status() != WL_CONNECTED)) && (!wifiTimeout)) { yield(); } if ((WiFi.status() == WL_CONNECTED) && WiFi.localIP()) { // won't go into Automatic Sleep without an active WiFi connection float reConn = (millis() - wifiBegin); @@ -229,9 +225,9 @@ void runTest4() { Serial.printf("%1.2f seconds\n", reConn / 1000); readVoltage(); // read internal VCC Serial.println(F("long press of the switch to continue")); - waitPushbutton(true, 350); /* Below 100 mS delay it only goes into 'Automatic Modem Sleep', - and below ~ 350 mS delay() the 'Automatic Light Sleep' is less frequent. Above 500 mS - delay() doesn't make significant improvement in power savings. */ + waitPushbutton(true, 350); /* Below 100 mS delay it only goes into 'Automatic Modem Sleep', + and below ~ 350 mS delay() the 'Automatic Light Sleep' is less frequent. Above 500 mS + delay() doesn't make significant improvement in power savings. */ } else { Serial.println(F("no WiFi connection, test skipped")); } @@ -241,61 +237,61 @@ void runTest5() { Serial.println(F("\n5th test - Timed Light Sleep, wake in 10 seconds")); Serial.println(F("Press the button when you're ready to proceed")); waitPushbutton(true, blinkDelay); - WiFi.mode(WIFI_OFF); // you must turn the modem off; using disconnect won't work - readVoltage(); // read internal VCC - printMillis(); // show millis() across sleep, including Serial.flush() + WiFi.mode(WIFI_OFF); // you must turn the modem off; using disconnect won't work + readVoltage(); // read internal VCC + printMillis(); // show millis() across sleep, including Serial.flush() digitalWrite(LED, HIGH); // turn the LED off so they know the CPU isn't running - testPoint_HIGH; // testPoint LOW in callback tracks delay from testPoint HIGH to LOW - extern os_timer_t *timer_list; + testPoint_HIGH; // testPoint LOW in callback tracks delay from testPoint HIGH to LOW + extern os_timer_t* timer_list; timer_list = nullptr; // stop (but don't disable) the 4 OS timers wifi_fpm_set_sleep_type(LIGHT_SLEEP_T); gpio_pin_wakeup_enable(GPIO_ID_PIN(WAKE_UP_PIN), GPIO_PIN_INTR_LOLEVEL); // GPIO wakeup (optional) // only LOLEVEL or HILEVEL interrupts work, no edge, that's an SDK or CPU limitation - wifi_fpm_set_wakeup_cb(wakeupCallback); // set wakeup callback + wifi_fpm_set_wakeup_cb(wakeupCallback); // set wakeup callback // the callback is optional, but without it the modem will wake in 10 seconds then delay(10 seconds) // with the callback the sleep time is only 10 seconds total, no extra delay() afterward wifi_fpm_open(); - wifi_fpm_do_sleep(10E6); // Sleep range = 10000 ~ 268,435,454 uS (0xFFFFFFE, 2^28-1) - delay(10e3 + 1); // delay needs to be 1 mS longer than sleep or it only goes into Modem Sleep + wifi_fpm_do_sleep(10E6); // Sleep range = 10000 ~ 268,435,454 uS (0xFFFFFFE, 2^28-1) + delay(10e3 + 1); // delay needs to be 1 mS longer than sleep or it only goes into Modem Sleep Serial.println(F("Woke up!")); // the interrupt callback hits before this is executed } void runTest6() { Serial.println(F("\n6th test - Forced Light Sleep, wake with GPIO interrupt")); Serial.flush(); - WiFi.mode(WIFI_OFF); // you must turn the modem off; using disconnect won't work + WiFi.mode(WIFI_OFF); // you must turn the modem off; using disconnect won't work digitalWrite(LED, HIGH); // turn the LED off so they know the CPU isn't running - readVoltage(); // read internal VCC + readVoltage(); // read internal VCC Serial.println(F("CPU going to sleep, pull WAKE_UP_PIN low to wake it (press the switch)")); - printMillis(); // show millis() across sleep, including Serial.flush() + printMillis(); // show millis() across sleep, including Serial.flush() testPoint_HIGH; // testPoint tracks latency from WAKE_UP_PIN LOW to testPoint LOW in callback wifi_fpm_set_sleep_type(LIGHT_SLEEP_T); gpio_pin_wakeup_enable(GPIO_ID_PIN(WAKE_UP_PIN), GPIO_PIN_INTR_LOLEVEL); // only LOLEVEL or HILEVEL interrupts work, no edge, that's an SDK or CPU limitation - wifi_fpm_set_wakeup_cb(wakeupCallback); // Set wakeup callback (optional) + wifi_fpm_set_wakeup_cb(wakeupCallback); // Set wakeup callback (optional) wifi_fpm_open(); - wifi_fpm_do_sleep(0xFFFFFFF); // only 0xFFFFFFF, any other value and it won't disconnect the RTC timer - delay(10); // it goes to sleep during this delay() and waits for an interrupt + wifi_fpm_do_sleep(0xFFFFFFF); // only 0xFFFFFFF, any other value and it won't disconnect the RTC timer + delay(10); // it goes to sleep during this delay() and waits for an interrupt Serial.println(F("Woke up!")); // the interrupt callback hits before this is executed*/ } void runTest7() { Serial.println(F("\n7th test - Deep Sleep for 10 seconds, reset and wake with RF_DEFAULT")); - initWiFi(); // initialize WiFi since we turned it off in the last test + initWiFi(); // initialize WiFi since we turned it off in the last test readVoltage(); // read internal VCC Serial.println(F("press the switch to continue")); while (!digitalRead(WAKE_UP_PIN)) { // wait for them to release the switch from the previous test delay(10); } - delay(50); // debounce time for the switch, pushbutton released + delay(50); // debounce time for the switch, pushbutton released waitPushbutton(false, blinkDelay); // set true if you want to see Automatic Modem Sleep - digitalWrite(LED, LOW); // turn the LED on, at least briefly - //WiFi.mode(WIFI_SHUTDOWN, &nv->wss); // Forced Modem Sleep for a more Instant Deep Sleep, - // and no extended RFCAL as it goes into Deep Sleep + digitalWrite(LED, LOW); // turn the LED on, at least briefly + // WiFi.shutdown(nv->wss); // Forced Modem Sleep for a more Instant Deep Sleep, + // and no extended RFCAL as it goes into Deep Sleep Serial.println(F("going into Deep Sleep now...")); - printMillis(); // show time difference across sleep - testPoint_HIGH; // testPoint set HIGH to track Deep Sleep period, cleared at startup() - ESP.deepSleep(10E6, WAKE_RF_DEFAULT); // good night! D0 fires a reset in 10 seconds... + printMillis(); // show time difference across sleep + testPoint_HIGH; // testPoint set HIGH to track Deep Sleep period, cleared at startup() + ESP.deepSleep(10E6, WAKE_RF_DEFAULT); // good night! D0 fires a reset in 10 seconds... // if you do ESP.deepSleep(0, mode); it needs a RESET to come out of sleep (RTC is disconnected) // maximum timed Deep Sleep interval ~ 3 to 4 hours depending on the RTC timer, see the README // the 2 uA GPIO amperage during Deep Sleep can't drive the LED so it's not lit now, although @@ -308,12 +304,12 @@ void runTest8() { readVoltage(); // read internal VCC Serial.println(F("press the switch to continue")); waitPushbutton(false, blinkDelay); // set true if you want to see Automatic Modem Sleep - //WiFi.mode(WIFI_SHUTDOWN, &nv->wss); // Forced Modem Sleep for a more Instant Deep Sleep, - // and no extended RFCAL as it goes into Deep Sleep + // WiFi.shutdown(nv->wss); // Forced Modem Sleep for a more Instant Deep Sleep, + // and no extended RFCAL as it goes into Deep Sleep Serial.println(F("going into Deep Sleep now...")); - Serial.flush(); // needs a delay(10) or Serial.flush() else it doesn't print the whole message - testPoint_HIGH; // testPoint set HIGH to track Deep Sleep period, cleared at startup() - ESP.deepSleep(10E6, WAKE_RFCAL); // good night! D0 fires a reset in 10 seconds... + Serial.flush(); // needs a delay(10) or Serial.flush() else it doesn't print the whole message + testPoint_HIGH; // testPoint set HIGH to track Deep Sleep period, cleared at startup() + ESP.deepSleep(10E6, WAKE_RFCAL); // good night! D0 fires a reset in 10 seconds... Serial.println(F("What... I'm not asleep?!?")); // it will never get here } @@ -322,11 +318,11 @@ void runTest9() { readVoltage(); // read internal VCC Serial.println(F("press the switch to continue")); waitPushbutton(false, blinkDelay); // set true if you want to see Automatic Modem Sleep - WiFi.mode(WIFI_SHUTDOWN, &nv->wss); // Forced Modem Sleep for a more Instant Deep Sleep + WiFi.shutdown(nv->wss); // Forced Modem Sleep for a more Instant Deep Sleep Serial.println(F("going into Deep Sleep now...")); - Serial.flush(); // needs a delay(10) or Serial.flush() else it doesn't print the whole message - testPoint_HIGH; // testPoint set HIGH to track Deep Sleep period, cleared at startup() - ESP.deepSleepInstant(10E6, WAKE_NO_RFCAL); // good night! D0 fires a reset in 10 seconds... + Serial.flush(); // needs a delay(10) or Serial.flush() else it doesn't print the whole message + testPoint_HIGH; // testPoint set HIGH to track Deep Sleep period, cleared at startup() + ESP.deepSleepInstant(10E6, WAKE_NO_RFCAL); // good night! D0 fires a reset in 10 seconds... Serial.println(F("What... I'm not asleep?!?")); // it will never get here } @@ -335,11 +331,11 @@ void runTest10() { readVoltage(); // read internal VCC Serial.println(F("press the switch to continue")); waitPushbutton(false, blinkDelay); // set true if you want to see Automatic Modem Sleep - //WiFi.mode(WIFI_SHUTDOWN); // Forced Modem Sleep for a more Instant Deep Sleep + // WiFi.forceSleepBegin(); // Forced Modem Sleep for a more Instant Deep Sleep Serial.println(F("going into Deep Sleep now...")); - Serial.flush(); // needs a delay(10) or Serial.flush() else it doesn't print the whole message - testPoint_HIGH; // testPoint set HIGH to track Deep Sleep period, cleared at startup() - ESP.deepSleepInstant(10E6, WAKE_RF_DISABLED); // good night! D0 fires a reset in 10 seconds... + Serial.flush(); // needs a delay(10) or Serial.flush() else it doesn't print the whole message + testPoint_HIGH; // testPoint set HIGH to track Deep Sleep period, cleared at startup() + ESP.deepSleepInstant(10E6, WAKE_RF_DISABLED); // good night! D0 fires a reset in 10 seconds... Serial.println(F("What... I'm not asleep?!?")); // it will never get here } @@ -361,25 +357,25 @@ void waitPushbutton(bool usesDelay, unsigned int delayTime) { // loop until the } yield(); // this would be a good place for ArduinoOTA.handle(); } - } else { // long delay() for the 3 modes that need it, but it misses quick switch presses - while (digitalRead(WAKE_UP_PIN)) { // wait for a pushbutton press + } else { // long delay() for the 3 modes that need it, but it misses quick switch presses + while (digitalRead(WAKE_UP_PIN)) { // wait for a pushbutton press digitalWrite(LED, !digitalRead(LED)); // toggle the activity LED - delay(delayTime); // another good place for ArduinoOTA.handle(); + delay(delayTime); // another good place for ArduinoOTA.handle(); if (delayTime < 100) { altDelay.reset(100 - delayTime); // pad the time < 100 mS with some real CPU cycles - while (!altDelay) { // this simulates 'your program running', not delay() time + while (!altDelay) { // this simulates 'your program running', not delay() time } } } } - delay(50); // debounce time for the switch, pushbutton pressed + delay(50); // debounce time for the switch, pushbutton pressed while (!digitalRead(WAKE_UP_PIN)) { // now wait for them to release the pushbutton delay(10); } delay(50); // debounce time for the switch, pushbutton released } -void readVoltage() { // read internal VCC +void readVoltage() { // read internal VCC float volts = ESP.getVcc(); Serial.printf("The internal VCC reads %1.2f volts\n", volts / 1000); } @@ -391,27 +387,27 @@ void printMillis() { } void updateRTCcrc() { // updates the reset count CRC - nv->rtcData.crc32 = crc32((uint8_t*) &nv->rtcData.rstCount, sizeof(nv->rtcData.rstCount)); + nv->rtcData.crc32 = crc32((uint8_t*)&nv->rtcData.rstCount, sizeof(nv->rtcData.rstCount)); } void initWiFi() { - digitalWrite(LED, LOW); // give a visual indication that we're alive but busy with WiFi + digitalWrite(LED, LOW); // give a visual indication that we're alive but busy with WiFi uint32_t wifiBegin = millis(); // how long does it take to connect - if ((crc32((uint8_t*) &nv->rtcData.rstCount + 1, sizeof(nv->wss)) && !WiFi.shutdownValidCRC(&nv->wss))) { + if ((crc32((uint8_t*)&nv->rtcData.rstCount + 1, sizeof(nv->wss)) && !WiFi.shutdownValidCRC(nv->wss))) { // if good copy of wss, overwrite invalid (primary) copy - memcpy((uint32_t*) &nv->wss, (uint32_t*) &nv->rtcData.rstCount + 1, sizeof(nv->wss)); + memcpy((uint32_t*)&nv->wss, (uint32_t*)&nv->rtcData.rstCount + 1, sizeof(nv->wss)); } - if (WiFi.shutdownValidCRC(&nv->wss)) { // if we have a valid WiFi saved state - memcpy((uint32_t*) &nv->rtcData.rstCount + 1, (uint32_t*) &nv->wss, sizeof(nv->wss)); // save a copy of it + if (WiFi.shutdownValidCRC(nv->wss)) { // if we have a valid WiFi saved state + memcpy((uint32_t*)&nv->rtcData.rstCount + 1, (uint32_t*)&nv->wss, sizeof(nv->wss)); // save a copy of it Serial.println(F("resuming WiFi")); } - if (!(WiFi.mode(WIFI_RESUME, &nv->wss))) { // couldn't resume, or no valid saved WiFi state yet + if (!(WiFi.resumeFromShutdown(nv->wss))) { // couldn't resume, or no valid saved WiFi state yet /* Explicitly set the ESP8266 as a WiFi-client (STAtion mode), otherwise by default it would try to act as both a client and an access-point and could cause network issues with other WiFi devices on your network. */ WiFi.persistent(false); // don't store the connection each time to save wear on the flash WiFi.mode(WIFI_STA); - WiFi.setOutputPower(10); // reduce RF output power, increase if it won't connect + WiFi.setOutputPower(10); // reduce RF output power, increase if it won't connect WiFi.config(staticIP, gateway, subnet); // if using static IP, enter parameters at the top WiFi.begin(AP_SSID, AP_PASS); Serial.print(F("connecting to WiFi ")); @@ -420,9 +416,7 @@ void initWiFi() { DEBUG_PRINTLN(WiFi.macAddress()); } wifiTimeout.reset(timeout); - while (((!WiFi.localIP()) || (WiFi.status() != WL_CONNECTED)) && (!wifiTimeout)) { - yield(); - } + while (((!WiFi.localIP()) || (WiFi.status() != WL_CONNECTED)) && (!wifiTimeout)) { yield(); } if ((WiFi.status() == WL_CONNECTED) && WiFi.localIP()) { DEBUG_PRINTLN(F("WiFi connected")); Serial.print(F("WiFi connect time = ")); diff --git a/libraries/esp8266/examples/MMU48K/MMU48K.ino b/libraries/esp8266/examples/MMU48K/MMU48K.ino index 6acb3840f9..93f2bde32b 100644 --- a/libraries/esp8266/examples/MMU48K/MMU48K.ino +++ b/libraries/esp8266/examples/MMU48K/MMU48K.ino @@ -3,15 +3,21 @@ #include #include -uint32_t timed_byte_read(char *pc, uint32_t * o); -uint32_t timed_byte_read2(char *pc, uint32_t * o); +#if defined(CORE_MOCK) +#define XCHAL_INSTRAM1_VADDR 0x40100000 +#else +#include // For config/core-isa.h +#endif + +uint32_t timed_byte_read(char *pc, uint32_t *o); +uint32_t timed_byte_read2(char *pc, uint32_t *o); int divideA_B(int a, int b); -int* nullPointer = NULL; +int *nullPointer = NULL; -char *probe_b = NULL; +char *probe_b = NULL; short *probe_s = NULL; -char *probe_c = (char *)0x40110000; +char *probe_c = (char *)0x40110000; short *unaligned_probe_s = NULL; uint32_t read_var = 0x11223344; @@ -27,7 +33,7 @@ uint32_t read_var = 0x11223344; uint32_t *gobble; size_t gobble_sz; -#elif (MMU_IRAM_SIZE > 32*1024) +#elif (MMU_IRAM_SIZE > 32 * 1024) uint32_t gobble[4 * 1024] IRAM_ATTR; constexpr size_t gobble_sz = sizeof(gobble); @@ -36,7 +42,7 @@ uint32_t gobble[256] IRAM_ATTR; constexpr size_t gobble_sz = sizeof(gobble); #endif -bool isValid(uint32_t *probe) { +bool isValid(uint32_t *probe) { bool rc = true; if (NULL == probe) { ets_uart_printf("\nNULL memory pointer %p ...\n", probe); @@ -48,7 +54,8 @@ bool isValid(uint32_t *probe) { uint32_t saveData = *probe; for (size_t i = 0; i < 32; i++) { *probe = BIT(i); - asm volatile("" ::: "memory"); + asm volatile("" :: + : "memory"); uint32_t val = *probe; if (val != BIT(i)) { ets_uart_printf(" Read 0x%08X != Wrote 0x%08X\n", val, (uint32_t)BIT(i)); @@ -62,7 +69,7 @@ bool isValid(uint32_t *probe) { } -void dump_mem32(const void * addr, const size_t len) { +void dump_mem32(const void *addr, const size_t len) { uint32_t *addr32 = (uint32_t *)addr; ets_uart_printf("\n"); if ((uintptr_t)addr32 & 3) { @@ -71,9 +78,7 @@ void dump_mem32(const void * addr, const size_t len) { } for (size_t i = 0; i < len;) { ets_uart_printf("%p: ", &addr32[i]); - do { - ets_uart_printf(" 0x%08x", addr32[i]); - } while (i++, (i & 3) && (i < len)); + do { ets_uart_printf(" 0x%08x", addr32[i]); } while (i++, (i & 3) && (i < len)); ets_uart_printf("\n"); } ets_uart_printf("\n"); @@ -81,7 +86,7 @@ void dump_mem32(const void * addr, const size_t len) { extern "C" void _text_end(void); // extern void *_text_end; -void print_mmu_status(Print& oStream) { +void print_mmu_status(Print &oStream) { oStream.println(); oStream.printf_P(PSTR("MMU Configuration")); oStream.println(); @@ -102,7 +107,7 @@ void print_mmu_status(Print& oStream) { #ifdef MMU_IRAM_SIZE oStream.printf_P(PSTR(" IRAM Size: %u"), MMU_IRAM_SIZE); oStream.println(); - const uint32_t iram_free = MMU_IRAM_SIZE - (uint32_t)((uintptr_t)_text_end - 0x40100000UL); + const uint32_t iram_free = MMU_IRAM_SIZE - (uint32_t)((uintptr_t)_text_end - (uintptr_t)XCHAL_INSTRAM1_VADDR); oStream.printf_P(PSTR(" IRAM free: %u"), iram_free); oStream.println(); #endif @@ -131,7 +136,7 @@ void setup() { { HeapSelectIram ephemeral; // Serial.printf_P(PSTR("ESP.getFreeHeap(): %u\n"), ESP.getFreeHeap()); - gobble_sz = ESP.getFreeHeap() - UMM_OVERHEAD_ADJUST; // - 4096; + gobble_sz = ESP.getFreeHeap() - UMM_OVERHEAD_ADJUST; // - 4096; gobble = (uint32_t *)malloc(gobble_sz); } Serial.printf_P(PSTR("\r\nmalloc() from IRAM Heap:\r\n")); @@ -146,9 +151,7 @@ void setup() { #if (MMU_IRAM_SIZE > 0x8000) || defined(MMU_IRAM_HEAP) || defined(MMU_SEC_HEAP) if (isValid(gobble)) { // Put something in our new memory - for (size_t i = 0; i < (gobble_sz / 4); i++) { - gobble[i] = (uint32_t)&gobble[i]; - } + for (size_t i = 0; i < (gobble_sz / 4); i++) { gobble[i] = (uint32_t)&gobble[i]; } // Now is it there? dump_mem32(gobble, 32); @@ -164,12 +167,12 @@ void setup() { probe_b = (char *)gobble; probe_s = (short *)((uintptr_t)gobble); unaligned_probe_s = (short *)((uintptr_t)gobble + 1); - } -void processKey(Print& out, int hotKey) { +void processKey(Print &out, int hotKey) { switch (hotKey) { - case 't': { + case 't': + { uint32_t tmp; out.printf_P(PSTR("Test how much time is added by exception handling")); out.println(); @@ -221,7 +224,8 @@ void processKey(Print& out, int hotKey) { out.printf_P(PSTR("Read Byte from iRAM, 0x%02X at %p"), probe_b[0], probe_b); out.println(); break; - case 'B': { + case 'B': + { out.printf_P(PSTR("Load/Store exception by writing byte to iRAM")); out.println(); char val = 0x55; @@ -240,7 +244,8 @@ void processKey(Print& out, int hotKey) { out.printf_P(PSTR("Read short from iRAM, 0x%04X at %p"), probe_s[0], probe_s); out.println(); break; - case 'S': { + case 'S': + { out.printf_P(PSTR("Load/Store exception by writing short to iRAM")); out.println(); short int val = 0x0AA0; @@ -266,10 +271,8 @@ void processKey(Print& out, int hotKey) { out.printf_P(PSTR("This should not print %d"), divideA_B(1, 0)); out.println(); break; - case '\r': - out.println(); - case '\n': - break; + case '\r': out.println(); + case '\n': break; case '?': out.println(); out.println(F("Press a key + ")); 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 376086fb61..8a9ea0e82c 100644 --- a/libraries/esp8266/examples/NTP-TZ-DST/NTP-TZ-DST.ino +++ b/libraries/esp8266/examples/NTP-TZ-DST/NTP-TZ-DST.ino @@ -15,11 +15,11 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif // initial time (possibly given by an external RTC) -#define RTC_UTC_TEST 1510592825 // 1510592825 = Monday 13 November 2017 17:07:05 UTC +#define RTC_UTC_TEST 1510592825 // 1510592825 = Monday 13 November 2017 17:07:05 UTC // This database is autogenerated from IANA timezone database @@ -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) @@ -44,17 +44,17 @@ //////////////////////////////////////////////////////// #include -#include // settimeofday_cb() +#include // settimeofday_cb() #include #include -#include // time() ctime() -#include // struct timeval +#include // time() ctime() +#include // struct timeval -#include // sntp_servermode_dhcp() +#include // sntp_servermode_dhcp() // for testing purpose: -extern "C" int clock_gettime(clockid_t unused, struct timespec *tp); +extern "C" int clock_gettime(clockid_t unused, struct timespec* tp); //////////////////////////////////////////////////////// @@ -64,14 +64,14 @@ static time_t now; static uint32_t now_ms, now_us; static esp8266::polledTimeout::periodicMs showTimeNow(60000); -static int time_machine_days = 0; // 0 = present +static int time_machine_days = 0; // 0 = present static bool time_machine_running = false; static bool time_machine_run_once = false; // OPTIONAL: change SNTP startup delay // a weak function is already defined and returns 0 (RFC violation) // it can be redefined: -//uint32_t sntp_startup_delay_MS_rfc_not_less_than_60000 () +// uint32_t sntp_startup_delay_MS_rfc_not_less_than_60000 () //{ // //info_sntp_startup_delay_MS_rfc_not_less_than_60000_has_been_called = true; // return 60000; // 60s (or lwIP's original default: (random() % 5000)) @@ -80,7 +80,7 @@ static bool time_machine_run_once = false; // OPTIONAL: change SNTP update delay // a weak function is already defined and returns 1 hour // it can be redefined: -//uint32_t sntp_update_delay_MS_rfc_not_less_than_15000 () +// uint32_t sntp_update_delay_MS_rfc_not_less_than_15000 () //{ // //info_sntp_update_delay_MS_rfc_not_less_than_15000_has_been_called = true; // return 15000; // 15s @@ -92,9 +92,15 @@ static bool time_machine_run_once = false; void printTm(const char* what, const tm* tm) { Serial.print(what); - PTM(isdst); PTM(yday); PTM(wday); - PTM(year); PTM(mon); PTM(mday); - PTM(hour); PTM(min); PTM(sec); + PTM(isdst); + PTM(yday); + PTM(wday); + PTM(year); + PTM(mon); + PTM(mday); + PTM(hour); + PTM(min); + PTM(sec); } void showTime() { @@ -135,7 +141,7 @@ void showTime() { Serial.println((uint32_t)now); // timezone and demo in the future - Serial.printf("timezone: %s\n", getenv("TZ") ? : "(none)"); + Serial.printf("timezone: %s\n", getenv("TZ") ?: "(none)"); // human readable Serial.print("ctime: "); @@ -152,9 +158,7 @@ void showTime() { } else { Serial.printf("%s ", sntp.toString().c_str()); } - Serial.printf("- IPv6: %s - Reachability: %o\n", - sntp.isV6() ? "Yes" : "No", - sntp_getreachability(i)); + Serial.printf("- IPv6: %s - Reachability: %o\n", sntp.isV6() ? "Yes" : "No", sntp_getreachability(i)); } } @@ -168,12 +172,8 @@ void showTime() { while (true) { gettimeofday(&tv, nullptr); if (tv.tv_sec != prevtv.tv_sec) { - Serial.printf("time(): %u gettimeofday(): %u.%06u seconds are unchanged\n", - (uint32_t)prevtime, - (uint32_t)prevtv.tv_sec, (uint32_t)prevtv.tv_usec); - Serial.printf("time(): %u gettimeofday(): %u.%06u <-- seconds have changed\n", - (uint32_t)(prevtime = time(nullptr)), - (uint32_t)tv.tv_sec, (uint32_t)tv.tv_usec); + Serial.printf("time(): %u gettimeofday(): %u.%06u seconds are unchanged\n", (uint32_t)prevtime, (uint32_t)prevtv.tv_sec, (uint32_t)prevtv.tv_usec); + Serial.printf("time(): %u gettimeofday(): %u.%06u <-- seconds have changed\n", (uint32_t)(prevtime = time(nullptr)), (uint32_t)tv.tv_sec, (uint32_t)tv.tv_usec); break; } prevtv = tv; @@ -194,10 +194,8 @@ void time_is_set(bool from_sntp /* <= this parameter is optional */) { } else { time_machine_running = from_sntp && !time_machine_run_once; } - if (time_machine_running) { - Serial.printf("\n-- \n-- Starting time machine demo to show libc's " - "automatic DST handling\n-- \n"); - } + if (time_machine_running) { Serial.printf("\n-- \n-- Starting time machine demo to show libc's " + "automatic DST handling\n-- \n"); } } Serial.print("settimeofday("); @@ -212,9 +210,7 @@ void time_is_set(bool from_sntp /* <= this parameter is optional */) { if (time_machine_running) { now = time(nullptr); const tm* tm = localtime(&now); - Serial.printf(": future=%3ddays: DST=%s - ", - time_machine_days, - tm->tm_isdst ? "true " : "false"); + Serial.printf(": future=%3ddays: DST=%s - ", time_machine_days, tm->tm_isdst ? "true " : "false"); Serial.print(ctime(&now)); gettimeofday(&tv, nullptr); constexpr int days = 30; @@ -241,7 +237,7 @@ void setup() { // install callback - called when settimeofday is called (by SNTP or user) // once enabled (by DHCP), SNTP is updated every hour by default - // ** optional boolean in callback function is true when triggerred by SNTP ** + // ** optional boolean in callback function is true when triggered by SNTP ** settimeofday_cb(time_is_set); // setup RTC time @@ -251,7 +247,7 @@ void setup() { timeval tv = { rtc, 0 }; settimeofday(&tv, nullptr); - // NTP servers may be overriden by your DHCP server for a more local one + // NTP servers may be overridden by your DHCP server for a more local one // (see below) // ----> Here is the ONLY ONE LINE needed in your sketch @@ -261,10 +257,10 @@ void setup() { // Former configTime is still valid, here is the call for 7 hours to the west // with an enabled 30mn DST - //configTime(7 * 3600, 3600 / 2, "pool.ntp.org"); + // configTime(7 * 3600, 3600 / 2, "pool.ntp.org"); // OPTIONAL: disable obtaining SNTP servers from DHCP - //sntp_servermode_dhcp(0); // 0: disable obtaining SNTP servers from DHCP (enabled by default) + // sntp_servermode_dhcp(0); // 0: disable obtaining SNTP servers from DHCP (enabled by default) // Give now a chance to the settimeofday callback, // because it is *always* deferred to the next yield()/loop()-call. @@ -280,7 +276,5 @@ void setup() { } void loop() { - if (showTimeNow) { - showTime(); - } + if (showTimeNow) { showTime(); } } diff --git a/libraries/esp8266/examples/RTCUserMemory/RTCUserMemory.ino b/libraries/esp8266/examples/RTCUserMemory/RTCUserMemory.ino index 9736995c74..b1699d0cda 100644 --- a/libraries/esp8266/examples/RTCUserMemory/RTCUserMemory.ino +++ b/libraries/esp8266/examples/RTCUserMemory/RTCUserMemory.ino @@ -34,11 +34,11 @@ void setup() { delay(1000); // Read struct from RTC memory - if (ESP.rtcUserMemoryRead(0, (uint32_t*) &rtcData, sizeof(rtcData))) { + if (ESP.rtcUserMemoryRead(0, (uint32_t *)&rtcData, sizeof(rtcData))) { Serial.println("Read: "); printMemory(); Serial.println(); - uint32_t crcOfData = calculateCRC32((uint8_t*) &rtcData.data[0], sizeof(rtcData.data)); + uint32_t crcOfData = calculateCRC32((uint8_t *)&rtcData.data[0], sizeof(rtcData.data)); Serial.print("CRC32 of data: "); Serial.println(crcOfData, HEX); Serial.print("CRC32 read from RTC: "); @@ -51,13 +51,11 @@ void setup() { } // Generate new data set for the struct - for (size_t i = 0; i < sizeof(rtcData.data); i++) { - rtcData.data[i] = random(0, 128); - } + for (size_t i = 0; i < sizeof(rtcData.data); i++) { rtcData.data[i] = random(0, 128); } // Update CRC32 of data - rtcData.crc32 = calculateCRC32((uint8_t*) &rtcData.data[0], sizeof(rtcData.data)); + rtcData.crc32 = calculateCRC32((uint8_t *)&rtcData.data[0], sizeof(rtcData.data)); // Write struct to RTC memory - if (ESP.rtcUserMemoryWrite(0, (uint32_t*) &rtcData, sizeof(rtcData))) { + if (ESP.rtcUserMemoryWrite(0, (uint32_t *)&rtcData, sizeof(rtcData))) { Serial.println("Write: "); printMemory(); Serial.println(); @@ -67,8 +65,7 @@ void setup() { ESP.deepSleep(5e6); } -void loop() { -} +void loop() {} uint32_t calculateCRC32(const uint8_t *data, size_t length) { uint32_t crc = 0xffffffff; @@ -76,19 +73,15 @@ uint32_t calculateCRC32(const uint8_t *data, size_t length) { uint8_t c = *data++; for (uint32_t i = 0x80; i > 0; i >>= 1) { bool bit = crc & 0x80000000; - if (c & i) { - bit = !bit; - } + if (c & i) { bit = !bit; } crc <<= 1; - if (bit) { - crc ^= 0x04c11db7; - } + if (bit) { crc ^= 0x04c11db7; } } } return crc; } -//prints all rtcData, including the leading crc32 +// prints all rtcData, including the leading crc32 void printMemory() { char buf[3]; uint8_t *ptr = (uint8_t *)&rtcData; diff --git a/libraries/esp8266/examples/SerialDetectBaudrate/SerialDetectBaudrate.ino b/libraries/esp8266/examples/SerialDetectBaudrate/SerialDetectBaudrate.ino index 61967c691f..ec41562357 100644 --- a/libraries/esp8266/examples/SerialDetectBaudrate/SerialDetectBaudrate.ino +++ b/libraries/esp8266/examples/SerialDetectBaudrate/SerialDetectBaudrate.ino @@ -1,4 +1,4 @@ -#define TIMEOUT (10000UL) // Maximum time to wait for serial activity to start +#define TIMEOUT (10000UL) // Maximum time to wait for serial activity to start void setup() { // put your setup code here, to run once: @@ -13,9 +13,7 @@ void setup() { Serial.printf("\nDetected baudrate is %lu, switching to that baudrate now...\n", detectedBaudrate); // Wait for printf to finish - while (Serial.availableForWrite() != UART_TX_FIFO_SIZE) { - yield(); - } + while (Serial.availableForWrite() != UART_TX_FIFO_SIZE) { yield(); } // Clear Tx buffer to avoid extra characters being printed Serial.flush(); @@ -29,6 +27,4 @@ void setup() { void loop() { // put your main code here, to run repeatedly: - } - diff --git a/libraries/esp8266/examples/SerialStress/SerialStress.ino b/libraries/esp8266/examples/SerialStress/SerialStress.ino index 1412abd2be..f491ba04e5 100644 --- a/libraries/esp8266/examples/SerialStress/SerialStress.ino +++ b/libraries/esp8266/examples/SerialStress/SerialStress.ino @@ -1,28 +1,30 @@ /* 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 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() +#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() -#define FAKE_INCREASED_AVAILABLE 100 // test readBytes's timeout +#define FAKE_INCREASED_AVAILABLE 100 // test readBytes's timeout #define TIMEOUT 5000 -#define DEBUG(x...) //x +#define DEBUG(x...) // x -uint8_t buf [BUFFER_SIZE]; -uint8_t temp [BUFFER_SIZE]; +#define READING_PIN 4 +#define TIMEOUT_PIN 5 + +uint8_t buf[BUFFER_SIZE]; +uint8_t temp[BUFFER_SIZE]; bool reading = true; size_t testReadBytesTimeout = 0; @@ -37,70 +39,50 @@ static uint64_t timeout; Stream* logger; void error(const char* what) { - logger->printf("\nerror: %s after %ld minutes\nread idx: %d\nwrite idx: %d\ntotal: %ld\nlast read: %d\nmaxavail: %d\n", - what, (long)((millis() - start_ms) / 60000), in_idx, out_idx, (long)in_total, (int)local_receive_size, maxavail); - if (Serial.hasOverrun()) { - logger->printf("overrun!\n"); - } + logger->printf("\nerror: %s after %ld minutes\nread idx: %d\nwrite idx: %d\ntotal: %ld\nlast read: %d\nmaxavail: %d\n", what, (long)((millis() - start_ms) / 60000), in_idx, out_idx, (long)in_total, (int)local_receive_size, maxavail); + if (Serial.hasOverrun()) { logger->printf("overrun!\n"); } logger->printf("should be (size=%d idx=%d..%d):\n ", BUFFER_SIZE, in_idx, in_idx + local_receive_size - 1); - for (size_t i = in_idx; i < in_idx + local_receive_size; i++) { - logger->printf("%02x(%c) ", buf[i], (unsigned char)((buf[i] > 31 && buf[i] < 128) ? buf[i] : '.')); - } + for (size_t i = in_idx; i < in_idx + local_receive_size; i++) { logger->printf("%02x(%c) ", buf[i], (unsigned char)((buf[i] > 31 && buf[i] < 128) ? buf[i] : '.')); } logger->print("\n\nis: "); - for (size_t i = 0; i < local_receive_size; i++) { - logger->printf("%02x(%c) ", temp[i], (unsigned char)((temp[i] > 31 && temp[i] < 128) ? temp[i] : '.')); - } + for (size_t i = 0; i < local_receive_size; i++) { logger->printf("%02x(%c) ", temp[i], (unsigned char)((temp[i] > 31 && temp[i] < 128) ? temp[i] : '.')); } logger->println("\n\n"); - while (true) { - delay(1000); - } -} - -void preinit() { - // (no C++ in function) - // disable wifi - ESP8266WiFiClass::preinitWiFiOff(); + while (true) { delay(1000); } } void setup() { pinMode(LED_BUILTIN, OUTPUT); + pinMode(READING_PIN, INPUT); + pinMode(TIMEOUT_PIN, INPUT); + Serial.begin(BAUD); - Serial.swap(); // RX=GPIO13 TX=GPIO15 + 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()); - logger->printf("\n\nBAUD: %d - CoreRxBuffer: %d bytes - TestBuffer: %d bytes\n", - baud, SERIAL_SIZE_RX, BUFFER_SIZE); + logger->printf("\n\nBAUD: %d - CoreRxBuffer: %d bytes - TestBuffer: %d bytes\n", baud, SERIAL_SIZE_RX, BUFFER_SIZE); - size_for_1sec = baud / 10; // 8n1=10baudFor8bits + size_for_1sec = baud / 10; // 8n1=10baudFor8bits logger->printf("led changes state every %zd bytes (= 1 second)\n", size_for_1sec); logger->printf("press 's' to stop reading, not writing (induces overrun)\n"); logger->printf("press 't' to toggle timeout testing on readBytes\n"); // prepare send/compare buffer - for (size_t i = 0; i < sizeof buf; i++) { - buf[i] = (uint8_t)i; - } + for (size_t i = 0; i < sizeof buf; i++) { buf[i] = (uint8_t)i; } // bind RX and TX USC0(0) |= (1 << UCLBE); while (Serial.read() == -1); - if (Serial.hasOverrun()) { - logger->print("overrun?\n"); - } + if (Serial.hasOverrun()) { logger->print("overrun?\n"); } timeout = (start_ms = last_ms = millis()) + TIMEOUT; logger->println("setup done"); @@ -109,59 +91,35 @@ void setup() { void loop() { size_t maxlen = Serial.availableForWrite(); // check remaining space in buffer - if (maxlen > BUFFER_SIZE - out_idx) { - maxlen = BUFFER_SIZE - out_idx; - } + if (maxlen > BUFFER_SIZE - out_idx) { maxlen = BUFFER_SIZE - out_idx; } // check if not cycling more than buffer size relatively to input - size_t in_out = out_idx == in_idx ? - BUFFER_SIZE : - (in_idx + BUFFER_SIZE - out_idx - 1) % BUFFER_SIZE; - if (maxlen > in_out) { - maxlen = in_out; - } + size_t in_out = out_idx == in_idx ? BUFFER_SIZE : (in_idx + BUFFER_SIZE - out_idx - 1) % BUFFER_SIZE; + if (maxlen > in_out) { maxlen = in_out; } DEBUG(logger->printf("(aw%i/w%i", Serial.availableForWrite(), maxlen)); size_t local_written_size = Serial.write(buf + out_idx, maxlen); DEBUG(logger->printf(":w%i/aw%i/ar%i)\n", local_written_size, Serial.availableForWrite(), Serial.available())); - if (local_written_size > maxlen) { - error("bad write"); - } - if ((out_idx += local_written_size) == BUFFER_SIZE) { - out_idx = 0; - } - delay(0); + if (local_written_size > maxlen) { error("bad write"); } + if ((out_idx += local_written_size) == BUFFER_SIZE) { out_idx = 0; } + yield(); DEBUG(logger->printf("----------\n")); - if (Serial.hasOverrun()) { - logger->printf("rx overrun!\n"); - } - if (Serial.hasRxError()) { - logger->printf("rx error!\n"); - } + if (Serial.hasOverrun()) { logger->printf("rx overrun!\n"); } + if (Serial.hasRxError()) { logger->printf("rx error!\n"); } if (reading) { // receive data maxlen = Serial.available() + testReadBytesTimeout; - if (maxlen > maxavail) { - maxavail = maxlen; - } + if (maxlen > maxavail) { maxavail = maxlen; } // check space in temp receive buffer - if (maxlen > BUFFER_SIZE - in_idx) { - maxlen = BUFFER_SIZE - in_idx; - } + if (maxlen > BUFFER_SIZE - in_idx) { maxlen = BUFFER_SIZE - in_idx; } DEBUG(logger->printf("(ar%i/r%i", Serial.available(), maxlen)); local_receive_size = Serial.readBytes(temp, maxlen); DEBUG(logger->printf(":r%i/ar%i)\n", local_receive_size, Serial.available())); - if (local_receive_size > maxlen) { - error("bad read"); - } + if (local_receive_size > maxlen) { error("bad read"); } if (local_receive_size) { - if (memcmp(buf + in_idx, temp, local_receive_size) != 0) { - error("fail"); - } - if ((in_idx += local_receive_size) == BUFFER_SIZE) { - in_idx = 0; - } + if (memcmp(buf + in_idx, temp, local_receive_size) != 0) { error("fail"); } + if ((in_idx += local_receive_size) == BUFFER_SIZE) { in_idx = 0; } in_total += local_receive_size; } DEBUG(logger->printf("r(%d) ok\n", local_receive_size)); @@ -172,29 +130,24 @@ void loop() { digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); size_for_led = 0; - if (in_prev == in_total) { - error("receiving nothing?\n"); - } + if (in_prev == in_total) { error("receiving nothing?\n"); } unsigned long now_ms = millis(); int bwkbps_avg = ((((uint64_t)in_total) * 8000) / (now_ms - start_ms)) >> 10; - int bwkbps_now = (((in_total - in_prev) * 8000) / (now_ms - last_ms)) >> 10 ; + int bwkbps_now = (((in_total - in_prev) * 8000) / (now_ms - last_ms)) >> 10; logger->printf("bwavg=%d bwnow=%d kbps maxavail=%i\n", bwkbps_avg, bwkbps_now, maxavail); in_prev = in_total; 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/SigmaDeltaDemo/SigmaDeltaDemo.ino b/libraries/esp8266/examples/SigmaDeltaDemo/SigmaDeltaDemo.ino index bdc1799eb7..d1592e7588 100644 --- a/libraries/esp8266/examples/SigmaDeltaDemo/SigmaDeltaDemo.ino +++ b/libraries/esp8266/examples/SigmaDeltaDemo/SigmaDeltaDemo.ino @@ -5,16 +5,15 @@ void setup() { Serial.begin(115200); - pinMode(LED_BUILTIN, OUTPUT); // blinkie & sigma-delta mix + pinMode(LED_BUILTIN, OUTPUT); // blinkie & sigma-delta mix uint32_t reqFreq = 1000; uint32_t realFreq; - realFreq = sigmaDeltaSetup(0, reqFreq); // chose a low frequency + realFreq = sigmaDeltaSetup(0, reqFreq); // chose a low frequency Serial.println(); Serial.println("Start Sigma Delta Example\n"); Serial.printf("Frequency = %u\n", realFreq); - } void loop() { @@ -36,7 +35,6 @@ void loop() { sigmaDeltaWrite(0, duty); delay(10); } - } Serial.println("Detaching builtin led & playing a blinkie\n"); diff --git a/libraries/esp8266/examples/StreamString/StreamString.ino b/libraries/esp8266/examples/StreamString/StreamString.ino new file mode 100644 index 0000000000..73512cf2af --- /dev/null +++ b/libraries/esp8266/examples/StreamString/StreamString.ino @@ -0,0 +1,199 @@ + +// this example sketch in the public domain is also a host and device test + +#include +#include + +void loop() { + delay(1000); +} + +void checksketch(const char* what, const char* res1, const char* res2) { + if (strcmp(res1, res2) == 0) { + Serial << "PASSED: Test " << what << " (result: '" << res1 << "')\n"; + } else { + Serial << "FAILED: Test " << what << ": '" << res1 << "' <> '" << res2 << "' !\n"; + } +} + +#ifndef check +#define check(what, res1, res2) checksketch(what, res1, res2) +#endif + +void testStringPtrProgmem() { + static const char inProgmem[] PROGMEM = "I am in progmem"; + auto inProgmem2 = F("I am too in progmem"); + + int heap = (int)ESP.getFreeHeap(); + auto stream1 = StreamConstPtr(inProgmem, sizeof(inProgmem) - 1); + auto stream2 = StreamConstPtr(inProgmem2); + Serial << stream1 << " - " << stream2 << "\n"; + heap -= (int)ESP.getFreeHeap(); + check("NO heap occupation while streaming progmem strings", String(heap).c_str(), "0"); +} + +void testStreamString() { + String inputString = "hello"; + StreamString result; + + // By default, reading a S2Stream(String) or a StreamString will consume the String. + // It can be disabled by calling ::resetPointer(), (not default) + // and re-enabled by calling ::setConsume(). (default) + // + // In default consume mode, reading a byte or a block will remove it from + // the String. Operations are O(n²). + // + // In non-default non-consume mode, it will just move a pointer. That one + // can be ::resetPointer(pos) anytime. See the example below. + + + // The String included in 'result' will not be modified by read: + // (this is not the default) + result.resetPointer(); + + { + // 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 read-only source to be sent to + // 'result'. + + result.clear(); + StreamConstPtr(inputString).sendAll(result); + StreamConstPtr(inputString).sendAll(result); + StreamConstPtr(inputString).sendAll(result); + check("StreamConstPtr.sendAll(StreamString)", result.c_str(), "hellohellohello"); + } + + { + // equivalent of the above + + result.clear(); + result << inputString; + result << inputString << inputString; + check("StreamString<event]); ehConsolePort.print(" ("); switch (event->event) { - case EVENT_STAMODE_CONNECTED: - break; - case EVENT_STAMODE_DISCONNECTED: - break; - case EVENT_STAMODE_AUTHMODE_CHANGE: - break; - case EVENT_STAMODE_GOT_IP: - break; + case EVENT_STAMODE_CONNECTED: break; + case EVENT_STAMODE_DISCONNECTED: break; + case EVENT_STAMODE_AUTHMODE_CHANGE: break; + case EVENT_STAMODE_GOT_IP: break; case EVENT_SOFTAPMODE_STACONNECTED: - case EVENT_SOFTAPMODE_STADISCONNECTED: { - char mac[32] = {0}; + case EVENT_SOFTAPMODE_STADISCONNECTED: + { + char mac[32] = { 0 }; snprintf(mac, 32, MACSTR ", aid: %d", MAC2STR(event->event_info.sta_connected.mac), event->event_info.sta_connected.aid); ehConsolePort.print(mac); @@ -139,16 +92,16 @@ void wifi_event_handler_cb(System_Event_t * event) { ehConsolePort.println(")"); } -void print_softap_config(Stream & consolePort, softap_config const& config) { +void print_softap_config(Stream& consolePort, softap_config const& config) { consolePort.println(); consolePort.println(F("SoftAP Configuration")); consolePort.println(F("--------------------")); consolePort.print(F("ssid: ")); - consolePort.println((char *) config.ssid); + consolePort.println((char*)config.ssid); consolePort.print(F("password: ")); - consolePort.println((char *) config.password); + consolePort.println((char*)config.password); consolePort.print(F("ssid_len: ")); consolePort.println(config.ssid_len); @@ -173,8 +126,8 @@ void print_softap_config(Stream & consolePort, softap_config const& config) { consolePort.println(); } -void print_system_info(Stream & consolePort) { - const rst_info * resetInfo = system_get_rst_info(); +void print_system_info(Stream& consolePort) { + const rst_info* resetInfo = system_get_rst_info(); consolePort.print(F("system_get_rst_info() reset reason: ")); consolePort.println(RST_REASONS[resetInfo->reason]); @@ -211,7 +164,7 @@ void print_system_info(Stream & consolePort) { consolePort.println(FLASH_SIZE_MAP_NAMES[system_get_flash_size_map()]); } -void print_wifi_general(Stream & consolePort) { +void print_wifi_general(Stream& consolePort) { consolePort.print(F("wifi_get_channel(): ")); consolePort.println(wifi_get_channel()); @@ -219,8 +172,8 @@ void print_wifi_general(Stream & consolePort) { consolePort.println(PHY_MODE_NAMES[wifi_get_phy_mode()]); } -void secure_softap_config(softap_config * config, const char * ssid, const char * password) { - size_t ssidLen = strlen(ssid) < sizeof(config->ssid) ? strlen(ssid) : sizeof(config->ssid); +void secure_softap_config(softap_config* config, const char* ssid, const char* password) { + size_t ssidLen = strlen(ssid) < sizeof(config->ssid) ? strlen(ssid) : sizeof(config->ssid); size_t passwordLen = strlen(password) < sizeof(config->password) ? strlen(password) : sizeof(config->password); memset(config->ssid, 0, sizeof(config->ssid)); @@ -230,7 +183,7 @@ void secure_softap_config(softap_config * config, const char * ssid, const char memcpy(config->password, password, passwordLen); config->ssid_len = ssidLen; - config->channel = 1; + config->channel = 1; config->authmode = AUTH_WPA2_PSK; // config->ssid_hidden = 1; config->max_connection = 4; @@ -293,4 +246,3 @@ void loop() { Serial.println(system_get_time()); delay(1000); } - diff --git a/libraries/esp8266/examples/UartDownload/UartDownload.ino b/libraries/esp8266/examples/UartDownload/UartDownload.ino new file mode 100644 index 0000000000..30cacb1127 --- /dev/null +++ b/libraries/esp8266/examples/UartDownload/UartDownload.ino @@ -0,0 +1,177 @@ +/* + + Example of Booting into UART Download using `ESP.rebootIntoUartDownloadMode()` + + Two methods are presented for starting UART Boot Mode. + 1) From `loop()` call the function `proxyEspSync()`, which peeks for a SLIP + frame marker. Then when present, look for an esptool ESP_SYNC packet on + the Serial port. + 2) A simple hotkey of 'D'. + + After either of these, `ESP.rebootIntoUartDownloadMode()` is called to place + the ESP8266 into UART Flash program mode. + + For a quick test to confirm the ESP8266 is responding to esptool.py, + use this command: + esptool.py --chip esp8266 --before no_reset --after soft_reset flash_id + + + Note with these methods a hard reset is not done, and the esptool.py may not + detect and report the correct Crystal frequency for the ESP Module. If you + need that info, it needs to be gathered after a Power-On or Hard Reset. +*/ + +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// +// Check Serial Receive for ESP_SYNC slip packet from esptool.py +// +// If you are already using Serial input for command input, the character '\xC0' +// is not available. We must reserve its use for the SLIP Frame Marker. I am not +// sure which languages if any, would pose a problem. For the non-English +// languages check your character set values to be sure it is not an issue. If +// it is an issue, you will not be able to use this method as presented. The +// '\xC0' character is defined by the SLIP protocol and cannot be changed. + +// If your needs require it, you can add logic to loop() for setting and +// clearing uartDownloadEnable. For example, you could add a push button to a +// GPIO pin and monitor for a 5-second press. Then, set uartDownloadEnable to +// true. In addition to that, you could also define a time-to-live for that +// state and clear it after it elapses. +// +// Change this to false if you do not want ESP_SYNC monitor always on. +bool uartDownloadEnable = true; + +// Buffer size to receive an ESP_SYNC packet into, larger than the expected +// ESP_SYNC packet length. +constexpr size_t pktBufSz = 64; + +// Enough time to receive 115 bytes at 115200bps. +// More than enough to finish receiving an ESP_SYNC packet. +constexpr size_t kSyncTimeoutMs = 10; + +// The SLIP Frame end character, which is also used to start a frame. +constexpr char slipFrameMarker = '\xC0'; + +// General packet format: +// <0xC0><32 bit cksum><0xC0> +// Slip packet for ESP_SYNC, minus the frame markers ('\xC0') captured from +// esptool using the `--trace` option. +const char syncPkt[] PROGMEM = "\x00\x08\x24\x00\x00\x00\x00\x00\x07\x07\x12\x20" + "UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU"; + +constexpr size_t syncPktSz = sizeof(syncPkt) - 1; // Don't compare zero terminator char + +// +// Use the discovery of an ESP_SYNC packet, to trigger calling UART Download +// Mode. At entry we expect the Serial FIFO to start with the byte following +// the slipFrameMarker. +// +void proxyEspSync() { + if (!uartDownloadEnable) { return; } + + byte buf[pktBufSz]; + + // If it is an ESP_SYNC packet, it will not take long for readBytesUntil() to + // complete. + Serial.setTimeout(kSyncTimeoutMs); + int len = Serial.readBytesUntil(slipFrameMarker, buf, pktBufSz); + + // To avoid a false trigger, only start UART Download Mode when we get an + // exact match to the captured esptool ESP_SYNC packet. + if (syncPktSz == len && 0 == memcmp_P(buf, syncPkt, len)) { + ESP.rebootIntoUartDownloadMode(); + // Does not return + } + + // Assume RX FIFO data is garbled and flush all RX data. + while (0 <= Serial.read()) {} // Clear FIFO + + // If your Serial requirements need a specific timeout value, you would + // restore those here. +} +// +//////////////////////////////////////////////////////////////////////////////// + +void setup() { + // For `proxyEspSync()` to work, the Serial.begin() speed needs to be + // 115200bps. This is the data rate used by esptool.py. It expects the Boot + // ROM to use its "auto-baud" feature to match up. Since `proxyEspSync()` is + // acting as a proxy we must use 115200. + // + // If on the Arduino IDE Tools menu you use "Upload Speeds" above 115200, it + // will work. When esptool.py is run with the `--baud BAUD` option specified + // above 115200, initial communication with the ESP8266 is done at 115200bps. + // Once esptool.py has synchronize with the ESP8266 and downloaded a short + // stub, then both devices shift their UART speeds to the command line value. + Serial.begin(115200); + + Serial.println(F("\r\n\r\n" + "Boot UART Download Demo - initialization started.\r\n" + "\r\n" + "For a quick test to see the UART Download work,\r\n" + "stop your serial terminal APP and run:\r\n" + " esptool.py --chip esp8266 --before no_reset --after soft_reset flash_id\r\n")); + + // ... +} + +void cmdLoop(Print& oStream, int key) { + switch (key) { + case 'e': + oStream.println(F("Enable monitor for detecting ESP_SYNC from esptool.py")); + uartDownloadEnable = true; + break; + + case 'D': + // This option would be prone to false triggering. It is here for DEMO + // purposes and debugging. + oStream.println(F("Boot into UART download mode ...")); + oStream.flush(); + ESP.rebootIntoUartDownloadMode(); + break; + + case 'R': + oStream.println(F("Restart ...")); + oStream.flush(); + ESP.restart(); + break; + + // ... + + case '?': + oStream.println(F("\r\nHot key help:")); + if (!uartDownloadEnable) { oStream.println(F(" e - Enable monitor for detecting ESP_SYNC from esptool.py")); } + oStream.println(F(" D - Boot into UART download mode")); + oStream.println(F(" R - Restart")); + oStream.println(F(" ? - This help message\r\n")); + break; + + default: break; + } + + oStream.println(); +} + + +void loop() { + + // In this example, we can have Serial data from a user keystroke for our + // command loop or the esptool trying to SYNC up for flashing. If the + // character matches the Slip Frame Marker (the 1st byte of the SYNC packet), + // we intercept it and call our ESP_SYNC proxy to complete the verification + // and reboot into the UART Downloader. Otherwise, process the keystroke as + // normal. + if (0 < Serial.available()) { + int keyPress = Serial.read(); + if (slipFrameMarker == keyPress) { + proxyEspSync(); + } else { + cmdLoop(Serial, keyPress); + } + } + + // ... +} diff --git a/libraries/esp8266/examples/WiFiShutdown/WiFiShutdown.ino b/libraries/esp8266/examples/WiFiShutdown/WiFiShutdown.ino deleted file mode 100644 index 1c0624fd60..0000000000 --- a/libraries/esp8266/examples/WiFiShutdown/WiFiShutdown.ino +++ /dev/null @@ -1,258 +0,0 @@ - -// demonstrate the use of WiFi.mode(SHUTDOWN/RESUME) -// released to public domain - -// current on wemos d1 mini (including: ldo, usbserial chip): -// ~85mA during normal operations -// ~30mA during wifi shutdown -// ~5mA during deepsleep - -#ifndef STASSID -#define STASSID "mynetwork" -#define STAPSK "mynetworkpasswd" -#endif - -#define WAIT_NTP 0 // define this to 1 for NTP check too - -#include -#include // crc32() -#include // WiFiState structure details - -enum state_e { - e_initial, - e_start_resume, - e_start_normal, - e_off_restart, - e_wait_connected, - e_wait_ntp, - e_shutdown, - e_wait_shutdown, - e_wait_off -}; - -static state_e step = e_initial; // step -static int wifi_timeout = 0; // wifi timeout counter -static bool time_is_set = false; // WAIT_NTP=1: wait for network - dhcp packet must have ntp server - -// non volatile data -struct nv_s { - WiFiState wss; // core's wifi save state - - uint32_t crc; - struct { - int rstcounter[7]; - } data; -}; -static nv_s* nv = (nv_s*)RTC_USER_MEM; // user non volatile area - -#define SEP "###### " -#define EV "!!!!!! " -#define NFO "------ " - -void resetUserCrc() { - nv->crc = crc32(&nv->data, sizeof(nv->data)); -} - -void printNv() { - Serial.printf(NFO "nfo1/2 wifi-nv-state: valid=%d, " - "persistent=%d, " - "mode=%d, " - "channel=%d, " - "ip=%s, " - "dns=%s, " - "ntp=%s\n", - WiFi.shutdownValidCRC(&nv->wss), - nv->wss.state.persistent, - nv->wss.state.mode, - nv->wss.state.channel, - IPAddress(&nv->wss.state.ip.ip).toString().c_str(), - IPAddress(&nv->wss.state.dns[0]).toString().c_str(), - IPAddress(&nv->wss.state.ntp[0]).toString().c_str()); - - Serial.printf(NFO "nfo2/2 rst reason counters: default:%d wdt:%d exception:%d softwdt:%d reset:%d deepsleep:%d extsys:%d\n", - nv->data.rstcounter[0], - nv->data.rstcounter[1], - nv->data.rstcounter[2], - nv->data.rstcounter[3], - nv->data.rstcounter[4], - nv->data.rstcounter[5], - nv->data.rstcounter[6]); -} - -void timeset_cb() { - time_is_set = true; - - static bool first = true; - if (first) { - first = false; - } -} - -decltype(millis()) startup; - - -WiFiEventHandler evOff = WiFi.onWiFiModeChange([](const WiFiEventModeChange& event) { - Serial.printf(EV "mode changed event: ev:%d->%d getMode=%d\n", event.oldMode, event.newMode, wifi_get_opmode()); -}); - -void preinit() { - ESP8266WiFiClass::preinitWiFiOff(); -} - -void setup() { - WiFi.persistent(false); - startup = millis(); - Serial.begin(115200); - settimeofday_cb(timeset_cb); - - // prepare non volatile user structure - if (crc32(&nv->data, sizeof(nv->data)) != nv->crc) { - memset(&nv->data, 0, sizeof(nv->data)); - Serial.printf(SEP "reset NV user data\n"); - } - // update reset reason - nv->data.rstcounter[system_get_rst_info()->reason]++; - // recalculate crc - resetUserCrc(); - // nfo - printNv(); - - Serial.println("setup()"); -} - -#define TEST(x...) ({ auto v = x; Serial.printf(SEP "'%s': result = %d\n", #x, v); v; }) - -void loop() { - - static int prev = 255; - if (step != prev) { - prev = step; - Serial.printf(NFO "step %d - wifi getMode=%d=%d heap=%d freeheap=%d\n", - prev, - WiFi.getMode(), - wifi_get_opmode(), - ESP.getFreeHeap(), - ESP.getFreeHeap()); - printNv(); - } - - switch (step) { - - case e_initial: { - if (WiFi.shutdownValidCRC(&nv->wss)) { - step = e_start_resume; - } else { - step = e_start_normal; - } - break; - } - - - case e_start_resume: - Serial.println(SEP "CRC valid => WIFI_RESUME"); - startup = millis(); - - if (!TEST(WiFi.mode(WIFI_RESUME, &nv->wss))) { - Serial.printf(SEP "issue resuming WiFi\n"); - step = e_off_restart; - } else { - Serial.printf(SEP "waiting for connected\\n"); - step = e_wait_connected; - } - break; - - - case e_start_normal: - Serial.printf(SEP "CRC NOT valid, begin/WIFI_STA (current mode = %d)\n", wifi_get_opmode()); - startup = millis(); - if (!TEST(WiFi.mode(WIFI_STA)) || !TEST(WiFi.begin(STASSID, STAPSK))) { - Serial.printf(SEP "issue setting up STA\n"); - step = e_off_restart; - } else { - Serial.printf(SEP "waiting for connected\n"); - step = e_wait_connected; - } - break; - - - case e_wait_connected: - if (WiFi.status() == WL_CONNECTED) { - Serial.printf(SEP "connected! ---- startup time: %ld ms ----\n\n\n", millis() - startup); - wifi_timeout = 0; - if (WAIT_NTP) { - step = e_wait_ntp; - Serial.printf(SEP "wait for NTP\n"); - } else { - step = e_shutdown; - } - } else if ((millis() - startup > 10000)) { - Serial.printf(SEP "connected TIMEOUT! status=%d\n", WiFi.status()); - wifi_timeout++; - step = e_off_restart; - } - break; - - - case e_off_restart: - Serial.printf(SEP "OFF -> wait 2s\n"); - (void)TEST(WiFi.mode(WIFI_OFF)); - delay(2000); // test - mad wifi loop until :oom if delay not there - to verify - step = e_initial; - break; - - - case e_wait_ntp: - // check when NTP has set time - if (time_is_set) { - Serial.printf(SEP "NTP is set\n"); - time_is_set = false; - step = e_shutdown; - } - break; - - - case e_shutdown: { - static int deepsleep = 0; - switch (++deepsleep) { - case 1: { - Serial.println(SEP "WIFI_OFF for 5s"); - TEST(WiFi.mode(WIFI_OFF)); - step = e_wait_off; - break; - } - case 2: // several loop on shutdown - case 3: // to check if it affects - case 4: { // reconnection duration - Serial.println(SEP "WIFI_SHUTDOWN for 5s"); - TEST(WiFi.mode(WIFI_SHUTDOWN, &nv->wss)); - step = e_wait_shutdown; - break; - } - default: { - Serial.println(SEP "DEEPSLEEP for 5s (bind GPIO16 <=> RST)"); - TEST(WiFi.mode(WIFI_SHUTDOWN, &nv->wss)); - Serial.flush(); - ESP.deepSleep(5000000); - // will reboot, GPIO16 must be connected to reset - } - } - - startup = millis(); - break; - } - - - case e_wait_shutdown: - if (millis() - startup > 5000) { - step = e_start_resume; - } - break; - - - case e_wait_off: - if (millis() - startup > 5000) { - step = e_start_normal; - } - break; - } -} diff --git a/libraries/esp8266/examples/interactive/interactive.ino b/libraries/esp8266/examples/interactive/interactive.ino index b3bd1ff931..0f0c56e31c 100644 --- a/libraries/esp8266/examples/interactive/interactive.ino +++ b/libraries/esp8266/examples/interactive/interactive.ino @@ -12,11 +12,11 @@ #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif -const char * SSID = STASSID; -const char * PSK = STAPSK; +const char* SSID = STASSID; +const char* PSK = STAPSK; IPAddress staticip(192, 168, 1, 123); IPAddress gateway(192, 168, 1, 254); @@ -35,16 +35,14 @@ void setup() { } Serial.println(); Serial.println(WiFi.localIP()); - Serial.print( - "WL_IDLE_STATUS = 0\n" - "WL_NO_SSID_AVAIL = 1\n" - "WL_SCAN_COMPLETED = 2\n" - "WL_CONNECTED = 3\n" - "WL_CONNECT_FAILED = 4\n" - "WL_CONNECTION_LOST = 5\n" - "WL_WRONG_PASSWORD = 6\n" - "WL_DISCONNECTED = 7\n" - ); + Serial.print("WL_IDLE_STATUS = 0\n" + "WL_NO_SSID_AVAIL = 1\n" + "WL_SCAN_COMPLETED = 2\n" + "WL_CONNECTED = 3\n" + "WL_CONNECT_FAILED = 4\n" + "WL_CONNECTION_LOST = 5\n" + "WL_WRONG_PASSWORD = 6\n" + "WL_DISCONNECTED = 7\n"); } void WiFiOn() { @@ -65,9 +63,16 @@ void WiFiOff() { void loop() { #define TEST(name, var, varinit, func) \ static decltype(func) var = (varinit); \ - if ((var) != (func)) { var = (func); Serial.printf("**** %s: ", name); Serial.println(var); } + if ((var) != (func)) { \ + var = (func); \ + Serial.printf("**** %s: ", name); \ + Serial.println(var); \ + } -#define DO(x...) Serial.println(F( #x )); x; break +#define DO(x...) \ + Serial.println(F(#x)); \ + x; \ + break TEST("Free Heap", freeHeap, 0, ESP.getFreeHeap()); TEST("WiFiStatus", status, WL_IDLE_STATUS, WiFi.status()); @@ -91,7 +96,7 @@ void loop() { case 'n': DO(WiFi.setSleepMode(WIFI_NONE_SLEEP)); case 'l': DO(WiFi.setSleepMode(WIFI_LIGHT_SLEEP)); case 'm': DO(WiFi.setSleepMode(WIFI_MODEM_SLEEP)); - case 'S': DO(WiFi.config(staticip, gateway, subnet)); // use static address - case 's': DO(WiFi.config(0u, 0u, 0u)); // back to dhcp client + case 'S': DO(WiFi.config(staticip, gateway, subnet)); // use static address + case 's': DO(WiFi.config(0u, 0u, 0u)); // back to dhcp client } } diff --git a/libraries/esp8266/examples/irammem/irammem.ino b/libraries/esp8266/examples/irammem/irammem.ino index 57b1b31b44..e8005d7687 100644 --- a/libraries/esp8266/examples/irammem/irammem.ino +++ b/libraries/esp8266/examples/irammem/irammem.ino @@ -14,12 +14,188 @@ #define ETS_PRINTF ets_uart_printf #endif +/* + Verify mmu_get_uint16()'s compliance with strict-aliasing rules under + different optimizations. +*/ + +#pragma GCC push_options +// reference +#pragma GCC optimize("O0") // We expect -O0 to generate the correct results +__attribute__((noinline)) void aliasTestReference(uint16_t *x) { + // Without adhearance to strict-aliasing, this sequence of code would fail + // when optimized by GCC Version 10.3 + size_t len = 3; + for (size_t u = 0; u < len; u++) { + uint16_t x1 = mmu_get_uint16(&x[0]); + for (size_t v = 0; v < len; v++) { x[v] = mmu_get_uint16(&x[v]) + x1; } + } +} +// Tests +#pragma GCC optimize("Os") +__attribute__((noinline)) void aliasTestOs(uint16_t *x) { + size_t len = 3; + for (size_t u = 0; u < len; u++) { + uint16_t x1 = mmu_get_uint16(&x[0]); + for (size_t v = 0; v < len; v++) { x[v] = mmu_get_uint16(&x[v]) + x1; } + } +} +#pragma GCC optimize("O2") +__attribute__((noinline)) void aliasTestO2(uint16_t *x) { + size_t len = 3; + for (size_t u = 0; u < len; u++) { + uint16_t x1 = mmu_get_uint16(&x[0]); + for (size_t v = 0; v < len; v++) { x[v] = mmu_get_uint16(&x[v]) + x1; } + } +} +#pragma GCC optimize("O3") +__attribute__((noinline)) void aliasTestO3(uint16_t *x) { + size_t len = 3; + for (size_t u = 0; u < len; u++) { + uint16_t x1 = mmu_get_uint16(&x[0]); + for (size_t v = 0; v < len; v++) { x[v] = mmu_get_uint16(&x[v]) + x1; } + } +} + +// Evaluate if optomizer may have changed 32-bit access to 8-bit. +// 8-bit access will take longer as it will be processed thought +// the exception handler. For this case the -O0 version will appear faster. +#pragma GCC optimize("O0") +__attribute__((noinline)) IRAM_ATTR uint32_t timedRead_Reference(uint8_t *res) { + // This test case was verified with GCC 10.3 + // There is a code case that can result in 32-bit wide IRAM load from memory + // being optimized down to an 8-bit memory access. In this test case we need + // to supply a constant IRAM address that is not 0 when anded with 3u. + // This section verifies that the workaround implimented by the inline + // function mmu_get_uint8() is preventing this. See comments for function + // mmu_get_uint8(() in mmu_iram.h for more details. + const uint8_t *x = (const uint8_t *)0x40100003ul; + uint32_t b = ESP.getCycleCount(); + *res = mmu_get_uint8(x); + return ESP.getCycleCount() - b; +} +#pragma GCC optimize("Os") +__attribute__((noinline)) IRAM_ATTR uint32_t timedRead_Os(uint8_t *res) { + const uint8_t *x = (const uint8_t *)0x40100003ul; + uint32_t b = ESP.getCycleCount(); + *res = mmu_get_uint8(x); + return ESP.getCycleCount() - b; +} +#pragma GCC optimize("O2") +__attribute__((noinline)) IRAM_ATTR uint32_t timedRead_O2(uint8_t *res) { + const uint8_t *x = (const uint8_t *)0x40100003ul; + uint32_t b = ESP.getCycleCount(); + *res = mmu_get_uint8(x); + return ESP.getCycleCount() - b; +} +#pragma GCC optimize("O3") +__attribute__((noinline)) IRAM_ATTR uint32_t timedRead_O3(uint8_t *res) { + const uint8_t *x = (const uint8_t *)0x40100003ul; + uint32_t b = ESP.getCycleCount(); + *res = mmu_get_uint8(x); + return ESP.getCycleCount() - b; +} +#pragma GCC pop_options + +bool test4_32bit_loads() { + bool result = true; + uint8_t res; + uint32_t cycle_count_ref, cycle_count; + Serial.printf("\r\nFor mmu_get_uint8, verify that 32-bit wide IRAM access is preserved across different optimizations:\r\n"); + cycle_count_ref = timedRead_Reference(&res); + /* + If the optimizer (for options -Os, -O2, and -O3) replaces the 32-bit wide + IRAM access with an 8-bit, the exception handler will get invoked on memory + reads. The total execution time will show a significant increase when + compared to the reference (option -O0). + */ + Serial.printf(" Option -O0, cycle count %5u - reference\r\n", cycle_count_ref); + cycle_count = timedRead_Os(&res); + Serial.printf(" Option -Os, cycle count %5u ", cycle_count); + if (cycle_count_ref > cycle_count) { + Serial.printf("- passed\r\n"); + } else { + result = false; + Serial.printf("- failed\r\n"); + } + cycle_count = timedRead_O2(&res); + Serial.printf(" Option -O2, cycle count %5u ", cycle_count); + if (cycle_count_ref > cycle_count) { + Serial.printf("- passed\r\n"); + } else { + result = false; + Serial.printf("- failed\r\n"); + } + cycle_count = timedRead_O3(&res); + Serial.printf(" Option -O3, cycle count %5u ", cycle_count); + if (cycle_count_ref > cycle_count) { + Serial.printf("- passed\r\n"); + } else { + result = false; + Serial.printf("- failed\r\n"); + } + return result; +} + +void printPunFail(uint16_t *ref, uint16_t *x, size_t sz) { + Serial.printf(" Expected:"); + for (size_t i = 0; i < sz; i++) { Serial.printf(" %3u", ref[i]); } + Serial.printf("\r\n Got: "); + for (size_t i = 0; i < sz; i++) { Serial.printf(" %3u", x[i]); } + Serial.printf("\r\n"); +} + +bool testPunning() { + bool result = true; + // Get reference result for verifing test + alignas(uint32_t) uint16_t x_ref[] = { 1, 2, 3, 0 }; + aliasTestReference(x_ref); // -O0 + Serial.printf("mmu_get_uint16() strict-aliasing tests with different optimizations:\r\n"); + + { + alignas(alignof(uint32_t)) uint16_t x[] = { 1, 2, 3, 0 }; + aliasTestOs(x); + Serial.printf(" Option -Os "); + if (0 == memcmp(x_ref, x, sizeof(x_ref))) { + Serial.printf("- passed\r\n"); + } else { + result = false; + Serial.printf("- failed\r\n"); + printPunFail(x_ref, x, sizeof(x_ref) / sizeof(uint16_t)); + } + } + { + alignas(alignof(uint32_t)) uint16_t x[] = { 1, 2, 3, 0 }; + aliasTestO2(x); + Serial.printf(" Option -O2 "); + if (0 == memcmp(x_ref, x, sizeof(x_ref))) { + Serial.printf("- passed\r\n"); + } else { + result = false; + Serial.printf("- failed\r\n"); + printPunFail(x_ref, x, sizeof(x_ref) / sizeof(uint16_t)); + } + } + { + alignas(alignof(uint32_t)) uint16_t x[] = { 1, 2, 3, 0 }; + aliasTestO3(x); + Serial.printf(" Option -O3 "); + if (0 == memcmp(x_ref, x, sizeof(x_ref))) { + Serial.printf("- passed\r\n"); + } else { + result = false; + Serial.printf("- failed\r\n"); + printPunFail(x_ref, x, sizeof(x_ref) / sizeof(uint16_t)); + } + } + return result; +} + + uint32_t cyclesToRead_nKx32(int n, unsigned int *x, uint32_t *res) { uint32_t b = ESP.getCycleCount(); uint32_t sum = 0; - for (int i = 0; i < n * 1024; i++) { - sum += *(x++); - } + for (int i = 0; i < n * 1024; i++) { sum += *(x++); } *res = sum; return ESP.getCycleCount() - b; } @@ -37,9 +213,7 @@ uint32_t cyclesToWrite_nKx32(int n, unsigned int *x) { uint32_t cyclesToRead_nKx16(int n, unsigned short *x, uint32_t *res) { uint32_t b = ESP.getCycleCount(); uint32_t sum = 0; - for (int i = 0; i < n * 1024; i++) { - sum += *(x++); - } + for (int i = 0; i < n * 1024; i++) { sum += *(x++); } *res = sum; return ESP.getCycleCount() - b; } @@ -54,17 +228,33 @@ uint32_t cyclesToWrite_nKx16(int n, unsigned short *x) { return ESP.getCycleCount() - b; } -uint32_t cyclesToRead_nKx8(int n, unsigned char*x, uint32_t *res) { +uint32_t cyclesToRead_nKxs16(int n, short *x, int32_t *res) { uint32_t b = ESP.getCycleCount(); - uint32_t sum = 0; + int32_t sum = 0; + for (int i = 0; i < n * 1024; i++) { sum += *(x++); } + *res = sum; + return ESP.getCycleCount() - b; +} + +uint32_t cyclesToWrite_nKxs16(int n, short *x) { + uint32_t b = ESP.getCycleCount(); + int32_t sum = 0; for (int i = 0; i < n * 1024; i++) { - sum += *(x++); + sum += i; + *(x++) = sum; } + return ESP.getCycleCount() - b; +} + +uint32_t cyclesToRead_nKx8(int n, unsigned char *x, uint32_t *res) { + uint32_t b = ESP.getCycleCount(); + uint32_t sum = 0; + for (int i = 0; i < n * 1024; i++) { sum += *(x++); } *res = sum; return ESP.getCycleCount() - b; } -uint32_t cyclesToWrite_nKx8(int n, unsigned char*x) { +uint32_t cyclesToWrite_nKx8(int n, unsigned char *x) { uint32_t b = ESP.getCycleCount(); uint32_t sum = 0; for (int i = 0; i < n * 1024; i++) { @@ -75,12 +265,11 @@ uint32_t cyclesToWrite_nKx8(int n, unsigned char*x) { } // Compare with Inline - uint32_t cyclesToRead_nKx16_viaInline(int n, unsigned short *x, uint32_t *res) { uint32_t b = ESP.getCycleCount(); uint32_t sum = 0; for (int i = 0; i < n * 1024; i++) { - sum += mmu_get_uint16(x++); //*(x++); + sum += mmu_get_uint16(x++); //*(x++); } *res = sum; return ESP.getCycleCount() - b; @@ -97,17 +286,38 @@ uint32_t cyclesToWrite_nKx16_viaInline(int n, unsigned short *x) { return ESP.getCycleCount() - b; } -uint32_t cyclesToRead_nKx8_viaInline(int n, unsigned char*x, uint32_t *res) { +uint32_t cyclesToRead_nKxs16_viaInline(int n, short *x, int32_t *res) { + uint32_t b = ESP.getCycleCount(); + int32_t sum = 0; + for (int i = 0; i < n * 1024; i++) { + sum += mmu_get_int16(x++); //*(x++); + } + *res = sum; + return ESP.getCycleCount() - b; +} + +uint32_t cyclesToWrite_nKxs16_viaInline(int n, short *x) { + uint32_t b = ESP.getCycleCount(); + int32_t sum = 0; + for (int i = 0; i < n * 1024; i++) { + sum += i; + // *(x++) = sum; + mmu_set_int16(x++, sum); + } + return ESP.getCycleCount() - b; +} + +uint32_t cyclesToRead_nKx8_viaInline(int n, unsigned char *x, uint32_t *res) { uint32_t b = ESP.getCycleCount(); uint32_t sum = 0; for (int i = 0; i < n * 1024; i++) { - sum += mmu_get_uint8(x++); //*(x++); + sum += mmu_get_uint8(x++); //*(x++); } *res = sum; return ESP.getCycleCount() - b; } -uint32_t cyclesToWrite_nKx8_viaInline(int n, unsigned char*x) { +uint32_t cyclesToWrite_nKx8_viaInline(int n, unsigned char *x) { uint32_t b = ESP.getCycleCount(); uint32_t sum = 0; for (int i = 0; i < n * 1024; i++) { @@ -118,39 +328,98 @@ uint32_t cyclesToWrite_nKx8_viaInline(int n, unsigned char*x) { return ESP.getCycleCount() - b; } -void perfTest_nK(int nK, uint32_t *mem, uint32_t *imem) { - uint32_t res; + +bool perfTest_nK(int nK, uint32_t *mem, uint32_t *imem) { + uint32_t res, verify_res; uint32_t t; + bool success = true; + int sres, verify_sres; + + Serial.printf("\r\nPerformance numbers for 16 bit access - using inline macros or exception handling for IRAM.\r\n"); + ; + t = cyclesToWrite_nKx16(nK, (uint16_t *)mem); + Serial.printf("DRAM Memory Write: %7d cycles for %dK by uint16, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024)); + t = cyclesToRead_nKx16(nK, (uint16_t *)mem, &verify_res); + Serial.printf("DRAM Memory Read: %7d cycles for %dK by uint16, %3d AVG cycles/transfer (sum %08x)\r\n", t, nK, t / (nK * 1024), verify_res); + t = cyclesToWrite_nKxs16(nK, (int16_t *)mem); + Serial.printf("DRAM Memory Write: %7d cycles for %dK by int16, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024)); + t = cyclesToRead_nKxs16(nK, (int16_t *)mem, &verify_sres); + Serial.printf("DRAM Memory Read: %7d cycles for %dK by int16, %3d AVG cycles/transfer (sum %08x)\r\n", t, nK, t / (nK * 1024), verify_sres); + + t = cyclesToWrite_nKx16_viaInline(nK, (uint16_t *)imem); + Serial.printf("IRAM Memory Write Inline: %7d cycles for %dK by uint16, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024)); + t = cyclesToRead_nKx16_viaInline(nK, (uint16_t *)imem, &res); + Serial.printf("IRAM Memory Read Inline: %7d cycles for %dK by uint16, %3d AVG cycles/transfer (sum %08x) ", t, nK, t / (nK * 1024), res); + if (res == verify_res) { + Serial.printf("- passed\r\n"); + } else { + Serial.printf("!= (sum %08x ) - failed\r\n", verify_res); + success = false; + } + + t = cyclesToWrite_nKxs16_viaInline(nK, (int16_t *)imem); + Serial.printf("IRAM Memory Write Inline: %7d cycles for %dK by int16, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024)); + t = cyclesToRead_nKxs16_viaInline(nK, (int16_t *)imem, &sres); + Serial.printf("IRAM Memory Read Inline: %7d cycles for %dK by int16, %3d AVG cycles/transfer (sum %08x) ", t, nK, t / (nK * 1024), sres); + if (sres == verify_sres) { + Serial.printf("- passed\r\n"); + } else { + Serial.printf("!= (sum %08x ) - failed\r\n", verify_sres); + success = false; + } - t = cyclesToWrite_nKx16(nK, (uint16_t*)imem); - Serial.printf("IRAM Memory Write: %6d cycles for %dK by 16, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024)); - t = cyclesToRead_nKx16(nK, (uint16_t*)imem, &res); - Serial.printf("IRAM Memory Read: %6d cycles for %dK by 16, %3d AVG cycles/transfer (sum %08x)\r\n", t, nK, t / (nK * 1024), res); - - t = cyclesToWrite_nKx16_viaInline(nK, (uint16_t*)imem); - Serial.printf("IRAM Memory Write Inline: %6d cycles for %dK by 16, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024)); - t = cyclesToRead_nKx16_viaInline(nK, (uint16_t*)imem, &res); - Serial.printf("IRAM Memory Read Inline: %6d cycles for %dK by 16, %3d AVG cycles/transfer (sum %08x)\r\n", t, nK, t / (nK * 1024), res); - - t = cyclesToWrite_nKx16(nK, (uint16_t*)mem); - Serial.printf("DRAM Memory Write: %6d cycles for %dK by 16, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024)); - t = cyclesToRead_nKx16(nK, (uint16_t*)mem, &res); - Serial.printf("DRAM Memory Read: %6d cycles for %dK by 16, %3d AVG cycles/transfer (sum %08x)\r\n", t, nK, t / (nK * 1024), res); - - t = cyclesToWrite_nKx8(nK, (uint8_t*)imem); - Serial.printf("IRAM Memory Write: %6d cycles for %dK by 8, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024)); - t = cyclesToRead_nKx8(nK, (uint8_t*)imem, &res); - Serial.printf("IRAM Memory Read: %6d cycles for %dK by 8, %3d AVG cycles/transfer (sum %08x)\r\n", t, nK, t / (nK * 1024), res); - - t = cyclesToWrite_nKx8_viaInline(nK, (uint8_t*)imem); - Serial.printf("IRAM Memory Write Inline: %6d cycles for %dK by 8, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024)); - t = cyclesToRead_nKx8_viaInline(nK, (uint8_t*)imem, &res); - Serial.printf("IRAM Memory Read Inline: %6d cycles for %dK by 8, %3d AVG cycles/transfer (sum %08x)\r\n", t, nK, t / (nK * 1024), res); - - t = cyclesToWrite_nKx8(nK, (uint8_t*)mem); - Serial.printf("DRAM Memory Write: %6d cycles for %dK by 8, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024)); - t = cyclesToRead_nKx8(nK, (uint8_t*)mem, &res); - Serial.printf("DRAM Memory Read: %6d cycles for %dK by 8, %3d AVG cycles/transfer (sum %08x)\r\n", t, nK, t / (nK * 1024), res); + t = cyclesToWrite_nKx16(nK, (uint16_t *)imem); + Serial.printf("IRAM Memory Write: %7d cycles for %dK by uint16, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024)); + t = cyclesToRead_nKx16(nK, (uint16_t *)imem, &res); + Serial.printf("IRAM Memory Read: %7d cycles for %dK by uint16, %3d AVG cycles/transfer (sum %08x) ", t, nK, t / (nK * 1024), res); + if (res == verify_res) { + Serial.printf("- passed\r\n"); + } else { + Serial.printf("!= (sum %08x ) - failed\r\n", verify_res); + success = false; + } + t = cyclesToWrite_nKxs16(nK, (int16_t *)imem); + Serial.printf("IRAM Memory Write: %7d cycles for %dK by int16, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024)); + t = cyclesToRead_nKxs16(nK, (int16_t *)imem, &sres); + Serial.printf("IRAM Memory Read: %7d cycles for %dK by int16, %3d AVG cycles/transfer (sum %08x) ", t, nK, t / (nK * 1024), sres); + if (sres == verify_sres) { + Serial.printf("- passed\r\n"); + } else { + Serial.printf("!= (sum %08x ) - failed\r\n", verify_sres); + success = false; + } + + Serial.printf("\r\nPerformance numbers for 8 bit access - using inline macros or exception handling for IRAM access.\r\n"); + ; + t = cyclesToWrite_nKx8(nK, (uint8_t *)mem); + Serial.printf("DRAM Memory Write: %7d cycles for %dK by uint8, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024)); + t = cyclesToRead_nKx8(nK, (uint8_t *)mem, &verify_res); + Serial.printf("DRAM Memory Read: %7d cycles for %dK by uint8, %3d AVG cycles/transfer (sum %08x)\r\n", t, nK, t / (nK * 1024), verify_res); + + t = cyclesToWrite_nKx8_viaInline(nK, (uint8_t *)imem); + Serial.printf("IRAM Memory Write Inline: %7d cycles for %dK by uint8, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024)); + t = cyclesToRead_nKx8_viaInline(nK, (uint8_t *)imem, &res); + Serial.printf("IRAM Memory Read Inline: %7d cycles for %dK by uint8, %3d AVG cycles/transfer (sum %08x) ", t, nK, t / (nK * 1024), res); + if (res == verify_res) { + Serial.printf("- passed\r\n"); + } else { + Serial.printf("!= (sum %08x ) - failed\r\n", verify_res); + success = false; + } + + t = cyclesToWrite_nKx8(nK, (uint8_t *)imem); + Serial.printf("IRAM Memory Write: %7d cycles for %dK by uint8, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024)); + t = cyclesToRead_nKx8(nK, (uint8_t *)imem, &res); + Serial.printf("IRAM Memory Read: %7d cycles for %dK by uint8, %3d AVG cycles/transfer (sum %08x) ", t, nK, t / (nK * 1024), res); + if (res == verify_res) { + Serial.printf("- passed\r\n"); + } else { + Serial.printf("!= (sum %08x ) - failed\r\n", verify_res); + success = false; + } + Serial.println(); + + return success; } void setup() { @@ -174,9 +443,7 @@ void setup() { Serial.printf("DRAM free: %6d\r\n", ESP.getFreeHeap()); uint32_t *mem = (uint32_t *)malloc(2 * 1024 * sizeof(uint32_t)); Serial.printf("DRAM buffer: Address %p, free %d\r\n", mem, ESP.getFreeHeap()); - if (!mem) { - return; - } + if (!mem) { return; } // Now request from the IRAM heap #ifdef USE_SET_IRAM_HEAP @@ -200,29 +467,38 @@ void setup() { Serial.printf("IRAM buffer: Address %p, free %d\r\n", imem, ESP.getFreeHeap()); } #endif - if (!imem) { - return; - } + if (!imem) { return; } uint32_t res; uint32_t t; int nK = 1; - Serial.println(); - t = cyclesToWrite_nKx32(nK, imem); - Serial.printf("IRAM Memory Write: %6d cycles for %dK by 32, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024)); - t = cyclesToRead_nKx32(nK, imem, &res); - Serial.printf("IRAM Memory Read: %6d cycles for %dK by 32, %3d AVG cycles/transfer (sum %08x)\r\n", t, nK, t / (nK * 1024), res); - + Serial.printf("\r\nPerformance numbers for 32 bit access - no exception handler or inline macros needed.\r\n"); + ; t = cyclesToWrite_nKx32(nK, mem); - Serial.printf("DRAM Memory Write: %6d cycles for %dK by 32, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024)); + Serial.printf("DRAM Memory Write: %7d cycles for %dK by uint32, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024)); t = cyclesToRead_nKx32(nK, mem, &res); - Serial.printf("DRAM Memory Read: %6d cycles for %dK by 32, %3d AVG cycles/transfer (sum %08x)\r\n", t, nK, t / (nK * 1024), res); - Serial.println(); - perfTest_nK(1, mem, imem); - Serial.println(); - perfTest_nK(4, mem, imem); + Serial.printf("DRAM Memory Read: %7d cycles for %dK by uint32, %3d AVG cycles/transfer (sum %08x)\r\n", t, nK, t / (nK * 1024), res); + + t = cyclesToWrite_nKx32(nK, imem); + Serial.printf("IRAM Memory Write: %7d cycles for %dK by uint32, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024)); + t = cyclesToRead_nKx32(nK, imem, &res); + Serial.printf("IRAM Memory Read: %7d cycles for %dK by uint32, %3d AVG cycles/transfer (sum %08x)\r\n", t, nK, t / (nK * 1024), res); Serial.println(); + + if (perfTest_nK(1, mem, imem) && testPunning() && test4_32bit_loads()) { + Serial.println(); + } else { + Serial.println("\r\n*******************************"); + Serial.println("*******************************"); + Serial.println("** **"); + Serial.println("** One or more test failed **"); + Serial.println("** **"); + Serial.println("*******************************"); + Serial.println("*******************************\r\n"); + return; + } + #ifdef USE_SET_IRAM_HEAP // Let's use IRAM heap to make a big ole' String ESP.setIramHeap(); @@ -287,11 +563,10 @@ void setup() { size_t free_iram = ESP.getFreeHeap(); ETS_PRINTF("IRAM free: %6d\n", free_iram); uint32_t hfree; - uint16_t hmax; + uint32_t hmax; uint8_t hfrag; ESP.getHeapStats(&hfree, &hmax, &hfrag); - ETS_PRINTF("ESP.getHeapStats(free: %u, max: %u, frag: %u)\n", - hfree, hmax, hfrag); + ETS_PRINTF("ESP.getHeapStats(free: %u, max: %u, frag: %u)\n", hfree, hmax, hfrag); if (free_iram > UMM_OVERHEAD_ADJUST) { void *all = malloc(free_iram - UMM_OVERHEAD_ADJUST); ETS_PRINTF("%p = malloc(%u)\n", all, free_iram); @@ -306,19 +581,22 @@ void setup() { } } -void processKey(Print& out, int hotKey) { +void processKey(Print &out, int hotKey) { switch (hotKey) { - case 'd': { + case 'd': + { HeapSelectDram ephemeral; umm_info(NULL, true); break; } - case 'i': { + case 'i': + { HeapSelectIram ephemeral; umm_info(NULL, true); break; } - case 'h': { + case 'h': + { { HeapSelectIram ephemeral; Serial.printf(PSTR("IRAM ESP.getFreeHeap: %u\n"), ESP.getFreeHeap()); @@ -333,10 +611,8 @@ void processKey(Print& out, int hotKey) { out.printf_P(PSTR("Restart, ESP.restart(); ...\r\n")); ESP.restart(); break; - case '\r': - out.println(); - case '\n': - break; + case '\r': out.println(); + case '\n': break; case '?': out.println(); out.println(F("Press a key + ")); diff --git a/libraries/esp8266/examples/virtualmem/virtualmem.ino b/libraries/esp8266/examples/virtualmem/virtualmem.ino new file mode 100644 index 0000000000..1c4e3340fa --- /dev/null +++ b/libraries/esp8266/examples/virtualmem/virtualmem.ino @@ -0,0 +1,130 @@ + +uint32_t cyclesToRead1Kx32(unsigned int *x, uint32_t *res) { + uint32_t b = ESP.getCycleCount(); + uint32_t sum = 0; + for (int i = 0; i < 1024; i++) { sum += *(x++); } + *res = sum; + return ESP.getCycleCount() - b; +} + +uint32_t cyclesToWrite1Kx32(unsigned int *x) { + uint32_t b = ESP.getCycleCount(); + uint32_t sum = 0; + for (int i = 0; i < 1024; i++) { + sum += i; + *(x++) = sum; + } + return ESP.getCycleCount() - b; +} + + +uint32_t cyclesToRead1Kx16(unsigned short *x, uint32_t *res) { + uint32_t b = ESP.getCycleCount(); + uint32_t sum = 0; + for (int i = 0; i < 1024; i++) { sum += *(x++); } + *res = sum; + return ESP.getCycleCount() - b; +} + +uint32_t cyclesToWrite1Kx16(unsigned short *x) { + uint32_t b = ESP.getCycleCount(); + uint32_t sum = 0; + for (int i = 0; i < 1024; i++) { + sum += i; + *(x++) = sum; + } + return ESP.getCycleCount() - b; +} + +uint32_t cyclesToRead1Kx8(unsigned char *x, uint32_t *res) { + uint32_t b = ESP.getCycleCount(); + uint32_t sum = 0; + for (int i = 0; i < 1024; i++) { sum += *(x++); } + *res = sum; + return ESP.getCycleCount() - b; +} + +uint32_t cyclesToWrite1Kx8(unsigned char *x) { + uint32_t b = ESP.getCycleCount(); + uint32_t sum = 0; + for (int i = 0; i < 1024; i++) { + sum += i; + *(x++) = sum; + } + return ESP.getCycleCount() - b; +} + +void setup() { + Serial.begin(115200); + Serial.printf("\n"); + + // Enabling VM does not change malloc to use the external region. It will continue to + // use the normal RAM until we request otherwise. + uint32_t *mem = (uint32_t *)malloc(1024 * sizeof(uint32_t)); + Serial.printf("Internal buffer: Address %p, free %d\n", mem, ESP.getFreeHeap()); + + // Now request from the VM heap + ESP.setExternalHeap(); + uint32_t *vm = (uint32_t *)malloc(1024 * sizeof(uint32_t)); + Serial.printf("External buffer: Address %p, free %d\n", vm, ESP.getFreeHeap()); + // Make sure we go back to the internal heap for other allocations. Don't forget to ESP.resetHeap()! + ESP.resetHeap(); + + uint32_t res; + uint32_t t; + t = cyclesToWrite1Kx32(vm); + Serial.printf("Virtual Memory Write: %d cycles for 4K\n", t); + t = cyclesToWrite1Kx32(mem); + Serial.printf("Physical Memory Write: %d cycles for 4K\n", t); + + t = cyclesToRead1Kx32(vm, &res); + Serial.printf("Virtual Memory Read: %d cycles for 4K (sum %08x)\n", t, res); + t = cyclesToRead1Kx32(mem, &res); + Serial.printf("Physical Memory Read: %d cycles for 4K (sum %08x)\n", t, res); + + t = cyclesToWrite1Kx16((uint16_t *)vm); + Serial.printf("Virtual Memory Write: %d cycles for 2K by 16\n", t); + t = cyclesToWrite1Kx16((uint16_t *)mem); + Serial.printf("Physical Memory Write: %d cycles for 2K by 16\n", t); + + t = cyclesToRead1Kx16((uint16_t *)vm, &res); + Serial.printf("Virtual Memory Read: %d cycles for 2K by 16 (sum %08x)\n", t, res); + t = cyclesToRead1Kx16((uint16_t *)mem, &res); + Serial.printf("Physical Memory Read: %d cycles for 2K by 16 (sum %08x)\n", t, res); + + t = cyclesToWrite1Kx8((uint8_t *)vm); + Serial.printf("Virtual Memory Write: %d cycles for 1K by 8\n", t); + t = cyclesToWrite1Kx8((uint8_t *)mem); + Serial.printf("Physical Memory Write: %d cycles for 1K by 8\n", t); + + t = cyclesToRead1Kx8((uint8_t *)vm, &res); + Serial.printf("Virtual Memory Read: %d cycles for 1K by 8 (sum %08x)\n", t, res); + t = cyclesToRead1Kx8((uint8_t *)mem, &res); + Serial.printf("Physical Memory Read: %d cycles for 1K by 8 (sum %08x)\n", t, res); + + // Let's use external heap to make a big ole' String + ESP.setExternalHeap(); + String s = ""; + for (int i = 0; i < 100; i++) { + s += i; + s += ' '; + } + ESP.resetHeap(); + Serial.printf("Internal free: %d\n", ESP.getFreeHeap()); + ESP.setExternalHeap(); + Serial.printf("External free: %d\n", ESP.getFreeHeap()); + ESP.resetHeap(); + Serial.printf("String: %s\n", s.c_str()); + + // Note that free/realloc will all use the heap specified when the pointer was created. + // No need to change heaps to delete an object, only to create it. + free(vm); + free(mem); + + Serial.printf("Internal free: %d\n", ESP.getFreeHeap()); + ESP.setExternalHeap(); + Serial.printf("External free: %d\n", ESP.getFreeHeap()); + ESP.resetHeap(); +} + +void loop() {} 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 ed2943c278..da15844ff3 100644 --- a/libraries/lwIP_PPP/examples/PPPServer/PPPServer.ino +++ b/libraries/lwIP_PPP/examples/PPPServer/PPPServer.ino @@ -19,13 +19,11 @@ #include #include #include -#include #include -#include #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif #define LOGGERBAUD 115200 @@ -34,18 +32,15 @@ #define NAPT 200 #define NAPT_PORT 3 -#define RX 13 // d1mini D7 -#define TX 15 // d1mini D8 +#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) { - logger.printf("ppp: ip=%s/mask=%s/gw=%s\n", - IPAddress(&nif->ip_addr).toString().c_str(), - IPAddress(&nif->netmask).toString().c_str(), - IPAddress(&nif->gw).toString().c_str()); + logger.printf("ppp: ip=%s/mask=%s/gw=%s\n", IPAddress(&nif->ip_addr).toString().c_str(), IPAddress(&nif->netmask).toString().c_str(), IPAddress(&nif->gw).toString().c_str()); logger.printf("Heap before: %d\n", ESP.getFreeHeap()); err_t ret = ip_napt_init(NAPT, NAPT_PORT); @@ -53,9 +48,7 @@ void PPPConnectedCallback(netif* nif) { if (ret == ERR_OK) { ret = ip_napt_enable_no(nif->num, 1); logger.printf("ip_napt_enable(nif): ret=%d (OK=%d)\n", (int)ret, (int)ERR_OK); - if (ret == ERR_OK) { - logger.printf("PPP client is NATed\n"); - } + if (ret == ERR_OK) { logger.printf("PPP client is NATed\n"); } // could not make this work yet, // but packets are arriving on ppp client (= linux host) @@ -64,9 +57,7 @@ void PPPConnectedCallback(netif* nif) { logger.printf("redirect443=%d\n", ip_portmap_add(IP_PROTO_TCP, ip_2_ip4(&nif->ip_addr)->addr, 443, ip_2_ip4(&nif->gw)->addr, 443)); } logger.printf("Heap after napt init: %d\n", ESP.getFreeHeap()); - if (ret != ERR_OK) { - logger.printf("NAPT initialization failed\n"); - } + if (ret != ERR_OK) { logger.printf("NAPT initialization failed\n"); } } void setup() { @@ -79,13 +70,11 @@ void setup() { logger.print('.'); delay(500); } - 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()); + 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"); @@ -107,5 +96,4 @@ void setup() { #endif -void loop() { -} +void loop() {} diff --git a/libraries/lwIP_PPP/library.properties b/libraries/lwIP_PPP/library.properties index 9b2293dbfb..073769568c 100644 --- a/libraries/lwIP_PPP/library.properties +++ b/libraries/lwIP_PPP/library.properties @@ -4,7 +4,7 @@ author=lwIP maintainer=esp8266/Arduino sentence=PPP interface paragraph=PPP interface for esp8266 arduino -category=Network +category=Communication url=https://github.com/esp8266/Arduino architectures=esp8266 dot_a_linkage=true diff --git a/libraries/lwIP_PPP/src/PPPServer.cpp b/libraries/lwIP_PPP/src/PPPServer.cpp index 98fddd7777..2e44d2433a 100644 --- a/libraries/lwIP_PPP/src/PPPServer.cpp +++ b/libraries/lwIP_PPP/src/PPPServer.cpp @@ -2,8 +2,8 @@ // This is still beta / a work in progress // testing on linux: -// sudo /usr/sbin/pppd /dev/ttyUSB1 38400 noipdefault nocrtscts local defaultroute noauth nodetach debug dump -// sudo /usr/sbin/pppd /dev/ttyUSB1 38400 noipdefault nocrtscts local defaultroute noauth +// sudo /usr/sbin/pppd /dev/ttyUSB1 38400 noipdefault nocrtscts local defaultroute noauth nodetach +// debug dump sudo /usr/sbin/pppd /dev/ttyUSB1 38400 noipdefault nocrtscts local defaultroute noauth // proxy arp is needed but we don't have it // http://lwip.100.n7.nabble.com/PPP-proxy-arp-support-tp33286p33345.html @@ -16,9 +16,7 @@ #include "PPPServer.h" -PPPServer::PPPServer(Stream* sio): _sio(sio), _cb(netif_status_cb_s), _enabled(false) -{ -} +PPPServer::PPPServer(Stream* sio) : _sio(sio), _cb(netif_status_cb_s), _enabled(false) { } bool PPPServer::handlePackets() { @@ -38,15 +36,15 @@ bool PPPServer::handlePackets() void PPPServer::link_status_cb_s(ppp_pcb* pcb, int err_code, void* ctx) { - bool stop = true; - netif* nif = ppp_netif(pcb); + bool stop = true; + netif* nif = ppp_netif(pcb); switch (err_code) { - case PPPERR_NONE: /* No error. */ + case PPPERR_NONE: /* No error. */ { #if LWIP_DNS - const ip_addr_t *ns; + const ip_addr_t* ns; #endif /* LWIP_DNS */ ets_printf("ppp_link_status_cb: PPPERR_NONE\n\r"); #if LWIP_IPV4 @@ -68,54 +66,54 @@ void PPPServer::link_status_cb_s(ppp_pcb* pcb, int err_code, void* ctx) ets_printf(" our6_ipaddr = %s\n\r", ip6addr_ntoa(netif_ip6_addr(nif, 0))); #endif /* PPP_IPV6_SUPPORT */ } - stop = false; - break; + stop = false; + break; - case PPPERR_PARAM: /* Invalid parameter. */ + case PPPERR_PARAM: /* Invalid parameter. */ ets_printf("ppp_link_status_cb: PPPERR_PARAM\n"); break; - case PPPERR_OPEN: /* Unable to open PPP session. */ + case PPPERR_OPEN: /* Unable to open PPP session. */ ets_printf("ppp_link_status_cb: PPPERR_OPEN\n"); break; - case PPPERR_DEVICE: /* Invalid I/O device for PPP. */ + case PPPERR_DEVICE: /* Invalid I/O device for PPP. */ ets_printf("ppp_link_status_cb: PPPERR_DEVICE\n"); break; - case PPPERR_ALLOC: /* Unable to allocate resources. */ + case PPPERR_ALLOC: /* Unable to allocate resources. */ ets_printf("ppp_link_status_cb: PPPERR_ALLOC\n"); break; - case PPPERR_USER: /* User interrupt. */ + case PPPERR_USER: /* User interrupt. */ ets_printf("ppp_link_status_cb: PPPERR_USER\n"); break; - case PPPERR_CONNECT: /* Connection lost. */ + case PPPERR_CONNECT: /* Connection lost. */ ets_printf("ppp_link_status_cb: PPPERR_CONNECT\n"); break; - case PPPERR_AUTHFAIL: /* Failed authentication challenge. */ + case PPPERR_AUTHFAIL: /* Failed authentication challenge. */ ets_printf("ppp_link_status_cb: PPPERR_AUTHFAIL\n"); break; - case PPPERR_PROTOCOL: /* Failed to meet protocol. */ + case PPPERR_PROTOCOL: /* Failed to meet protocol. */ ets_printf("ppp_link_status_cb: PPPERR_PROTOCOL\n"); break; - case PPPERR_PEERDEAD: /* Connection timeout. */ + case PPPERR_PEERDEAD: /* Connection timeout. */ ets_printf("ppp_link_status_cb: PPPERR_PEERDEAD\n"); break; - case PPPERR_IDLETIMEOUT: /* Idle Timeout. */ + case PPPERR_IDLETIMEOUT: /* Idle Timeout. */ ets_printf("ppp_link_status_cb: PPPERR_IDLETIMEOUT\n"); break; - case PPPERR_CONNECTTIME: /* PPPERR_CONNECTTIME. */ + case PPPERR_CONNECTTIME: /* PPPERR_CONNECTTIME. */ ets_printf("ppp_link_status_cb: PPPERR_CONNECTTIME\n"); break; - case PPPERR_LOOPBACK: /* Connection timeout. */ + case PPPERR_LOOPBACK: /* Connection timeout. */ ets_printf("ppp_link_status_cb: PPPERR_LOOPBACK\n"); break; @@ -165,22 +163,24 @@ bool PPPServer::begin(const IPAddress& ourAddress, const IPAddress& peer) ppp_set_ipcp_ouraddr(_ppp, ip_2_ip4((const ip_addr_t*)ourAddress)); ppp_set_ipcp_hisaddr(_ppp, ip_2_ip4((const ip_addr_t*)peer)); - //ip4_addr_t addr; - //IP4_ADDR(&addr, 10,0,1,254); - //ppp_set_ipcp_dnsaddr(_ppp, 0, &addr); + // ip4_addr_t addr; + // IP4_ADDR(&addr, 10,0,1,254); + // ppp_set_ipcp_dnsaddr(_ppp, 0, &addr); - //ppp_set_auth(_ppp, PPPAUTHTYPE_ANY, "login", "password"); - //ppp_set_auth_required(_ppp, 1); + // ppp_set_auth(_ppp, PPPAUTHTYPE_ANY, "login", "password"); + // ppp_set_auth_required(_ppp, 1); ppp_set_silent(_ppp, 1); ppp_listen(_ppp); netif_set_status_callback(&_netif, _cb); _enabled = true; - if (!schedule_recurrent_function_us([&]() -{ - return this->handlePackets(); - }, 1000)) + if (!schedule_recurrent_function_us( + [&]() + { + return this->handlePackets(); + }, + 1000)) { netif_remove(&_netif); return false; diff --git a/libraries/lwIP_PPP/src/PPPServer.h b/libraries/lwIP_PPP/src/PPPServer.h index e2c95658a6..260ebfe2d8 100644 --- a/libraries/lwIP_PPP/src/PPPServer.h +++ b/libraries/lwIP_PPP/src/PPPServer.h @@ -27,7 +27,6 @@ */ - #ifndef __PPPSERVER_H #define __PPPSERVER_H @@ -40,7 +39,6 @@ class PPPServer { public: - PPPServer(Stream* sio); bool begin(const IPAddress& ourAddress, const IPAddress& peer = IPAddress(172, 31, 255, 254)); @@ -56,22 +54,20 @@ class PPPServer } protected: - static constexpr size_t _bufsize = 128; - Stream* _sio; - ppp_pcb* _ppp; - netif _netif; + Stream* _sio; + ppp_pcb* _ppp; + netif _netif; void (*_cb)(netif*); uint8_t _buf[_bufsize]; - bool _enabled; + bool _enabled; // feed ppp from stream - to call on a regular basis or on interrupt bool handlePackets(); static u32_t output_cb_s(ppp_pcb* pcb, u8_t* data, u32_t len, void* ctx); - static void link_status_cb_s(ppp_pcb* pcb, int err_code, void* ctx); - static void netif_status_cb_s(netif* nif); - + static void link_status_cb_s(ppp_pcb* pcb, int err_code, void* ctx); + static void netif_status_cb_s(netif* nif); }; -#endif // __PPPSERVER_H +#endif // __PPPSERVER_H diff --git a/libraries/lwIP_enc28j60/library.properties b/libraries/lwIP_enc28j60/library.properties index 444e0f1c41..28ebd2d5c3 100644 --- a/libraries/lwIP_enc28j60/library.properties +++ b/libraries/lwIP_enc28j60/library.properties @@ -4,7 +4,7 @@ author=Nicholas Humfrey maintainer=esp8266/Arduino sentence=Ethernet driver paragraph=ENC28J60 ethernet drivers for lwIP and esp8266 Arduino from https://github.com/njh/EtherSia/tree/master/src/enc28j60.cpp -category=Network +category=Communication url=https://github.com/esp8266/Arduino architectures=esp8266 dot_a_linkage=true diff --git a/libraries/lwIP_enc28j60/src/ENC28J60lwIP.h b/libraries/lwIP_enc28j60/src/ENC28J60lwIP.h index e525e94069..cef7d61f8e 100644 --- a/libraries/lwIP_enc28j60/src/ENC28J60lwIP.h +++ b/libraries/lwIP_enc28j60/src/ENC28J60lwIP.h @@ -7,4 +7,4 @@ using ENC28J60lwIP = LwipIntfDev; -#endif // _ENC28J60LWIP_H +#endif // _ENC28J60LWIP_H diff --git a/libraries/lwIP_enc28j60/src/utility/enc28j60.cpp b/libraries/lwIP_enc28j60/src/utility/enc28j60.cpp index 42d1af7fc2..0ee3096486 100644 --- a/libraries/lwIP_enc28j60/src/utility/enc28j60.cpp +++ b/libraries/lwIP_enc28j60/src/utility/enc28j60.cpp @@ -42,10 +42,9 @@ #include "enc28j60.h" - -void serial_printf(const char *fmt, ...) +void serial_printf(const char* fmt, ...) { - char buf[128]; + char buf[128]; va_list args; va_start(args, fmt); vsnprintf(buf, 128, fmt, args); @@ -57,11 +56,15 @@ void serial_printf(const char *fmt, ...) #if DEBUG #define PRINTF(...) printf(__VA_ARGS__) #else -#define PRINTF(...) do { (void)0; } while (0) +#define PRINTF(...) \ + do \ + { \ + (void)0; \ + } while (0) #endif -#define EIE 0x1b -#define EIR 0x1c +#define EIE 0x1b +#define EIR 0x1c #define ESTAT 0x1d #define ECON2 0x1e #define ECON1 0x1f @@ -69,13 +72,13 @@ void serial_printf(const char *fmt, ...) #define ESTAT_CLKRDY 0x01 #define ESTAT_TXABRT 0x02 -#define ECON1_RXEN 0x04 -#define ECON1_TXRTS 0x08 +#define ECON1_RXEN 0x04 +#define ECON1_TXRTS 0x08 #define ECON2_AUTOINC 0x80 -#define ECON2_PKTDEC 0x40 +#define ECON2_PKTDEC 0x40 -#define EIR_TXIF 0x08 +#define EIR_TXIF 0x08 #define ERXTX_BANK 0x00 @@ -95,30 +98,41 @@ void serial_printf(const char *fmt, ...) #define ERXRDPTH 0x0d #define RX_BUF_START 0x0000 -#define RX_BUF_END 0x0fff +#define RX_BUF_END 0x0fff #define TX_BUF_START 0x1200 /* MACONx registers are in bank 2 */ #define MACONX_BANK 0x02 -#define MACON1 0x00 -#define MACON3 0x02 -#define MACON4 0x03 +#define MACON1 0x00 +#define MACSTAT1 0x01 +#define MACON3 0x02 +#define MACON4 0x03 #define MABBIPG 0x04 -#define MAIPGL 0x06 -#define MAIPGH 0x07 +#define MAIPGL 0x06 +#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 #define MACON1_MARXEN 0x01 #define MACON3_PADCFG_FULL 0xe0 -#define MACON3_TXCRCEN 0x10 -#define MACON3_FRMLNEN 0x02 -#define MACON3_FULDPX 0x01 +#define MACON3_TXCRCEN 0x10 +#define MACON3_FRMLNEN 0x02 +#define MACON3_FULDPX 0x01 #define MAX_MAC_LENGTH 1518 @@ -132,43 +146,41 @@ 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 -#define ERXFCON_UCEN 0x80 +#define ERXFCON_UCEN 0x80 #define ERXFCON_ANDOR 0x40 #define ERXFCON_CRCEN 0x20 -#define ERXFCON_MCEN 0x02 -#define ERXFCON_BCEN 0x01 +#define ERXFCON_MCEN 0x02 +#define ERXFCON_BCEN 0x01 // The ENC28J60 SPI Interface supports clock speeds up to 20 MHz static const SPISettings spiSettings(20000000, MSBFIRST, SPI_MODE0); -ENC28J60::ENC28J60(int8_t cs, SPIClass& spi, int8_t intr): - _bank(ERXTX_BANK), _cs(cs), _spi(spi) +ENC28J60::ENC28J60(int8_t cs, SPIClass& spi, int8_t intr) : _bank(ERXTX_BANK), _cs(cs), _spi(spi) { (void)intr; } -void -ENC28J60::enc28j60_arch_spi_select(void) +void ENC28J60::enc28j60_arch_spi_select(void) { SPI.beginTransaction(spiSettings); digitalWrite(_cs, LOW); } -void -ENC28J60::enc28j60_arch_spi_deselect(void) +void ENC28J60::enc28j60_arch_spi_deselect(void) { digitalWrite(_cs, HIGH); SPI.endTransaction(); } - /*---------------------------------------------------------------------------*/ -uint8_t -ENC28J60::is_mac_mii_reg(uint8_t reg) +uint8_t ENC28J60::is_mac_mii_reg(uint8_t reg) { /* MAC or MII register (otherwise, ETH register)? */ switch (_bank) @@ -184,8 +196,7 @@ ENC28J60::is_mac_mii_reg(uint8_t reg) } } /*---------------------------------------------------------------------------*/ -uint8_t -ENC28J60::readreg(uint8_t reg) +uint8_t ENC28J60::readreg(uint8_t reg) { uint8_t r; enc28j60_arch_spi_select(); @@ -200,8 +211,7 @@ ENC28J60::readreg(uint8_t reg) return r; } /*---------------------------------------------------------------------------*/ -void -ENC28J60::writereg(uint8_t reg, uint8_t data) +void ENC28J60::writereg(uint8_t reg, uint8_t data) { enc28j60_arch_spi_select(); SPI.transfer(0x40 | (reg & 0x1f)); @@ -209,8 +219,7 @@ ENC28J60::writereg(uint8_t reg, uint8_t data) enc28j60_arch_spi_deselect(); } /*---------------------------------------------------------------------------*/ -void -ENC28J60::setregbitfield(uint8_t reg, uint8_t mask) +void ENC28J60::setregbitfield(uint8_t reg, uint8_t mask) { if (is_mac_mii_reg(reg)) { @@ -225,8 +234,7 @@ ENC28J60::setregbitfield(uint8_t reg, uint8_t mask) } } /*---------------------------------------------------------------------------*/ -void -ENC28J60::clearregbitfield(uint8_t reg, uint8_t mask) +void ENC28J60::clearregbitfield(uint8_t reg, uint8_t mask) { if (is_mac_mii_reg(reg)) { @@ -241,15 +249,13 @@ ENC28J60::clearregbitfield(uint8_t reg, uint8_t mask) } } /*---------------------------------------------------------------------------*/ -void -ENC28J60::setregbank(uint8_t new_bank) +void ENC28J60::setregbank(uint8_t new_bank) { writereg(ECON1, (readreg(ECON1) & 0xfc) | (new_bank & 0x03)); _bank = new_bank; } /*---------------------------------------------------------------------------*/ -void -ENC28J60::writedata(const uint8_t *data, int datalen) +void ENC28J60::writedata(const uint8_t* data, int datalen) { int i; enc28j60_arch_spi_select(); @@ -262,14 +268,12 @@ ENC28J60::writedata(const uint8_t *data, int datalen) enc28j60_arch_spi_deselect(); } /*---------------------------------------------------------------------------*/ -void -ENC28J60::writedatabyte(uint8_t byte) +void ENC28J60::writedatabyte(uint8_t byte) { writedata(&byte, 1); } /*---------------------------------------------------------------------------*/ -int -ENC28J60::readdata(uint8_t *buf, int len) +int ENC28J60::readdata(uint8_t* buf, int len) { int i; enc28j60_arch_spi_select(); @@ -283,8 +287,7 @@ ENC28J60::readdata(uint8_t *buf, int len) return i; } /*---------------------------------------------------------------------------*/ -uint8_t -ENC28J60::readdatabyte(void) +uint8_t ENC28J60::readdatabyte(void) { uint8_t r; readdata(&r, 1); @@ -292,8 +295,7 @@ ENC28J60::readdatabyte(void) } /*---------------------------------------------------------------------------*/ -void -ENC28J60::softreset(void) +void ENC28J60::softreset(void) { enc28j60_arch_spi_select(); /* The System Command (soft reset) is 1 1 1 1 1 1 1 1 */ @@ -304,8 +306,7 @@ ENC28J60::softreset(void) /*---------------------------------------------------------------------------*/ //#if DEBUG -uint8_t -ENC28J60::readrev(void) +uint8_t ENC28J60::readrev(void) { uint8_t rev; setregbank(MAADRX_BANK); @@ -324,8 +325,7 @@ ENC28J60::readrev(void) /*---------------------------------------------------------------------------*/ -bool -ENC28J60::reset(void) +bool ENC28J60::reset(void) { PRINTF("enc28j60: resetting chip\n"); @@ -398,7 +398,9 @@ 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); @@ -470,8 +472,7 @@ ENC28J60::reset(void) setregbitfield(MACON1, MACON1_MARXEN | MACON1_TXPAUS | MACON1_RXPAUS); /* Set padding, crc, full duplex */ - setregbitfield(MACON3, MACON3_PADCFG_FULL | MACON3_TXCRCEN | MACON3_FULDPX | - MACON3_FRMLNEN); + setregbitfield(MACON3, MACON3_PADCFG_FULL | MACON3_TXCRCEN | MACON3_FULDPX | MACON3_FRMLNEN); /* Don't modify MACON4 */ @@ -531,12 +532,11 @@ ENC28J60::reset(void) return true; } /*---------------------------------------------------------------------------*/ -boolean -ENC28J60::begin(const uint8_t *address) +boolean ENC28J60::begin(const uint8_t* address) { _localMac = address; - bool ret = reset(); + bool ret = reset(); uint8_t rev = readrev(); PRINTF("ENC28J60 rev. B%d\n", rev); @@ -546,8 +546,7 @@ ENC28J60::begin(const uint8_t *address) /*---------------------------------------------------------------------------*/ -uint16_t -ENC28J60::sendFrame(const uint8_t *data, uint16_t datalen) +uint16_t ENC28J60::sendFrame(const uint8_t* data, uint16_t datalen) { uint16_t dataend; @@ -598,13 +597,14 @@ ENC28J60::sendFrame(const uint8_t *data, uint16_t datalen) /* Send the packet */ setregbitfield(ECON1, ECON1_TXRTS); - while ((readreg(ECON1) & ECON1_TXRTS) > 0); + while ((readreg(ECON1) & ECON1_TXRTS) > 0) + ; #if DEBUG if ((readreg(ESTAT) & ESTAT_TXABRT) != 0) { uint16_t erdpt; - uint8_t tsv[7]; + uint8_t tsv[7]; erdpt = (readreg(ERDPTH) << 8) | readreg(ERDPTL); writereg(ERDPTL, (dataend + 1) & 0xff); writereg(ERDPTH, (dataend + 1) >> 8); @@ -612,35 +612,32 @@ ENC28J60::sendFrame(const uint8_t *data, uint16_t datalen) writereg(ERDPTL, erdpt & 0xff); writereg(ERDPTH, erdpt >> 8); PRINTF("enc28j60: tx err: %d: %02x:%02x:%02x:%02x:%02x:%02x\n" - " tsv: %02x%02x%02x%02x%02x%02x%02x\n", datalen, - 0xff & data[0], 0xff & data[1], 0xff & data[2], - 0xff & data[3], 0xff & data[4], 0xff & data[5], - tsv[6], tsv[5], tsv[4], tsv[3], tsv[2], tsv[1], tsv[0]); + " tsv: %02x%02x%02x%02x%02x%02x%02x\n", + datalen, 0xff & data[0], 0xff & data[1], 0xff & data[2], 0xff & data[3], + 0xff & data[4], 0xff & data[5], tsv[6], tsv[5], tsv[4], tsv[3], tsv[2], tsv[1], + tsv[0]); } else { - PRINTF("enc28j60: tx: %d: %02x:%02x:%02x:%02x:%02x:%02x\n", datalen, - 0xff & data[0], 0xff & data[1], 0xff & data[2], - 0xff & data[3], 0xff & data[4], 0xff & data[5]); + PRINTF("enc28j60: tx: %d: %02x:%02x:%02x:%02x:%02x:%02x\n", datalen, 0xff & data[0], + 0xff & data[1], 0xff & data[2], 0xff & data[3], 0xff & data[4], 0xff & data[5]); } #endif - //sent_packets++; - //PRINTF("enc28j60: sent_packets %d\n", sent_packets); + // sent_packets++; + // PRINTF("enc28j60: sent_packets %d\n", sent_packets); return datalen; } /*---------------------------------------------------------------------------*/ -uint16_t -ENC28J60::readFrame(uint8_t *buffer, uint16_t bufsize) +uint16_t ENC28J60::readFrame(uint8_t* buffer, uint16_t bufsize) { readFrameSize(); return readFrameData(buffer, bufsize); } -uint16_t -ENC28J60::readFrameSize() +uint16_t ENC28J60::readFrameSize() { uint8_t n; @@ -662,13 +659,13 @@ ENC28J60::readFrameSize() /* Read the next packet pointer */ nxtpkt[0] = readdatabyte(); nxtpkt[1] = readdatabyte(); - _next = (nxtpkt[1] << 8) + nxtpkt[0]; + _next = (nxtpkt[1] << 8) + nxtpkt[0]; PRINTF("enc28j60: nxtpkt 0x%02x%02x\n", _nxtpkt[1], _nxtpkt[0]); length[0] = readdatabyte(); length[1] = readdatabyte(); - _len = (length[1] << 8) + length[0]; + _len = (length[1] << 8) + length[0]; PRINTF("enc28j60: length 0x%02x%02x\n", length[1], length[0]); @@ -682,19 +679,16 @@ ENC28J60::readFrameSize() return _len; } -void -ENC28J60::discardFrame(uint16_t framesize) +void ENC28J60::discardFrame(uint16_t framesize) { (void)framesize; (void)readFrameData(nullptr, 0); } -uint16_t -ENC28J60::readFrameData(uint8_t *buffer, uint16_t framesize) +uint16_t ENC28J60::readFrameData(uint8_t* buffer, uint16_t framesize) { if (framesize < _len) { - buffer = nullptr; /* flush rx fifo */ @@ -733,12 +727,35 @@ ENC28J60::readFrameData(uint8_t *buffer, uint16_t framesize) PRINTF("enc28j60: rx err: flushed %d\n", _len); return 0; } - PRINTF("enc28j60: rx: %d: %02x:%02x:%02x:%02x:%02x:%02x\n", _len, - 0xff & buffer[0], 0xff & buffer[1], 0xff & buffer[2], - 0xff & buffer[3], 0xff & buffer[4], 0xff & buffer[5]); + PRINTF("enc28j60: rx: %d: %02x:%02x:%02x:%02x:%02x:%02x\n", _len, 0xff & buffer[0], + 0xff & buffer[1], 0xff & buffer[2], 0xff & buffer[3], 0xff & buffer[4], + 0xff & buffer[5]); - //received_packets++; - //PRINTF("enc28j60: received_packets %d\n", received_packets); + // received_packets++; + // PRINTF("enc28j60: received_packets %d\n", received_packets); 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 0545039026..e6c65d7759 100644 --- a/libraries/lwIP_enc28j60/src/utility/enc28j60.h +++ b/libraries/lwIP_enc28j60/src/utility/enc28j60.h @@ -46,7 +46,6 @@ */ class ENC28J60 { - public: /** Constructor that uses the default hardware SPI pins @@ -61,7 +60,7 @@ class ENC28J60 @param address the local MAC address for the Ethernet interface @return Returns true if setting up the Ethernet interface was successful */ - boolean begin(const uint8_t *address); + boolean begin(const uint8_t* address); /** Send an Ethernet frame @@ -69,7 +68,7 @@ class ENC28J60 @param datalen the length of the data in the packet @return the number of bytes transmitted */ - virtual uint16_t sendFrame(const uint8_t *data, uint16_t datalen); + virtual uint16_t sendFrame(const uint8_t* data, uint16_t datalen); /** Read an Ethernet frame @@ -78,10 +77,24 @@ class ENC28J60 @return the length of the received packet or 0 if no packet was received */ - virtual uint16_t readFrame(uint8_t *buffer, uint16_t bufsize); + virtual uint16_t readFrame(uint8_t* buffer, uint16_t bufsize); -protected: + /** + 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() { return false; @@ -107,40 +120,41 @@ class ENC28J60 @param buffer a pointer to a buffer to write the frame to @param framesize readFrameSize()'s result @return the length of the received frame - or 0 if a problem occured + or 0 if a problem occurred */ - uint16_t readFrameData(uint8_t *frame, uint16_t framesize); + uint16_t readFrameData(uint8_t* frame, uint16_t framesize); private: - uint8_t is_mac_mii_reg(uint8_t reg); uint8_t readreg(uint8_t reg); - void writereg(uint8_t reg, uint8_t data); - void setregbitfield(uint8_t reg, uint8_t mask); - void clearregbitfield(uint8_t reg, uint8_t mask); - void setregbank(uint8_t new_bank); - void writedata(const uint8_t *data, int datalen); - void writedatabyte(uint8_t byte); - int readdata(uint8_t *buf, int len); + void writereg(uint8_t reg, uint8_t data); + void setregbitfield(uint8_t reg, uint8_t mask); + void clearregbitfield(uint8_t reg, uint8_t mask); + void setregbank(uint8_t new_bank); + void writedata(const uint8_t* data, int datalen); + void writedatabyte(uint8_t byte); + int readdata(uint8_t* buf, int len); uint8_t readdatabyte(void); - void softreset(void); + void softreset(void); uint8_t readrev(void); - bool reset(void); + bool reset(void); - void enc28j60_arch_spi_init(void); + void enc28j60_arch_spi_init(void); uint8_t enc28j60_arch_spi_write(uint8_t data); uint8_t enc28j60_arch_spi_read(void); - void enc28j60_arch_spi_select(void); - void enc28j60_arch_spi_deselect(void); + void enc28j60_arch_spi_select(void); + void enc28j60_arch_spi_deselect(void); // Previously defined in contiki/core/sys/clock.h void clock_delay_usec(uint16_t dt); - uint8_t _bank; - int8_t _cs; + uint16_t phyread(uint8_t reg); + + uint8_t _bank; + int8_t _cs; SPIClass& _spi; - const uint8_t *_localMac; + const uint8_t* _localMac; /* readFrame*() state */ uint16_t _next, _len; diff --git a/libraries/lwIP_w5100/library.properties b/libraries/lwIP_w5100/library.properties index b65d4fdb64..b74c5ba56e 100644 --- a/libraries/lwIP_w5100/library.properties +++ b/libraries/lwIP_w5100/library.properties @@ -1,10 +1,10 @@ -name=lwIP_w5500 +name=lwIP_w5100 version=1 author=Nicholas Humfrey maintainer=esp8266/Arduino sentence=Ethernet driver paragraph=Wiznet5100 ethernet drivers for lwIP and esp8266 Arduino from https://github.com/njh/W5100MacRaw -category=Network +category=Communication url=https://github.com/esp8266/Arduino architectures=esp8266 dot_a_linkage=true diff --git a/libraries/lwIP_w5100/src/W5100lwIP.h b/libraries/lwIP_w5100/src/W5100lwIP.h index c8f549529d..593625a8d5 100644 --- a/libraries/lwIP_w5100/src/W5100lwIP.h +++ b/libraries/lwIP_w5100/src/W5100lwIP.h @@ -7,4 +7,4 @@ using Wiznet5100lwIP = LwipIntfDev; -#endif // _W5500LWIP_H +#endif // _W5500LWIP_H diff --git a/libraries/lwIP_w5100/src/utility/w5100.cpp b/libraries/lwIP_w5100/src/utility/w5100.cpp index 6377aa5b63..84ec861e31 100644 --- a/libraries/lwIP_w5100/src/utility/w5100.cpp +++ b/libraries/lwIP_w5100/src/utility/w5100.cpp @@ -35,15 +35,14 @@ #include #include "w5100.h" - uint8_t Wiznet5100::wizchip_read(uint16_t address) { uint8_t ret; wizchip_cs_select(); _spi.transfer(0x0F); - _spi.transfer((address & 0xFF00) >> 8); - _spi.transfer((address & 0x00FF) >> 0); + _spi.transfer((address & 0xFF00) >> 8); + _spi.transfer((address & 0x00FF) >> 0); ret = _spi.transfer(0); wizchip_cs_deselect(); @@ -55,7 +54,6 @@ uint16_t Wiznet5100::wizchip_read_word(uint16_t address) return ((uint16_t)wizchip_read(address) << 8) + wizchip_read(address + 1); } - void Wiznet5100::wizchip_read_buf(uint16_t address, uint8_t* pBuf, uint16_t len) { for (uint16_t i = 0; i < len; i++) @@ -68,16 +66,16 @@ void Wiznet5100::wizchip_write(uint16_t address, uint8_t wb) { wizchip_cs_select(); _spi.transfer(0xF0); - _spi.transfer((address & 0xFF00) >> 8); - _spi.transfer((address & 0x00FF) >> 0); - _spi.transfer(wb); // Data write (write 1byte data) + _spi.transfer((address & 0xFF00) >> 8); + _spi.transfer((address & 0x00FF) >> 0); + _spi.transfer(wb); // Data write (write 1byte data) wizchip_cs_deselect(); } void Wiznet5100::wizchip_write_word(uint16_t address, uint16_t word) { wizchip_write(address, (uint8_t)(word >> 8)); - wizchip_write(address + 1, (uint8_t) word); + wizchip_write(address + 1, (uint8_t)word); } void Wiznet5100::wizchip_write_buf(uint16_t address, const uint8_t* pBuf, uint16_t len) @@ -94,7 +92,8 @@ void Wiznet5100::setSn_CR(uint8_t cr) wizchip_write(Sn_CR, cr); // Now wait for the command to complete - while (wizchip_read(Sn_CR)); + while (wizchip_read(Sn_CR)) + ; } uint16_t Wiznet5100::getSn_TX_FSR() @@ -111,7 +110,6 @@ uint16_t Wiznet5100::getSn_TX_FSR() return val; } - uint16_t Wiznet5100::getSn_RX_RSR() { uint16_t val = 0, val1 = 0; @@ -126,7 +124,7 @@ uint16_t Wiznet5100::getSn_RX_RSR() return val; } -void Wiznet5100::wizchip_send_data(const uint8_t *wizdata, uint16_t len) +void Wiznet5100::wizchip_send_data(const uint8_t* wizdata, uint16_t len) { uint16_t ptr; uint16_t size; @@ -136,14 +134,14 @@ void Wiznet5100::wizchip_send_data(const uint8_t *wizdata, uint16_t len) ptr = getSn_TX_WR(); dst_mask = ptr & TxBufferMask; - dst_ptr = TxBufferAddress + dst_mask; + dst_ptr = TxBufferAddress + dst_mask; if (dst_mask + len > TxBufferLength) { size = TxBufferLength - dst_mask; wizchip_write_buf(dst_ptr, wizdata, size); wizdata += size; - size = len - size; + size = len - size; dst_ptr = TxBufferAddress; wizchip_write_buf(dst_ptr, wizdata, size); } @@ -157,7 +155,7 @@ void Wiznet5100::wizchip_send_data(const uint8_t *wizdata, uint16_t len) setSn_TX_WR(ptr); } -void Wiznet5100::wizchip_recv_data(uint8_t *wizdata, uint16_t len) +void Wiznet5100::wizchip_recv_data(uint8_t* wizdata, uint16_t len) { uint16_t ptr; uint16_t size; @@ -167,15 +165,14 @@ void Wiznet5100::wizchip_recv_data(uint8_t *wizdata, uint16_t len) ptr = getSn_RX_RD(); src_mask = ptr & RxBufferMask; - src_ptr = RxBufferAddress + src_mask; - + src_ptr = RxBufferAddress + src_mask; if ((src_mask + len) > RxBufferLength) { size = RxBufferLength - src_mask; wizchip_read_buf(src_ptr, wizdata, size); wizdata += size; - size = len - size; + size = len - size; src_ptr = RxBufferAddress; wizchip_read_buf(src_ptr, wizdata, size); } @@ -201,19 +198,17 @@ void Wiznet5100::wizchip_recv_ignore(uint16_t len) void Wiznet5100::wizchip_sw_reset() { setMR(MR_RST); - getMR(); // for delay + getMR(); // for delay setSHAR(_mac_address); } - -Wiznet5100::Wiznet5100(int8_t cs, SPIClass& spi, int8_t intr): - _spi(spi), _cs(cs) +Wiznet5100::Wiznet5100(int8_t cs, SPIClass& spi, int8_t intr) : _spi(spi), _cs(cs) { (void)intr; } -boolean Wiznet5100::begin(const uint8_t *mac_address) +boolean Wiznet5100::begin(const uint8_t* mac_address) { memcpy(_mac_address, mac_address, 6); @@ -257,10 +252,11 @@ void Wiznet5100::end() setSn_IR(0xFF); // Wait for socket to change to closed - while (getSn_SR() != SOCK_CLOSED); + while (getSn_SR() != SOCK_CLOSED) + ; } -uint16_t Wiznet5100::readFrame(uint8_t *buffer, uint16_t bufsize) +uint16_t Wiznet5100::readFrame(uint8_t* buffer, uint16_t bufsize) { uint16_t data_len = readFrameSize(); @@ -288,7 +284,7 @@ uint16_t Wiznet5100::readFrameSize() return 0; } - uint8_t head[2]; + uint8_t head[2]; uint16_t data_len = 0; wizchip_recv_data(head, 2); @@ -307,7 +303,7 @@ void Wiznet5100::discardFrame(uint16_t framesize) setSn_CR(Sn_CR_RECV); } -uint16_t Wiznet5100::readFrameData(uint8_t *buffer, uint16_t framesize) +uint16_t Wiznet5100::readFrameData(uint8_t* buffer, uint16_t framesize) { wizchip_recv_data(buffer, framesize); setSn_CR(Sn_CR_RECV); @@ -329,7 +325,7 @@ uint16_t Wiznet5100::readFrameData(uint8_t *buffer, uint16_t framesize) #endif } -uint16_t Wiznet5100::sendFrame(const uint8_t *buf, uint16_t len) +uint16_t Wiznet5100::sendFrame(const uint8_t* buf, uint16_t len) { // Wait for space in the transmit buffer while (1) diff --git a/libraries/lwIP_w5100/src/utility/w5100.h b/libraries/lwIP_w5100/src/utility/w5100.h index 300cb334a4..9ed1e4cdab 100644 --- a/libraries/lwIP_w5100/src/utility/w5100.h +++ b/libraries/lwIP_w5100/src/utility/w5100.h @@ -32,17 +32,15 @@ // original sources: https://github.com/njh/W5100MacRaw -#ifndef W5100_H -#define W5100_H +#ifndef W5100_H +#define W5100_H #include #include #include - class Wiznet5100 { - public: /** Constructor that uses the default hardware SPI pins @@ -57,7 +55,7 @@ class Wiznet5100 @param address the local MAC address for the Ethernet interface @return Returns true if setting up the Ethernet interface was successful */ - boolean begin(const uint8_t *address); + boolean begin(const uint8_t* address); /** Shut down the Ethernet controlled @@ -70,7 +68,7 @@ class Wiznet5100 @param datalen the length of the data in the packet @return the number of bytes transmitted */ - uint16_t sendFrame(const uint8_t *data, uint16_t datalen); + uint16_t sendFrame(const uint8_t* data, uint16_t datalen); /** Read an Ethernet frame @@ -79,10 +77,27 @@ class Wiznet5100 @return the length of the received packet or 0 if no packet was received */ - uint16_t readFrame(uint8_t *buffer, uint16_t bufsize); + uint16_t readFrame(uint8_t* buffer, uint16_t bufsize); -protected: + /** + 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() { return false; @@ -108,30 +123,32 @@ class Wiznet5100 @param buffer a pointer to a buffer to write the frame to @param framesize readFrameSize()'s result @return the length of the received frame - or 0 if a problem occured + or 0 if a problem occurred */ - uint16_t readFrameData(uint8_t *frame, uint16_t framesize); - + uint16_t readFrameData(uint8_t* frame, uint16_t framesize); private: - static const uint16_t TxBufferAddress = 0x4000; /* Internal Tx buffer address of the iinchip */ - static const uint16_t RxBufferAddress = 0x6000; /* Internal Rx buffer address of the iinchip */ - static const uint8_t TxBufferSize = 0x3; /* Buffer size configuration: 0=1kb, 1=2kB, 2=4kB, 3=8kB */ - static const uint8_t RxBufferSize = 0x3; /* Buffer size configuration: 0=1kb, 1=2kB, 2=4kB, 3=8kB */ - static const uint16_t TxBufferLength = (1 << TxBufferSize) << 10; /* Length of Tx buffer in bytes */ - static const uint16_t RxBufferLength = (1 << RxBufferSize) << 10; /* Length of Rx buffer in bytes */ + static const uint16_t TxBufferAddress = 0x4000; /* Internal Tx buffer address of the iinchip */ + static const uint16_t RxBufferAddress = 0x6000; /* Internal Rx buffer address of the iinchip */ + static const uint8_t TxBufferSize + = 0x3; /* Buffer size configuration: 0=1kb, 1=2kB, 2=4kB, 3=8kB */ + static const uint8_t RxBufferSize + = 0x3; /* Buffer size configuration: 0=1kb, 1=2kB, 2=4kB, 3=8kB */ + static const uint16_t TxBufferLength = (1 << TxBufferSize) + << 10; /* Length of Tx buffer in bytes */ + static const uint16_t RxBufferLength = (1 << RxBufferSize) + << 10; /* Length of Rx buffer in bytes */ static const uint16_t TxBufferMask = TxBufferLength - 1; static const uint16_t RxBufferMask = RxBufferLength - 1; - SPIClass& _spi; - int8_t _cs; - uint8_t _mac_address[6]; + int8_t _cs; + uint8_t _mac_address[6]; /** Default function to select chip. - @note This function help not to access wrong address. If you do not describe this function or register any functions, - null function is called. + @note This function help not to access wrong address. If you do not describe this function + or register any functions, null function is called. */ inline void wizchip_cs_select() { @@ -140,8 +157,8 @@ class Wiznet5100 /** Default function to deselect chip. - @note This function help not to access wrong address. If you do not describe this function or register any functions, - null function is called. + @note This function help not to access wrong address. If you do not describe this function + or register any functions, null function is called. */ inline void wizchip_cs_deselect() { @@ -194,7 +211,6 @@ class Wiznet5100 */ void wizchip_write_buf(uint16_t address, const uint8_t* pBuf, uint16_t len); - /** Reset WIZCHIP by softly. */ @@ -204,15 +220,15 @@ class Wiznet5100 It copies data to internal TX memory @details This function reads the Tx write pointer register and after that, - it copies the wizdata(pointer buffer) of the length of len(variable) bytes to internal TX memory - and updates the Tx write pointer register. - This function is being called by send() and sendto() function also. + it copies the wizdata(pointer buffer) of the length of len(variable) bytes to + internal TX memory and updates the Tx write pointer register. This function is being called + by send() and sendto() function also. @param wizdata Pointer buffer to write data @param len Data length @sa wizchip_recv_data() */ - void wizchip_send_data(const uint8_t *wizdata, uint16_t len); + void wizchip_send_data(const uint8_t* wizdata, uint16_t len); /** It copies data to your buffer from internal RX memory @@ -226,11 +242,12 @@ class Wiznet5100 @param len Data length @sa wizchip_send_data() */ - void wizchip_recv_data(uint8_t *wizdata, uint16_t len); + void wizchip_recv_data(uint8_t* wizdata, uint16_t len); /** It discard the received data in RX memory. - @details It discards the data of the length of len(variable) bytes in internal RX memory. + @details It discards the data of the length of len(variable) bytes in internal RX + memory. @param len Data length */ void wizchip_recv_ignore(uint16_t len); @@ -247,106 +264,105 @@ class Wiznet5100 */ uint16_t getSn_RX_RSR(); - /** Common registers */ enum { - MR = 0x0000, ///< Mode Register address (R/W) - GAR = 0x0001, ///< Gateway IP Register address (R/W) - SUBR = 0x0005, ///< Subnet mask Register address (R/W) - SHAR = 0x0009, ///< Source MAC Register address (R/W) - SIPR = 0x000F, ///< Source IP Register address (R/W) - IR = 0x0015, ///< Interrupt Register (R/W) - IMR = 0x0016, ///< Socket Interrupt Mask Register (R/W) - RTR = 0x0017, ///< Timeout register address (1 is 100us) (R/W) - RCR = 0x0019, ///< Retry count register (R/W) - RMSR = 0x001A, ///< Receive Memory Size - TMSR = 0x001B, ///< Transmit Memory Size + MR = 0x0000, ///< Mode Register address (R/W) + GAR = 0x0001, ///< Gateway IP Register address (R/W) + SUBR = 0x0005, ///< Subnet mask Register address (R/W) + SHAR = 0x0009, ///< Source MAC Register address (R/W) + SIPR = 0x000F, ///< Source IP Register address (R/W) + IR = 0x0015, ///< Interrupt Register (R/W) + IMR = 0x0016, ///< Socket Interrupt Mask Register (R/W) + RTR = 0x0017, ///< Timeout register address (1 is 100us) (R/W) + RCR = 0x0019, ///< Retry count register (R/W) + RMSR = 0x001A, ///< Receive Memory Size + TMSR = 0x001B, ///< Transmit Memory Size }; /** Socket registers */ enum { - Sn_MR = 0x0400, ///< Socket Mode register(R/W) - Sn_CR = 0x0401, ///< Socket command register (R/W) - Sn_IR = 0x0402, ///< Socket interrupt register (R) - Sn_SR = 0x0403, ///< Socket status register (R) - Sn_PORT = 0x0404, ///< Source port register (R/W) - Sn_DHAR = 0x0406, ///< Peer MAC register address (R/W) - Sn_DIPR = 0x040C, ///< Peer IP register address (R/W) - Sn_DPORT = 0x0410, ///< Peer port register address (R/W) - Sn_MSSR = 0x0412, ///< Maximum Segment Size(Sn_MSSR0) register address (R/W) - Sn_PROTO = 0x0414, ///< IP Protocol(PROTO) Register (R/W) - Sn_TOS = 0x0415, ///< IP Type of Service(TOS) Register (R/W) - Sn_TTL = 0x0416, ///< IP Time to live(TTL) Register (R/W) - Sn_TX_FSR = 0x0420, ///< Transmit free memory size register (R) - Sn_TX_RD = 0x0422, ///< Transmit memory read pointer register address (R) - Sn_TX_WR = 0x0424, ///< Transmit memory write pointer register address (R/W) - Sn_RX_RSR = 0x0426, ///< Received data size register (R) - Sn_RX_RD = 0x0428, ///< Read point of Receive memory (R/W) - Sn_RX_WR = 0x042A, ///< Write point of Receive memory (R) + Sn_MR = 0x0400, ///< Socket Mode register(R/W) + Sn_CR = 0x0401, ///< Socket command register (R/W) + Sn_IR = 0x0402, ///< Socket interrupt register (R) + Sn_SR = 0x0403, ///< Socket status register (R) + Sn_PORT = 0x0404, ///< Source port register (R/W) + Sn_DHAR = 0x0406, ///< Peer MAC register address (R/W) + Sn_DIPR = 0x040C, ///< Peer IP register address (R/W) + Sn_DPORT = 0x0410, ///< Peer port register address (R/W) + Sn_MSSR = 0x0412, ///< Maximum Segment Size(Sn_MSSR0) register address (R/W) + Sn_PROTO = 0x0414, ///< IP Protocol(PROTO) Register (R/W) + Sn_TOS = 0x0415, ///< IP Type of Service(TOS) Register (R/W) + Sn_TTL = 0x0416, ///< IP Time to live(TTL) Register (R/W) + Sn_TX_FSR = 0x0420, ///< Transmit free memory size register (R) + Sn_TX_RD = 0x0422, ///< Transmit memory read pointer register address (R) + Sn_TX_WR = 0x0424, ///< Transmit memory write pointer register address (R/W) + Sn_RX_RSR = 0x0426, ///< Received data size register (R) + Sn_RX_RD = 0x0428, ///< Read point of Receive memory (R/W) + Sn_RX_WR = 0x042A, ///< Write point of Receive memory (R) }; /** Mode register values */ enum { - MR_RST = 0x80, ///< Reset - MR_PB = 0x10, ///< Ping block - MR_AI = 0x02, ///< Address Auto-Increment in Indirect Bus Interface - MR_IND = 0x01, ///< Indirect Bus Interface mode + MR_RST = 0x80, ///< Reset + MR_PB = 0x10, ///< Ping block + MR_AI = 0x02, ///< Address Auto-Increment in Indirect Bus Interface + MR_IND = 0x01, ///< Indirect Bus Interface mode }; /** Socket Mode Register values @ref Sn_MR */ enum { - Sn_MR_CLOSE = 0x00, ///< Unused socket - Sn_MR_TCP = 0x01, ///< TCP - Sn_MR_UDP = 0x02, ///< UDP - Sn_MR_IPRAW = 0x03, ///< IP LAYER RAW SOCK - Sn_MR_MACRAW = 0x04, ///< MAC LAYER RAW SOCK - Sn_MR_ND = 0x20, ///< No Delayed Ack(TCP) flag - Sn_MR_MF = 0x40, ///< Use MAC filter - Sn_MR_MULTI = 0x80, ///< support multicating + Sn_MR_CLOSE = 0x00, ///< Unused socket + Sn_MR_TCP = 0x01, ///< TCP + Sn_MR_UDP = 0x02, ///< UDP + Sn_MR_IPRAW = 0x03, ///< IP LAYER RAW SOCK + Sn_MR_MACRAW = 0x04, ///< MAC LAYER RAW SOCK + Sn_MR_ND = 0x20, ///< No Delayed Ack(TCP) flag + Sn_MR_MF = 0x40, ///< Use MAC filter + Sn_MR_MULTI = 0x80, ///< support multicating }; /** Socket Command Register values */ enum { - Sn_CR_OPEN = 0x01, ///< Initialise or open socket - Sn_CR_CLOSE = 0x10, ///< Close socket - Sn_CR_SEND = 0x20, ///< Update TX buffer pointer and send data - Sn_CR_SEND_MAC = 0x21, ///< Send data with MAC address, so without ARP process - Sn_CR_SEND_KEEP = 0x22, ///< Send keep alive message - Sn_CR_RECV = 0x40, ///< Update RX buffer pointer and receive data + Sn_CR_OPEN = 0x01, ///< Initialise or open socket + Sn_CR_CLOSE = 0x10, ///< Close socket + Sn_CR_SEND = 0x20, ///< Update TX buffer pointer and send data + Sn_CR_SEND_MAC = 0x21, ///< Send data with MAC address, so without ARP process + Sn_CR_SEND_KEEP = 0x22, ///< Send keep alive message + Sn_CR_RECV = 0x40, ///< Update RX buffer pointer and receive data }; /** Socket Interrupt register values */ enum { - Sn_IR_CON = 0x01, ///< CON Interrupt - Sn_IR_DISCON = 0x02, ///< DISCON Interrupt - Sn_IR_RECV = 0x04, ///< RECV Interrupt + Sn_IR_CON = 0x01, ///< CON Interrupt + Sn_IR_DISCON = 0x02, ///< DISCON Interrupt + Sn_IR_RECV = 0x04, ///< RECV Interrupt Sn_IR_TIMEOUT = 0x08, ///< TIMEOUT Interrupt - Sn_IR_SENDOK = 0x10, ///< SEND_OK Interrupt + Sn_IR_SENDOK = 0x10, ///< SEND_OK Interrupt }; /** Socket Status Register values */ enum { - SOCK_CLOSED = 0x00, ///< Closed - SOCK_INIT = 0x13, ///< Initiate state - SOCK_LISTEN = 0x14, ///< Listen state - SOCK_SYNSENT = 0x15, ///< Connection state - SOCK_SYNRECV = 0x16, ///< Connection state - SOCK_ESTABLISHED = 0x17, ///< Success to connect - SOCK_FIN_WAIT = 0x18, ///< Closing state - SOCK_CLOSING = 0x1A, ///< Closing state - SOCK_TIME_WAIT = 0x1B, ///< Closing state - SOCK_CLOSE_WAIT = 0x1C, ///< Closing state - SOCK_LAST_ACK = 0x1D, ///< Closing state - SOCK_UDP = 0x22, ///< UDP socket - SOCK_IPRAW = 0x32, ///< IP raw mode socket - SOCK_MACRAW = 0x42, ///< MAC raw mode socket + SOCK_CLOSED = 0x00, ///< Closed + SOCK_INIT = 0x13, ///< Initiate state + SOCK_LISTEN = 0x14, ///< Listen state + SOCK_SYNSENT = 0x15, ///< Connection state + SOCK_SYNRECV = 0x16, ///< Connection state + SOCK_ESTABLISHED = 0x17, ///< Success to connect + SOCK_FIN_WAIT = 0x18, ///< Closing state + SOCK_CLOSING = 0x1A, ///< Closing state + SOCK_TIME_WAIT = 0x1B, ///< Closing state + SOCK_CLOSE_WAIT = 0x1C, ///< Closing state + SOCK_LAST_ACK = 0x1D, ///< Closing state + SOCK_UDP = 0x22, ///< UDP socket + SOCK_IPRAW = 0x32, ///< IP raw mode socket + SOCK_MACRAW = 0x42, ///< MAC raw mode socket }; /** @@ -371,7 +387,8 @@ class Wiznet5100 /** Set local MAC address - @param (uint8_t*)shar Pointer variable to set local MAC address. It should be allocated 6 bytes. + @param (uint8_t*)shar Pointer variable to set local MAC address. It should be allocated 6 + bytes. @sa getSHAR() */ inline void setSHAR(const uint8_t* macaddr) @@ -381,7 +398,8 @@ class Wiznet5100 /** Get local MAC address - @param (uint8_t*)shar Pointer variable to get local MAC address. It should be allocated 6 bytes. + @param (uint8_t*)shar Pointer variable to get local MAC address. It should be allocated 6 + bytes. @sa setSHAR() */ inline void getSHAR(uint8_t* macaddr) @@ -496,4 +514,4 @@ class Wiznet5100 } }; -#endif // W5100_H +#endif // W5100_H diff --git a/libraries/lwIP_w5500/examples/TCPClient/TCPClient.ino b/libraries/lwIP_w5500/examples/TCPClient/TCPClient.ino deleted file mode 100644 index 56dcb8092b..0000000000 --- a/libraries/lwIP_w5500/examples/TCPClient/TCPClient.ino +++ /dev/null @@ -1,102 +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) -#include // ESP8266WiFiClass::preinitWiFiOff() - -const char* host = "djxmmx.net"; -const uint16_t port = 17; - -using TCPClient = WiFiClient; - -#define CSPIN 16 // wemos/lolin/nodemcu D0 -Wiznet5500lwIP eth(CSPIN); - -void preinit() { - // (no C++ in function) - // disable wifi - ESP8266WiFiClass::preinitWiFiOff(); -} - -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/library.properties b/libraries/lwIP_w5500/library.properties index 8cb76d861d..de0c87eafe 100644 --- a/libraries/lwIP_w5500/library.properties +++ b/libraries/lwIP_w5500/library.properties @@ -4,7 +4,7 @@ author=Nicholas Humfrey maintainer=esp8266/Arduino sentence=Ethernet driver paragraph=Wiznet5500 ethernet drivers for lwIP and esp8266 Arduino from https://github.com/njh/W5500MacRaw -category=Network +category=Communication url=https://github.com/esp8266/Arduino architectures=esp8266 dot_a_linkage=true diff --git a/libraries/lwIP_w5500/src/W5500lwIP.h b/libraries/lwIP_w5500/src/W5500lwIP.h index 1e8f201331..8b708cbf90 100644 --- a/libraries/lwIP_w5500/src/W5500lwIP.h +++ b/libraries/lwIP_w5500/src/W5500lwIP.h @@ -7,4 +7,4 @@ using Wiznet5500lwIP = LwipIntfDev; -#endif // _W5500LWIP_H +#endif // _W5500LWIP_H diff --git a/libraries/lwIP_w5500/src/utility/w5500.cpp b/libraries/lwIP_w5500/src/utility/w5500.cpp index b3c3ce0162..978cf9e74c 100644 --- a/libraries/lwIP_w5500/src/utility/w5500.cpp +++ b/libraries/lwIP_w5500/src/utility/w5500.cpp @@ -35,7 +35,6 @@ #include #include "w5500.h" - uint8_t Wiznet5500::wizchip_read(uint8_t block, uint16_t address) { uint8_t ret; @@ -95,10 +94,11 @@ void Wiznet5500::wizchip_write(uint8_t block, uint16_t address, uint8_t wb) void Wiznet5500::wizchip_write_word(uint8_t block, uint16_t address, uint16_t word) { wizchip_write(block, address, (uint8_t)(word >> 8)); - wizchip_write(block, address + 1, (uint8_t) word); + wizchip_write(block, address + 1, (uint8_t)word); } -void Wiznet5500::wizchip_write_buf(uint8_t block, uint16_t address, const uint8_t* pBuf, uint16_t len) +void Wiznet5500::wizchip_write_buf(uint8_t block, uint16_t address, const uint8_t* pBuf, + uint16_t len) { uint16_t i; @@ -123,7 +123,8 @@ void Wiznet5500::setSn_CR(uint8_t cr) wizchip_write(BlockSelectSReg, Sn_CR, cr); // Now wait for the command to complete - while (wizchip_read(BlockSelectSReg, Sn_CR)); + while (wizchip_read(BlockSelectSReg, Sn_CR)) + ; } uint16_t Wiznet5500::getSn_TX_FSR() @@ -140,7 +141,6 @@ uint16_t Wiznet5500::getSn_TX_FSR() return val; } - uint16_t Wiznet5500::getSn_RX_RSR() { uint16_t val = 0, val1 = 0; @@ -155,7 +155,7 @@ uint16_t Wiznet5500::getSn_RX_RSR() return val; } -void Wiznet5500::wizchip_send_data(const uint8_t *wizdata, uint16_t len) +void Wiznet5500::wizchip_send_data(const uint8_t* wizdata, uint16_t len) { uint16_t ptr = 0; @@ -171,7 +171,7 @@ void Wiznet5500::wizchip_send_data(const uint8_t *wizdata, uint16_t len) setSn_TX_WR(ptr); } -void Wiznet5500::wizchip_recv_data(uint8_t *wizdata, uint16_t len) +void Wiznet5500::wizchip_recv_data(uint8_t* wizdata, uint16_t len) { uint16_t ptr; @@ -198,7 +198,7 @@ void Wiznet5500::wizchip_recv_ignore(uint16_t len) void Wiznet5500::wizchip_sw_reset() { setMR(MR_RST); - getMR(); // for delay + getMR(); // for delay setSHAR(_mac_address); } @@ -244,7 +244,7 @@ void Wiznet5500::wizphy_reset() int8_t Wiznet5500::wizphy_setphypmode(uint8_t pmode) { uint8_t tmp = 0; - tmp = getPHYCFGR(); + tmp = getPHYCFGR(); if ((tmp & PHYCFGR_OPMD) == 0) { return -1; @@ -278,14 +278,12 @@ int8_t Wiznet5500::wizphy_setphypmode(uint8_t pmode) return -1; } - -Wiznet5500::Wiznet5500(int8_t cs, SPIClass& spi, int8_t intr): - _spi(spi), _cs(cs) +Wiznet5500::Wiznet5500(int8_t cs, SPIClass& spi, int8_t intr) : _spi(spi), _cs(cs) { (void)intr; } -boolean Wiznet5500::begin(const uint8_t *mac_address) +boolean Wiznet5500::begin(const uint8_t* mac_address) { memcpy(_mac_address, mac_address, 6); @@ -329,10 +327,11 @@ void Wiznet5500::end() setSn_IR(0xFF); // Wait for socket to change to closed - while (getSn_SR() != SOCK_CLOSED); + while (getSn_SR() != SOCK_CLOSED) + ; } -uint16_t Wiznet5500::readFrame(uint8_t *buffer, uint16_t bufsize) +uint16_t Wiznet5500::readFrame(uint8_t* buffer, uint16_t bufsize) { uint16_t data_len = readFrameSize(); @@ -360,7 +359,7 @@ uint16_t Wiznet5500::readFrameSize() return 0; } - uint8_t head[2]; + uint8_t head[2]; uint16_t data_len = 0; wizchip_recv_data(head, 2); @@ -379,7 +378,7 @@ void Wiznet5500::discardFrame(uint16_t framesize) setSn_CR(Sn_CR_RECV); } -uint16_t Wiznet5500::readFrameData(uint8_t *buffer, uint16_t framesize) +uint16_t Wiznet5500::readFrameData(uint8_t* buffer, uint16_t framesize) { wizchip_recv_data(buffer, framesize); setSn_CR(Sn_CR_RECV); @@ -402,7 +401,7 @@ uint16_t Wiznet5500::readFrameData(uint8_t *buffer, uint16_t framesize) #endif } -uint16_t Wiznet5500::sendFrame(const uint8_t *buf, uint16_t len) +uint16_t Wiznet5500::sendFrame(const uint8_t* buf, uint16_t len) { // Wait for space in the transmit buffer while (1) diff --git a/libraries/lwIP_w5500/src/utility/w5500.h b/libraries/lwIP_w5500/src/utility/w5500.h index 428689b8e5..7e8058fdb1 100644 --- a/libraries/lwIP_w5500/src/utility/w5500.h +++ b/libraries/lwIP_w5500/src/utility/w5500.h @@ -39,11 +39,8 @@ #include #include - - class Wiznet5500 { - public: /** Constructor that uses the default hardware SPI pins @@ -51,7 +48,6 @@ class Wiznet5500 */ Wiznet5500(int8_t cs = SS, SPIClass& spi = SPI, int8_t intr = -1); - /** Initialise the Ethernet controller Must be called before sending or receiving Ethernet frames @@ -59,7 +55,7 @@ class Wiznet5500 @param address the local MAC address for the Ethernet interface @return Returns true if setting up the Ethernet interface was successful */ - boolean begin(const uint8_t *address); + boolean begin(const uint8_t* address); /** Shut down the Ethernet controlled @@ -72,7 +68,7 @@ class Wiznet5500 @param datalen the length of the data in the packet @return the number of bytes transmitted */ - uint16_t sendFrame(const uint8_t *data, uint16_t datalen); + uint16_t sendFrame(const uint8_t* data, uint16_t datalen); /** Read an Ethernet frame @@ -81,10 +77,27 @@ class Wiznet5500 @return the length of the received packet or 0 if no packet was received */ - uint16_t readFrame(uint8_t *buffer, uint16_t bufsize); + uint16_t readFrame(uint8_t* buffer, uint16_t bufsize); -protected: + /** + 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() { return false; @@ -110,13 +123,11 @@ class Wiznet5500 @param buffer a pointer to a buffer to write the frame to @param framesize readFrameSize()'s result @return the length of the received frame - or 0 if a problem occured + or 0 if a problem occurred */ - uint16_t readFrameData(uint8_t *frame, uint16_t framesize); - + uint16_t readFrameData(uint8_t* frame, uint16_t framesize); private: - //< SPI interface Read operation in Control Phase static const uint8_t AccessModeRead = (0x00 << 2); @@ -135,16 +146,14 @@ class Wiznet5500 //< Socket 0 Rx buffer address block static const uint8_t BlockSelectRxBuf = (0x03 << 3); - - SPIClass& _spi; - int8_t _cs; - uint8_t _mac_address[6]; + int8_t _cs; + uint8_t _mac_address[6]; /** Default function to select chip. - @note This function help not to access wrong address. If you do not describe this function or register any functions, - null function is called. + @note This function help not to access wrong address. If you do not describe this function + or register any functions, null function is called. */ inline void wizchip_cs_select() { @@ -153,8 +162,8 @@ class Wiznet5500 /** Default function to deselect chip. - @note This function help not to access wrong address. If you do not describe this function or register any functions, - null function is called. + @note This function help not to access wrong address. If you do not describe this function + or register any functions, null function is called. */ inline void wizchip_cs_deselect() { @@ -163,8 +172,8 @@ class Wiznet5500 /** Default function to read in SPI interface. - @note This function help not to access wrong address. If you do not describe this function or register any functions, - null function is called. + @note This function help not to access wrong address. If you do not describe this function + or register any functions, null function is called. */ inline uint8_t wizchip_spi_read_byte() { @@ -173,15 +182,14 @@ class Wiznet5500 /** Default function to write in SPI interface. - @note This function help not to access wrong address. If you do not describe this function or register any functions, - null function is called. + @note This function help not to access wrong address. If you do not describe this function + or register any functions, null function is called. */ inline void wizchip_spi_write_byte(uint8_t wb) { _spi.transfer(wb); } - /** Read a 1 byte value from a register. @param address Register address @@ -240,7 +248,6 @@ class Wiznet5500 */ uint16_t getSn_RX_RSR(); - /** Reset WIZCHIP by softly. */ @@ -262,7 +269,8 @@ class Wiznet5500 void wizphy_reset(); /** - set the power mode of phy inside WIZCHIP. Refer to @ref PHYCFGR in W5500, @ref PHYSTATUS in W5200 + set the power mode of phy inside WIZCHIP. Refer to @ref PHYCFGR in W5500, @ref PHYSTATUS in + W5200 @param pmode Settig value of power down mode. */ int8_t wizphy_setphypmode(uint8_t pmode); @@ -271,15 +279,15 @@ class Wiznet5500 It copies data to internal TX memory @details This function reads the Tx write pointer register and after that, - it copies the wizdata(pointer buffer) of the length of len(variable) bytes to internal TX memory - and updates the Tx write pointer register. - This function is being called by send() and sendto() function also. + it copies the wizdata(pointer buffer) of the length of len(variable) bytes to + internal TX memory and updates the Tx write pointer register. This function is being called + by send() and sendto() function also. @param wizdata Pointer buffer to write data @param len Data length @sa wizchip_recv_data() */ - void wizchip_send_data(const uint8_t *wizdata, uint16_t len); + void wizchip_send_data(const uint8_t* wizdata, uint16_t len); /** It copies data to your buffer from internal RX memory @@ -293,183 +301,180 @@ class Wiznet5500 @param len Data length @sa wizchip_send_data() */ - void wizchip_recv_data(uint8_t *wizdata, uint16_t len); + void wizchip_recv_data(uint8_t* wizdata, uint16_t len); /** It discard the received data in RX memory. - @details It discards the data of the length of len(variable) bytes in internal RX memory. + @details It discards the data of the length of len(variable) bytes in internal RX + memory. @param len Data length */ void wizchip_recv_ignore(uint16_t len); - - /** Common registers */ enum { - MR = 0x0000, ///< Mode Register address (R/W) - SHAR = 0x0009, ///< Source MAC Register address (R/W) + MR = 0x0000, ///< Mode Register address (R/W) + SHAR = 0x0009, ///< Source MAC Register address (R/W) INTLEVEL = 0x0013, ///< Set Interrupt low level timer register address (R/W) - IR = 0x0015, ///< Interrupt Register (R/W) - _IMR_ = 0x0016, ///< Interrupt mask register (R/W) - SIR = 0x0017, ///< Socket Interrupt Register (R/W) - SIMR = 0x0018, ///< Socket Interrupt Mask Register (R/W) - _RTR_ = 0x0019, ///< Timeout register address (1 is 100us) (R/W) - _RCR_ = 0x001B, ///< Retry count register (R/W) - UIPR = 0x0028, ///< Unreachable IP register address in UDP mode (R) - UPORTR = 0x002C, ///< Unreachable Port register address in UDP mode (R) - PHYCFGR = 0x002E, ///< PHY Status Register (R/W) + IR = 0x0015, ///< Interrupt Register (R/W) + _IMR_ = 0x0016, ///< Interrupt mask register (R/W) + SIR = 0x0017, ///< Socket Interrupt Register (R/W) + SIMR = 0x0018, ///< Socket Interrupt Mask Register (R/W) + _RTR_ = 0x0019, ///< Timeout register address (1 is 100us) (R/W) + _RCR_ = 0x001B, ///< Retry count register (R/W) + UIPR = 0x0028, ///< Unreachable IP register address in UDP mode (R) + UPORTR = 0x002C, ///< Unreachable Port register address in UDP mode (R) + PHYCFGR = 0x002E, ///< PHY Status Register (R/W) VERSIONR = 0x0039, ///< Chip version register address (R) }; /** Socket registers */ enum { - Sn_MR = 0x0000, ///< Socket Mode register (R/W) - Sn_CR = 0x0001, ///< Socket command register (R/W) - Sn_IR = 0x0002, ///< Socket interrupt register (R) - Sn_SR = 0x0003, ///< Socket status register (R) - Sn_PORT = 0x0004, ///< Source port register (R/W) - Sn_DHAR = 0x0006, ///< Peer MAC register address (R/W) - Sn_DIPR = 0x000C, ///< Peer IP register address (R/W) - Sn_DPORT = 0x0010, ///< Peer port register address (R/W) - Sn_MSSR = 0x0012, ///< Maximum Segment Size(Sn_MSSR0) register address (R/W) - Sn_TOS = 0x0015, ///< IP Type of Service(TOS) Register (R/W) - Sn_TTL = 0x0016, ///< IP Time to live(TTL) Register (R/W) + Sn_MR = 0x0000, ///< Socket Mode register (R/W) + Sn_CR = 0x0001, ///< Socket command register (R/W) + Sn_IR = 0x0002, ///< Socket interrupt register (R) + Sn_SR = 0x0003, ///< Socket status register (R) + Sn_PORT = 0x0004, ///< Source port register (R/W) + Sn_DHAR = 0x0006, ///< Peer MAC register address (R/W) + Sn_DIPR = 0x000C, ///< Peer IP register address (R/W) + Sn_DPORT = 0x0010, ///< Peer port register address (R/W) + Sn_MSSR = 0x0012, ///< Maximum Segment Size(Sn_MSSR0) register address (R/W) + Sn_TOS = 0x0015, ///< IP Type of Service(TOS) Register (R/W) + Sn_TTL = 0x0016, ///< IP Time to live(TTL) Register (R/W) Sn_RXBUF_SIZE = 0x001E, ///< Receive memory size register (R/W) Sn_TXBUF_SIZE = 0x001F, ///< Transmit memory size register (R/W) - Sn_TX_FSR = 0x0020, ///< Transmit free memory size register (R) - Sn_TX_RD = 0x0022, ///< Transmit memory read pointer register address (R) - Sn_TX_WR = 0x0024, ///< Transmit memory write pointer register address (R/W) - Sn_RX_RSR = 0x0026, ///< Received data size register (R) - Sn_RX_RD = 0x0028, ///< Read point of Receive memory (R/W) - Sn_RX_WR = 0x002A, ///< Write point of Receive memory (R) - Sn_IMR = 0x002C, ///< Socket interrupt mask register (R) - Sn_FRAG = 0x002D, ///< Fragment field value in IP header register (R/W) - Sn_KPALVTR = 0x002F, ///< Keep Alive Timer register (R/W) + Sn_TX_FSR = 0x0020, ///< Transmit free memory size register (R) + Sn_TX_RD = 0x0022, ///< Transmit memory read pointer register address (R) + Sn_TX_WR = 0x0024, ///< Transmit memory write pointer register address (R/W) + Sn_RX_RSR = 0x0026, ///< Received data size register (R) + Sn_RX_RD = 0x0028, ///< Read point of Receive memory (R/W) + Sn_RX_WR = 0x002A, ///< Write point of Receive memory (R) + Sn_IMR = 0x002C, ///< Socket interrupt mask register (R) + Sn_FRAG = 0x002D, ///< Fragment field value in IP header register (R/W) + Sn_KPALVTR = 0x002F, ///< Keep Alive Timer register (R/W) }; /** Mode register values */ enum { - MR_RST = 0x80, ///< Reset - MR_WOL = 0x20, ///< Wake on LAN - MR_PB = 0x10, ///< Ping block + MR_RST = 0x80, ///< Reset + MR_WOL = 0x20, ///< Wake on LAN + MR_PB = 0x10, ///< Ping block MR_PPPOE = 0x08, ///< Enable PPPoE - MR_FARP = 0x02, ///< Enable UDP_FORCE_ARP CHECHK + MR_FARP = 0x02, ///< Enable UDP_FORCE_ARP CHECK }; /* Interrupt Register values */ enum { IR_CONFLICT = 0x80, ///< Check IP conflict - IR_UNREACH = 0x40, ///< Get the destination unreachable message in UDP sending - IR_PPPoE = 0x20, ///< Get the PPPoE close message - IR_MP = 0x10, ///< Get the magic packet interrupt + IR_UNREACH = 0x40, ///< Get the destination unreachable message in UDP sending + IR_PPPoE = 0x20, ///< Get the PPPoE close message + IR_MP = 0x10, ///< Get the magic packet interrupt }; /* Interrupt Mask Register values */ enum { - IM_IR7 = 0x80, ///< IP Conflict Interrupt Mask - IM_IR6 = 0x40, ///< Destination unreachable Interrupt Mask - IM_IR5 = 0x20, ///< PPPoE Close Interrupt Mask - IM_IR4 = 0x10, ///< Magic Packet Interrupt Mask + IM_IR7 = 0x80, ///< IP Conflict Interrupt Mask + IM_IR6 = 0x40, ///< Destination unreachable Interrupt Mask + IM_IR5 = 0x20, ///< PPPoE Close Interrupt Mask + IM_IR4 = 0x10, ///< Magic Packet Interrupt Mask }; /** Socket Mode Register values @ref Sn_MR */ enum { - Sn_MR_CLOSE = 0x00, ///< Unused socket - Sn_MR_TCP = 0x01, ///< TCP - Sn_MR_UDP = 0x02, ///< UDP - Sn_MR_MACRAW = 0x04, ///< MAC LAYER RAW SOCK - Sn_MR_UCASTB = 0x10, ///< Unicast Block in UDP Multicasting - Sn_MR_ND = 0x20, ///< No Delayed Ack(TCP), Multicast flag - Sn_MR_BCASTB = 0x40, ///< Broadcast block in UDP Multicasting - Sn_MR_MULTI = 0x80, ///< Support UDP Multicasting - Sn_MR_MIP6B = 0x10, ///< IPv6 packet Blocking in @ref Sn_MR_MACRAW mode - Sn_MR_MMB = 0x20, ///< Multicast Blocking in @ref Sn_MR_MACRAW mode - Sn_MR_MFEN = 0x80, ///< MAC filter enable in @ref Sn_MR_MACRAW mode + Sn_MR_CLOSE = 0x00, ///< Unused socket + Sn_MR_TCP = 0x01, ///< TCP + Sn_MR_UDP = 0x02, ///< UDP + Sn_MR_MACRAW = 0x04, ///< MAC LAYER RAW SOCK + Sn_MR_UCASTB = 0x10, ///< Unicast Block in UDP Multicasting + Sn_MR_ND = 0x20, ///< No Delayed Ack(TCP), Multicast flag + Sn_MR_BCASTB = 0x40, ///< Broadcast block in UDP Multicasting + Sn_MR_MULTI = 0x80, ///< Support UDP Multicasting + Sn_MR_MIP6B = 0x10, ///< IPv6 packet Blocking in @ref Sn_MR_MACRAW mode + Sn_MR_MMB = 0x20, ///< Multicast Blocking in @ref Sn_MR_MACRAW mode + Sn_MR_MFEN = 0x80, ///< MAC filter enable in @ref Sn_MR_MACRAW mode }; /** Socket Command Register values */ enum { - Sn_CR_OPEN = 0x01, ///< Initialise or open socket - Sn_CR_LISTEN = 0x02, ///< Wait connection request in TCP mode (Server mode) - Sn_CR_CONNECT = 0x04, ///< Send connection request in TCP mode (Client mode) - Sn_CR_DISCON = 0x08, ///< Send closing request in TCP mode - Sn_CR_CLOSE = 0x10, ///< Close socket - Sn_CR_SEND = 0x20, ///< Update TX buffer pointer and send data - Sn_CR_SEND_MAC = 0x21, ///< Send data with MAC address, so without ARP process - Sn_CR_SEND_KEEP = 0x22, ///< Send keep alive message - Sn_CR_RECV = 0x40, ///< Update RX buffer pointer and receive data + Sn_CR_OPEN = 0x01, ///< Initialise or open socket + Sn_CR_LISTEN = 0x02, ///< Wait connection request in TCP mode (Server mode) + Sn_CR_CONNECT = 0x04, ///< Send connection request in TCP mode (Client mode) + Sn_CR_DISCON = 0x08, ///< Send closing request in TCP mode + Sn_CR_CLOSE = 0x10, ///< Close socket + Sn_CR_SEND = 0x20, ///< Update TX buffer pointer and send data + Sn_CR_SEND_MAC = 0x21, ///< Send data with MAC address, so without ARP process + Sn_CR_SEND_KEEP = 0x22, ///< Send keep alive message + Sn_CR_RECV = 0x40, ///< Update RX buffer pointer and receive data }; /** Socket Interrupt register values */ enum { - Sn_IR_CON = 0x01, ///< CON Interrupt - Sn_IR_DISCON = 0x02, ///< DISCON Interrupt - Sn_IR_RECV = 0x04, ///< RECV Interrupt + Sn_IR_CON = 0x01, ///< CON Interrupt + Sn_IR_DISCON = 0x02, ///< DISCON Interrupt + Sn_IR_RECV = 0x04, ///< RECV Interrupt Sn_IR_TIMEOUT = 0x08, ///< TIMEOUT Interrupt - Sn_IR_SENDOK = 0x10, ///< SEND_OK Interrupt + Sn_IR_SENDOK = 0x10, ///< SEND_OK Interrupt }; /** Socket Status Register values */ enum { - SOCK_CLOSED = 0x00, ///< Closed - SOCK_INIT = 0x13, ///< Initiate state - SOCK_LISTEN = 0x14, ///< Listen state - SOCK_SYNSENT = 0x15, ///< Connection state - SOCK_SYNRECV = 0x16, ///< Connection state - SOCK_ESTABLISHED = 0x17, ///< Success to connect - SOCK_FIN_WAIT = 0x18, ///< Closing state - SOCK_CLOSING = 0x1A, ///< Closing state - SOCK_TIME_WAIT = 0x1B, ///< Closing state - SOCK_CLOSE_WAIT = 0x1C, ///< Closing state - SOCK_LAST_ACK = 0x1D, ///< Closing state - SOCK_UDP = 0x22, ///< UDP socket - SOCK_MACRAW = 0x42, ///< MAC raw mode socket + SOCK_CLOSED = 0x00, ///< Closed + SOCK_INIT = 0x13, ///< Initiate state + SOCK_LISTEN = 0x14, ///< Listen state + SOCK_SYNSENT = 0x15, ///< Connection state + SOCK_SYNRECV = 0x16, ///< Connection state + SOCK_ESTABLISHED = 0x17, ///< Success to connect + SOCK_FIN_WAIT = 0x18, ///< Closing state + SOCK_CLOSING = 0x1A, ///< Closing state + SOCK_TIME_WAIT = 0x1B, ///< Closing state + SOCK_CLOSE_WAIT = 0x1C, ///< Closing state + SOCK_LAST_ACK = 0x1D, ///< Closing state + SOCK_UDP = 0x22, ///< UDP socket + SOCK_MACRAW = 0x42, ///< MAC raw mode socket }; - /* PHYCFGR register value */ enum { - PHYCFGR_RST = ~(1 << 7), //< For PHY reset, must operate AND mask. - PHYCFGR_OPMD = (1 << 6), // Configre PHY with OPMDC value - PHYCFGR_OPMDC_ALLA = (7 << 3), + PHYCFGR_RST = ~(1 << 7), //< For PHY reset, must operate AND mask. + PHYCFGR_OPMD = (1 << 6), // Configre PHY with OPMDC value + PHYCFGR_OPMDC_ALLA = (7 << 3), PHYCFGR_OPMDC_PDOWN = (6 << 3), - PHYCFGR_OPMDC_NA = (5 << 3), + PHYCFGR_OPMDC_NA = (5 << 3), PHYCFGR_OPMDC_100FA = (4 << 3), - PHYCFGR_OPMDC_100F = (3 << 3), - PHYCFGR_OPMDC_100H = (2 << 3), - PHYCFGR_OPMDC_10F = (1 << 3), - PHYCFGR_OPMDC_10H = (0 << 3), - PHYCFGR_DPX_FULL = (1 << 2), - PHYCFGR_DPX_HALF = (0 << 2), - PHYCFGR_SPD_100 = (1 << 1), - PHYCFGR_SPD_10 = (0 << 1), - PHYCFGR_LNK_ON = (1 << 0), - PHYCFGR_LNK_OFF = (0 << 0), + PHYCFGR_OPMDC_100F = (3 << 3), + PHYCFGR_OPMDC_100H = (2 << 3), + PHYCFGR_OPMDC_10F = (1 << 3), + PHYCFGR_OPMDC_10H = (0 << 3), + PHYCFGR_DPX_FULL = (1 << 2), + PHYCFGR_DPX_HALF = (0 << 2), + PHYCFGR_SPD_100 = (1 << 1), + PHYCFGR_SPD_10 = (0 << 1), + PHYCFGR_LNK_ON = (1 << 0), + PHYCFGR_LNK_OFF = (0 << 0), }; enum { - PHY_SPEED_10 = 0, ///< Link Speed 10 - PHY_SPEED_100 = 1, ///< Link Speed 100 + PHY_SPEED_10 = 0, ///< Link Speed 10 + PHY_SPEED_100 = 1, ///< Link Speed 100 PHY_DUPLEX_HALF = 0, ///< Link Half-Duplex PHY_DUPLEX_FULL = 1, ///< Link Full-Duplex - PHY_LINK_OFF = 0, ///< Link Off - PHY_LINK_ON = 1, ///< Link On - PHY_POWER_NORM = 0, ///< PHY power normal mode - PHY_POWER_DOWN = 1, ///< PHY power down mode + PHY_LINK_OFF = 0, ///< Link Off + PHY_LINK_ON = 1, ///< Link On + PHY_POWER_NORM = 0, ///< PHY power normal mode + PHY_POWER_DOWN = 1, ///< PHY power down mode }; - /** Set Mode Register @param (uint8_t)mr The value to be set. @@ -492,7 +497,8 @@ class Wiznet5500 /** Set local MAC address - @param (uint8_t*)shar Pointer variable to set local MAC address. It should be allocated 6 bytes. + @param (uint8_t*)shar Pointer variable to set local MAC address. It should be allocated 6 + bytes. @sa getSHAR() */ inline void setSHAR(const uint8_t* macaddr) @@ -502,7 +508,8 @@ class Wiznet5500 /** Get local MAC address - @param (uint8_t*)shar Pointer variable to get local MAC address. It should be allocated 6 bytes. + @param (uint8_t*)shar Pointer variable to get local MAC address. It should be allocated 6 + bytes. @sa setSHAR() */ inline void getSHAR(uint8_t* macaddr) @@ -764,4 +771,4 @@ class Wiznet5500 } }; -#endif // W5500_H +#endif // W5500_H diff --git a/package.json b/package.json index 20bad37f53..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.0.0-dev" + "version": "3.2.0-dev" } diff --git a/package/README.md b/package/README.md index 5c77d9f80c..b2d64ef76e 100644 --- a/package/README.md +++ b/package/README.md @@ -45,7 +45,7 @@ Here is a rough overview of the effective release process. See the section below 1. Release process effectively starts when a maintainer pushes a tag into the repository. -2. Travis CI runs a build for this tag, and one of the jobs (with `BUILD_TYPE=package`) is used to prepare the boards manager package. This job runs `build_boards_manager_package.sh`. +2. CI runs a build for this tag, and one of the jobs is used to prepare the boards manager package. This job runs `build_boards_manager_package.sh`. 3. `build_boards_manager_package.sh` does a few things to build the boards manager package (.zip) file and the json index: @@ -54,9 +54,9 @@ Here is a rough overview of the effective release process. See the section below * Generate package index for the new release. * Combines new release with previous releases in one json file (using `merge_packages.py` script). -4. Travis CI uploads boards manager package (.zip file) and package index (.json file) to Github Releases, creating a draft release at the same time. +4. CI uploads boards manager package (.zip file) and package index (.json file) to Github Releases, creating a draft release at the same time. -5. Travis CI also uploads package index .json file to `https://arduino.esp8266.com/stable/package_esp8266_index.json`, i.e. well-known URL used by most users. +5. CI also uploads package index .json file to `https://arduino.esp8266.com/stable/package_esp8266_index.json`, i.e. well-known URL used by most users. 6. When the draft release is created, maintainer edits release description and inserts changelog into the description field, unmarks the release as draft, and publishes the release. @@ -65,18 +65,18 @@ Here is a rough overview of the effective release process. See the section below ## Creating a release (for maintainers) -1. [Open a new issue](https://github.com/esp8266/Arduino/issues/new/choose) to track activities, which will be closed after the release is done. Copy the checklist below into it, and check the steps one by one as they get completed. +1. Make sure that no issues or PRs are assigned to the milestone to be released. If there are any Issues/PRs assigned to the relevant milestone, they should either be addressed, pushed back to a future milestone, or closed. -2. Make sure that no issues or PRs are assigned to the milestone to be released. If there are any Issues/PRs assigned to the relevant milestone, they should either be addressed, pushed back to a future milestone, or closed. +2. Open a new issue to track activities, which will be closed after the release is done. Copy the checklist below into it, and check the steps one by one as they get completed. 3. Assemble release notes. * 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: - + ``` Breaking Changes ================ @@ -99,34 +99,33 @@ 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. -4. Make a PR with the following, [wait for Travis CI](https://travis-ci.org/github/esp8266/Arduino/builds/), and merge. +4. Make a PR with the following, wait for CI, and merge. * [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` -5. Wait until the release notes have been checked by other maintainers + * Update SSL/TLS certificates and public keys in examples:\ + `$ cd tools; sh certsUpdate.sh` -6. Navigate to [Travis CI options](https://travis-ci.org/esp8266/Arduino/settings), enable ´Build pushed branches´ (before tagging in next step) +5. Wait until the release notes have been checked by other maintainers -7. Tag the latest commit on the master branch. In this project, tags have form `X.Y.Z`, e.g. `3.0.0`, or `X.Y.Z-betaN` for release candidate versions. Notice that there's no `v`at the beginning of the tag. Tags must be annotated, not lightweight tags. To create a tag, use git command (assuming that the master branch is checked out): +6. Tag the latest commit on the master branch. In this project, tags have form `X.Y.Z`, e.g. `3.0.0`, or `X.Y.Z-betaN` for release candidate versions. Notice that there's no `v`at the beginning of the tag. Tags must be annotated, not lightweight tags. To create a tag, use git command (assuming that the master branch is checked out): ``` git tag -a -m "Release 3.0.0" 3.0.0 ``` - navigate to [Travis CI options](https://travis-ci.org/esp8266/Arduino/settings), enable `Build pushed branches`, - - then push the tag created above to esp8266/Arduino Github repository: + push the tag created above to esp8266/Arduino Github repository: ``` git push origin 3.0.0 @@ -138,24 +137,21 @@ The following points assume work in a direct clone of the repository, and not in * Release must be deleted: github > releases > edit x.y.z > remove all files > delete button appears -8. Wait for Travis CI build for the tag to pass, see https://travis-ci.org/esp8266/Arduino/builds, - +7. Wait for CI build to pass -9. Check that the new (draft) release has been created (no editing at this point!), see https://github.com/esp8266/Arduino/releases. +8. Check that the new (draft) release has been created (no editing at this point!), see https://github.com/esp8266/Arduino/releases. -10. 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. -11. 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). +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). -12. Return to the [Travis CI options](https://travis-ci.org/esp8266/Arduino/settings) and disable `Build pushed branches`. +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. -13. 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. Close the milestone associated with the released version (the milestone should be empty per point 1 above) -14. 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) +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. -15. 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. - -16. Create a commit to the master branch, updating: +14. Create a commit to the master branch, updating: * The version in platform.txt and package.json files. This should correspond to the version of the *next* milestone, plus `-dev` suffix. E.g. `3.1.0-dev`. @@ -167,43 +163,74 @@ The following points assume work in a direct clone of the repository, and not in --------------COPY BELOW THIS LINE-------------- [Reference](https://github.com/esp8266/Arduino/tree/master/package#creating-a-release-for-maintainers) for details. -- [ ] 1. Open a new issue to track activities. +- [ ] 1. Make sure that no issues or PRs are assigned to the milestone to be released. -- [ ] 2. Make sure that no issues or PRs are assigned to the milestone to be released. +- [ ] 2. Open a new issue to track activities. - [ ] 3. Assemble release notes. -- [ ] 4. Make a PR with the following, [wait for Travis CI](https://travis-ci.org/github/esp8266/Arduino/builds/), and merge. +- [ ] 4. Make a PR with the following, [wait for CI](https://github.com/esp8266/Arduino/pull/8034/checks), and merge. * [platform.txt](https://github.com/esp8266/Arduino/blob/master/platform.txt) * [package.json](https://github.com/esp8266/Arduino/blob/master/package.json) - * [cores/esp8266/TZ.h](https://github.com/esp8266/Arduino/blob/master/cores/esp8266/TZ.h) - -- [ ] 5. Wait until the release notes have been checked by other maintainers - -- [ ] 6. Navigate to [Travis CI options](https://travis-ci.org/esp8266/Arduino/settings), enable ´Build pushed branches´ (before tagging in next step) + * [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`) -- [ ] 7. Tag the latest commit on the master branch, then push it to esp8266/Arduino +- [ ] 5. Wait until the release notes have been checked by other maintainers (can be changed afterwards anyway) -- [ ] 8. Wait for Travis CI build for the tag to pass, see https://travis-ci.org/esp8266/Arduino/builds, +- [ ] 6. Tag the latest commit on the master branch, then push it to esp8266/Arduino + add: `git tag -a -m "Release 3.0.0" 3.0.0; git push origin 3.0.0` + remove: `git tag -d X.Y.Z; git push --delete origin X.Y.Z` -- [ ] 9. Check that the new (draft) release has been created (no editing at this point!), see https://github.com/esp8266/Arduino/releases. +- [ ] 7. Wait for CI build for the tag to pass (in `Actions` menu) -- [ ] 10. Check that the boards manager package .zip file has been successfully uploaded as a release artifact. +- [ ] 8. Check that the new (draft) release has been created (no editing at this point!), see https://github.com/esp8266/Arduino/releases. -- [ ] 11. 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). +- [ ] 9. Check that the boards manager package .zip file has been successfully uploaded as a release asset. -- [ ] 12. Return to the [Travis CI options](https://travis-ci.org/esp8266/Arduino/settings) and disable `Build pushed branches`. +- [ ] 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). -- [ ] 13. 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. +- [ ] 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. -- [ ] 14. 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) -- [ ] 15. 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. +- [ ] 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. -- [ ] 16. Create a commit to the master branch, updating: +- [ ] 14. Create a commit to the master branch, updating: * The version in platform.txt and package.json files. This should correspond to the version of the *next* milestone, plus `-dev` suffix. E.g. `3.1.0-dev`. * 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 05778d5487..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 @@ -8,6 +7,7 @@ if [ ! -z "${manualversion}" ]; then plain_ver=${ver} visiblever=${ver} [ -z "${REMOTE_URL}" ] && REMOTE_URL=https://github.com/esp8266/Arduino/releases/download + echo "manual version: ver=${ver} plain_ver=${plain_ver} visiblever=${visiblever}" else @@ -16,67 +16,74 @@ else # Default to draft tag name ver=$(basename $(jq -e -r '.ref' "$GITHUB_EVENT_PATH")) # If not available, try the publish tag name - if [ "$ver" == "null" ]; then - ver=$(jq -e -r '.release.tag_name' "$GITHUB_EVENT_PATH") + if [ "${ver}" == "null" ]; then + ver=$(jq -e -r '.release.tag_name' "${GITHUB_EVENT_PATH}") + echo "release-log-1: ver=${ver} plain_ver=${plain_ver} visiblever=${visiblever}" fi # Fall back to the git description OTW (i.e. interactive) - if [ "$ver" == "null" ]; then + if [ "${ver}" == "null" ]; then ver=$(git describe --tag) + echo "release-log-2: ver=${ver} plain_ver=${plain_ver} visiblever=${visiblever}" fi - visiblever=$ver - plainver=$ver + visiblever=${ver} + plain_ver=${ver} + echo "release-log-3: ver=${ver} plain_ver=${plain_ver} visiblever=${visiblever}" # Match 0.0.* as special-case early-access builds if [ "${ver%.*}" = 0.0 ]; then git tag -d ${ver} ver=`git describe --tag HEAD` plain_ver=$ver + echo "release-log-4: ver=${ver} plain_ver=${plain_ver} visiblever=${visiblever}" fi fi set -e -package_name=esp8266-$visiblever -echo "Version: $visiblever ($ver)" -echo "Package name: $package_name" +package_name=esp8266-${visiblever} +echo "Version: ${visiblever} (real: ${ver})" +echo "Package name: ${package_name}" # Set REMOTE_URL environment variable to the address where the package will be # available for download. This gets written into package json file. -if [ -z "$REMOTE_URL" ]; then +if [ -z "${REMOTE_URL}" ]; then REMOTE_URL="http://localhost:8000" echo "REMOTE_URL not defined, using default" fi -echo "Remote: $REMOTE_URL" +echo "Remote: ${REMOTE_URL}" -if [ -z "$PKG_URL" ]; then - if [ -z "$PKG_URL_PREFIX" ]; then - PKG_URL_PREFIX="$REMOTE_URL/versions/$visiblever" +if [ -z "${PKG_URL}" ]; then + if [ -z "${PKG_URL_PREFIX}" ]; then + PKG_URL_PREFIX="${REMOTE_URL}/versions/${visiblever}" fi - PKG_URL="$PKG_URL_PREFIX/$package_name.zip" + PKG_URL="${PKG_URL_PREFIX}/${package_name}.zip" fi -echo "Package: $PKG_URL" -echo "Docs: $DOC_URL" +echo "Package: ${PKG_URL}" +echo "Docs: ${DOC_URL}" pushd .. # Create directory for the package -outdir=package/versions/$visiblever/$package_name -srcdir=$PWD -rm -rf package/versions/$visiblever -mkdir -p $outdir +outdir=package/versions/${visiblever}/${package_name} +srcdir=${PWD} +rm -rf package/versions/${visiblever} +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/ +rsync -a --exclude-from 'exclude.txt' ${srcdir}/ ${outdir}/ rm exclude.txt # For compatibility, on OS X we need GNU sed which is usually called 'gsed' @@ -88,7 +95,7 @@ fi # Do some replacements in platform.txt file, which are required because IDE # handles tool paths differently when package is installed in hardware folder -cat $srcdir/platform.txt | \ +cat ${srcdir}/platform.txt | \ $SED 's/runtime.tools.xtensa-lx106-elf-gcc.path={runtime.platform.path}\/tools\/xtensa-lx106-elf//g' | \ $SED 's/runtime.tools.python3.path=.*//g' | \ $SED 's/runtime.tools.esptool.path={runtime.platform.path}\/tools\/esptool//g' | \ @@ -99,54 +106,55 @@ $SED 's/^#tools.esptool.cmd=/tools.esptool.cmd=/g' | \ $SED 's/^#tools.esptool.network_cmd=/tools.esptool.network_cmd=/g' | \ $SED 's/tools.mkspiffs.path={runtime.platform.path}\/tools\/mkspiffs/tools.mkspiffs.path=\{runtime.tools.mkspiffs.path\}/g' |\ $SED 's/recipe.hooks.*makecorever.*//g' |\ -$SED "s/version=.*/version=$ver/g" |\ -$SED -E "s/name=([a-zA-Z0-9\ -]+).*/name=\1($ver)/g"\ - > $outdir/platform.txt +$SED "s/version=.*/version=${ver}/g" |\ +$SED -E "s/name=([a-zA-Z0-9\ -]+).*/name=\1(${ver})/g"\ + > ${outdir}/platform.txt # Put core version and short hash of git version into core_version.h -ver_define=`echo $plain_ver | tr "[:lower:].\055" "[:upper:]_"` -echo Ver define: $ver_define -echo \#define ARDUINO_ESP8266_GIT_VER 0x`git rev-parse --short=8 HEAD 2>/dev/null` >$outdir/cores/esp8266/core_version.h -echo \#define ARDUINO_ESP8266_GIT_DESC `git describe --tags 2>/dev/null` >>$outdir/cores/esp8266/core_version.h -echo \#define ARDUINO_ESP8266_RELEASE_$ver_define >>$outdir/cores/esp8266/core_version.h -echo \#define ARDUINO_ESP8266_RELEASE \"$ver_define\" >>$outdir/cores/esp8266/core_version.h +#ver_define=`echo "${plain_ver}" | tr "[:lower:]." "[:upper:]_"` +#echo "ver_define: ${ver_define} (plain_ver: ${plain_ver})" +#echo "#define ARDUINO_ESP8266_GIT_VER 0x`git rev-parse --short=8 HEAD 2>/dev/null`" >${outdir}/cores/esp8266/core_version.h +#echo "#define ARDUINO_ESP8266_GIT_DESC `git describe --tags 2>/dev/null`" >>${outdir}/cores/esp8266/core_version.h +#echo "#define ARDUINO_ESP8266_RELEASE_${ver_define}" >>${outdir}/cores/esp8266/core_version.h +#echo "#define ARDUINO_ESP8266_RELEASE \"${ver_define}\"" >>${outdir}/cores/esp8266/core_version.h +python3 ${srcdir}/tools/makecorever.py -b ${outdir} -i cores/esp8266 -p ${srcdir} -v ${plain_ver} -r # Zip the package -pushd package/versions/$visiblever -echo "Making $package_name.zip" -zip -qr $package_name.zip $package_name -rm -rf $package_name +pushd package/versions/${visiblever} +echo "Making ${package_name}.zip" +zip -qr ${package_name}.zip ${package_name} +rm -rf ${package_name} # Calculate SHA sum and size -sha=`shasum -a 256 $package_name.zip | cut -f 1 -d ' '` -size=`/bin/ls -l $package_name.zip | awk '{print $5}'` -echo Size: $size -echo SHA-256: $sha +sha=`shasum -a 256 ${package_name}.zip | cut -f 1 -d ' '` +size=`/bin/ls -l ${package_name}.zip | awk '{print $5}'` +echo "Size: ${size}" +echo "SHA-256: ${sha}" echo "Making package_esp8266com_index.json" -jq_arg=".packages[0].platforms[0].version = \"$visiblever\" | \ - .packages[0].platforms[0].url = \"$PKG_URL\" |\ - .packages[0].platforms[0].archiveFileName = \"$package_name.zip\"" +jq_arg=".packages[0].platforms[0].version = \"${visiblever}\" | \ + .packages[0].platforms[0].url = \"${PKG_URL}\" |\ + .packages[0].platforms[0].archiveFileName = \"${package_name}.zip\"" -if [ -z "$is_nightly" ]; then - jq_arg="$jq_arg |\ - .packages[0].platforms[0].size = \"$size\" |\ - .packages[0].platforms[0].checksum = \"SHA-256:$sha\"" +if [ -z "${is_nightly}" ]; then + jq_arg="${jq_arg} |\ + .packages[0].platforms[0].size = \"${size}\" |\ + .packages[0].platforms[0].checksum = \"SHA-256:${sha}\"" fi -if [ ! -z "$DOC_URL" ]; then - jq_arg="$jq_arg |\ - .packages[0].platforms[0].help.online = \"$DOC_URL\"" +if [ ! -z "${DOC_URL}" ]; then + jq_arg="${jq_arg} |\ + .packages[0].platforms[0].help.online = \"${DOC_URL}\"" fi -cat $srcdir/package/package_esp8266com_index.template.json | \ - jq "$jq_arg" > package_esp8266com_index.json +cat ${srcdir}/package/package_esp8266com_index.template.json | \ + jq "${jq_arg}" > package_esp8266com_index.json # Use Github API token, if available curl_gh_token_arg=() -if [ ! -z "$CI_GITHUB_API_KEY" ]; then - curl_gh_token_arg=(-H "Authorization: token $CI_GITHUB_API_KEY") +if [ ! -z "${CI_GITHUB_API_KEY}" ]; then + curl_gh_token_arg=(-H "Authorization: token ${CI_GITHUB_API_KEY}") fi # Get previous release name curl --silent "${curl_gh_token_arg[@]}" https://api.github.com/repos/esp8266/Arduino/releases > releases.json @@ -157,39 +165,39 @@ prev_any_release=$(jq -r '. | map(select(.draft == false)) | sort_by(.created_at # Previous pre-release prev_pre_release=$(jq -r '. | map(select(.draft == false and .prerelease == true)) | sort_by(.created_at | - fromdateiso8601) | .[0].tag_name' releases.json) -echo "Previous release: $prev_release" -echo "Previous (pre-?)release: $prev_any_release" -echo "Previous pre-release: $prev_pre_release" +echo "Previous release: ${prev_release}" +echo "Previous (pre-?)release: ${prev_any_release}" +echo "Previous pre-release: ${prev_pre_release}" # Make all released versions available in one package (i.e. don't separate stable/staging versions) -base_ver=$prev_any_release +base_ver=${prev_any_release} # Download previous release -echo "Downloading base package: $base_ver" +echo "Downloading base package: ${base_ver}" old_json=package_esp8266com_index_stable.json -curl -L -o $old_json "https://github.com/esp8266/Arduino/releases/download/${base_ver}/package_esp8266com_index.json" +curl -L -o ${old_json} "https://github.com/esp8266/Arduino/releases/download/${base_ver}/package_esp8266com_index.json" new_json=package_esp8266com_index.json set +e # Merge the old and new -python3 ../../merge_packages.py $new_json $old_json > tmp +python3 ../../merge_packages.py ${new_json} ${old_json} > tmp # additional json to merge (for experimental releases) echo "Additional json package files: ${MOREJSONPACKAGES}" for json in ${MOREJSONPACKAGES}; do - if [ ! -z "$json" -a -r "$json" ]; then - echo "- merging $json" - python3 ../../merge_packages.py tmp $json > tmp2 + if [ ! -z "${json}" -a -r "${json}" ]; then + echo "- merging ${json}" + python3 ../../merge_packages.py tmp ${json} > tmp2 mv tmp2 tmp fi done # drop any obsolete package versions -python3 ../../drop_versions.py - tools 1.20.0-26-gb404fb9 < tmp > tmp2 && mv tmp2 $new_json && rm $old_json tmp +python3 ../../drop_versions.py - tools 1.20.0-26-gb404fb9 < tmp > tmp2 && mv tmp2 ${new_json} && rm ${old_json} tmp # Verify the JSON file can be read, fail if it's not OK set -e -cat $new_json | jq empty +cat ${new_json} | jq empty popd popd diff --git a/package/deploy_package_index.sh b/package/deploy_package_index.sh index edfb27c230..b940c7d0f7 100644 --- a/package/deploy_package_index.sh +++ b/package/deploy_package_index.sh @@ -24,8 +24,11 @@ chmod go-w ~/.ssh/config git clone git@github.com:esp8266/esp8266.github.io.git pushd esp8266.github.io -# Update the package index -cp ../versions/*/package_esp8266com_index.json stable/package_esp8266com_index.json +# Copy from published release, ensure JSON valid +rm -f stable/package_esp8266com_index.json +wget https://github.com/esp8266/Arduino/releases/download/$tag/package_esp8266com_index.json -O stable/package_esp8266com_index.json +cat stable/package_esp8266com_index.json | jq empty + git add stable/package_esp8266com_index.json # Commit and push the changes diff --git a/package/package_esp8266com_index.template.json b/package/package_esp8266com_index.template.json index 42f2637428..3c8d1bed39 100644 --- a/package/package_esp8266com_index.template.json +++ b/package/package_esp8266com_index.template.json @@ -23,12 +23,18 @@ { "name": "Generic ESP8285 Module" }, + { + "name": "Lifely Agrumino Lemon v4" + }, { "name": "ESPDuino (ESP-13 Module)" }, { "name": "Adafruit Feather HUZZAH ESP8266" }, + { + "name": "WiFi Kit 8" + }, { "name": "Invent One" }, @@ -41,6 +47,9 @@ { "name": "ESPresso Lite 2.0" }, + { + "name": "Mercury 1.0" + }, { "name": "Phoenix 1.0" }, @@ -71,6 +80,12 @@ { "name": "LOLIN(WEMOS) D1 R2 & mini" }, + { + "name": "LOLIN(WEMOS) D1 ESP-WROOM-02" + }, + { + "name": "LOLIN(WEMOS) D1 mini (clone)" + }, { "name": "LOLIN(WEMOS) D1 mini Pro" }, @@ -123,17 +138,22 @@ "toolsDependencies": [ { "packager": "esp8266", - "version": "3.0.0-newlib4.0.0-gnu21-dba3cc8", + "version": "3.2.0-gcc10.3-c791b74", "name": "xtensa-lx106-elf-gcc" }, { "packager": "esp8266", - "version": "3.0.0-newlib4.0.0-gnu21-dba3cc8", + "version": "3.2.0-gcc10.3-c791b74", "name": "mkspiffs" }, { "packager": "esp8266", - "version": "3.7.2-post1", + "version": "3.2.0-gcc10.3-c791b74", + "name": "mklittlefs" + }, + { + "packager": "esp8266", + "version": "3.7.2-post2", "name": "python3" } ], @@ -144,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", - "archiveFileName": "python3-3.7.2.post1-embed-win32va2.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.0-newlib4.0.0-gnu21-dba3cc8", + "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.0-newlib4.0.0-gnu21/aarch64-linux-gnu.xtensa-lx106-elf-dba3cc8.201223.tar.gz", - "archiveFileName": "aarch64-linux-gnu.xtensa-lx106-elf-dba3cc8.201223.tar.gz", - "checksum": "SHA-256:fa6278eda34ec18fb7cbc3fb1b76f559a4a18bd6c817b795f6c8807f7cd181bc", - "size": "71018486" + "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.0-newlib4.0.0-gnu21/arm-linux-gnueabihf.xtensa-lx106-elf-dba3cc8.201223.tar.gz", - "archiveFileName": "arm-linux-gnueabihf.xtensa-lx106-elf-dba3cc8.201223.tar.gz", - "checksum": "SHA-256:87eccbeb735ece0e8eb45c060007ef906ac1975589259854a325f9f3911c9923", - "size": "67098909" + "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.0-newlib4.0.0-gnu21/i686-linux-gnu.xtensa-lx106-elf-dba3cc8.201223.tar.gz", - "archiveFileName": "i686-linux-gnu.xtensa-lx106-elf-dba3cc8.201223.tar.gz", - "checksum": "SHA-256:fc267621440e0a0f91c89a44edd604b07bec38bdcb7bcdda687a160d9397f04a", - "size": "74193717" + "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.0-newlib4.0.0-gnu21/i686-w64-mingw32.xtensa-lx106-elf-dba3cc8.201223.zip", - "archiveFileName": "i686-w64-mingw32.xtensa-lx106-elf-dba3cc8.201223.zip", - "checksum": "SHA-256:c909331865ec15a822300763e159fe183e7cd89a47e9283abb5d1fb0fcdeaf50", - "size": "71433726" + "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.0-newlib4.0.0-gnu21/x86_64-apple-darwin14.xtensa-lx106-elf-dba3cc8.201223.tar.gz", - "archiveFileName": "x86_64-apple-darwin14.xtensa-lx106-elf-dba3cc8.201223.tar.gz", - "checksum": "SHA-256:e193490e343c7d1c0222af6a1b961e26683367fd07bc49ee471b7c98298ce0ca", - "size": "75617500" + "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.0-newlib4.0.0-gnu21/x86_64-linux-gnu.xtensa-lx106-elf-dba3cc8.201223.tar.gz", - "archiveFileName": "x86_64-linux-gnu.xtensa-lx106-elf-dba3cc8.201223.tar.gz", - "checksum": "SHA-256:356f6ac8bd8e241731a6b4bad17811f80d6ccfb9c479f8cf6128fb80152f2037", - "size": "74719337" + "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.0-newlib4.0.0-gnu21/x86_64-w64-mingw32.xtensa-lx106-elf-dba3cc8.201223.zip", - "archiveFileName": "x86_64-w64-mingw32.xtensa-lx106-elf-dba3cc8.201223.zip", - "checksum": "SHA-256:56af2a313aa0b707d4308350b36bd6d112f98bfb8c22fe118dff9b27092f06ac", - "size": "75406203" + "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.0-newlib4.0.0-gnu21-dba3cc8", + "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.0-newlib4.0.0-gnu21/aarch64-linux-gnu.mkspiffs-7fefeac.201223.tar.gz", - "archiveFileName": "aarch64-linux-gnu.mkspiffs-7fefeac.201223.tar.gz", - "checksum": "SHA-256:d2875219d38e176e03c6c1c96136dbc18a99f15e7de5e1bd08938ca8235a7bd8", - "size": "51258" + "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.0-newlib4.0.0-gnu21/arm-linux-gnueabihf.mkspiffs-7fefeac.201223.tar.gz", - "archiveFileName": "arm-linux-gnueabihf.mkspiffs-7fefeac.201223.tar.gz", - "checksum": "SHA-256:20a19854e6b38220caa283fc0c9b0c3134474215899c05df68c11dabe85b0952", - "size": "44270" + "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.0-newlib4.0.0-gnu21/i686-linux-gnu.mkspiffs-7fefeac.201223.tar.gz", - "archiveFileName": "i686-linux-gnu.mkspiffs-7fefeac.201223.tar.gz", - "checksum": "SHA-256:43534ee2575a2d3ab895a2367f79ceed7e90e68cf6790106b91cdfb2eada34c8", - "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.0-newlib4.0.0-gnu21/i686-w64-mingw32.mkspiffs-7fefeac.201223.zip", - "archiveFileName": "i686-w64-mingw32.mkspiffs-7fefeac.201223.zip", - "checksum": "SHA-256:0f98fa50195f1523504021f793f4947fbc5e064f701308299ce4e67de22a8c6c", - "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.0-newlib4.0.0-gnu21/x86_64-apple-darwin14.mkspiffs-7fefeac.201223.tar.gz", - "archiveFileName": "x86_64-apple-darwin14.mkspiffs-7fefeac.201223.tar.gz", - "checksum": "SHA-256:4807b03e1e9a4dee01acf9a0da37c3914983baf20e303fd2c2ae896a435af8b2", - "size": "368776" + "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.0-newlib4.0.0-gnu21/x86_64-linux-gnu.mkspiffs-7fefeac.201223.tar.gz", - "archiveFileName": "x86_64-linux-gnu.mkspiffs-7fefeac.201223.tar.gz", - "checksum": "SHA-256:2139296513ce2449e602dfca690116580ce90d24e65da7361745f0030af0005b", - "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.0-newlib4.0.0-gnu21/x86_64-w64-mingw32.mkspiffs-7fefeac.201223.zip", - "archiveFileName": "x86_64-w64-mingw32.mkspiffs-7fefeac.201223.zip", - "checksum": "SHA-256:9d58e0220356066ef912eef3ce2362d2b1ab1618c8379b4a01809c37120655e6", - "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.0-newlib4.0.0-gnu21-dba3cc8", + "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.0-newlib4.0.0-gnu21/aarch64-linux-gnu.mklittlefs-6b5c62d.201223.tar.gz", - "archiveFileName": "aarch64-linux-gnu.mklittlefs-6b5c62d.201223.tar.gz", - "checksum": "SHA-256:ff3b6559bcfb56bb0e20abd0297cb61e010667ef9ac8a1e7aad6a94ff8a32684", - "size": "44788" + "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.0-newlib4.0.0-gnu21/arm-linux-gnueabihf.mklittlefs-6b5c62d.201223.tar.gz", - "archiveFileName": "arm-linux-gnueabihf.mklittlefs-6b5c62d.201223.tar.gz", - "checksum": "SHA-256:f85dc41964321e4c1cf73118cbe255e6b852b58edef994beab220a6d1f8d3015", - "size": "37331" + "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.0-newlib4.0.0-gnu21/i686-linux-gnu.mklittlefs-6b5c62d.201223.tar.gz", - "archiveFileName": "i686-linux-gnu.mklittlefs-6b5c62d.201223.tar.gz", - "checksum": "SHA-256:b909bbc0b9edba9f0db6ed19f5bcc5d7c614665e2b56b107cc9e9316673ba91c", - "size": "48242" + "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.0-newlib4.0.0-gnu21/i686-w64-mingw32.mklittlefs-6b5c62d.201223.zip", - "archiveFileName": "i686-w64-mingw32.mklittlefs-6b5c62d.201223.zip", - "checksum": "SHA-256:ec22bd21c4e5420d67dfdd014eec4b24342b575a097ef79d8e0eed05762e31e3", - "size": "332804" + "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.0-newlib4.0.0-gnu21/x86_64-apple-darwin14.mklittlefs-6b5c62d.201223.tar.gz", - "archiveFileName": "x86_64-apple-darwin14.mklittlefs-6b5c62d.201223.tar.gz", - "checksum": "SHA-256:8735f6c2bdf5788c6238842bf41f2a35eac807fd55009f51f2659d2b11b1aa3c", - "size": "362805" + "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.0-newlib4.0.0-gnu21/x86_64-linux-gnu.mklittlefs-6b5c62d.201223.tar.gz", - "archiveFileName": "x86_64-linux-gnu.mklittlefs-6b5c62d.201223.tar.gz", - "checksum": "SHA-256:e11216bef3f85504d030762c390bc974a31d721063d4b001c03f0b03034df545", - "size": "46920" + "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.0-newlib4.0.0-gnu21/x86_64-w64-mingw32.mklittlefs-6b5c62d.201223.zip", - "archiveFileName": "x86_64-w64-mingw32.mklittlefs-6b5c62d.201223.zip", - "checksum": "SHA-256:57311851b8c8710ea747f608f5a728d5e88e75355f0a644e6a93d05797d983a3", - "size": "345249" + "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 17af47d77f..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.0.0-dev) -version=3.0.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,15 +17,16 @@ 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 -compiler.warning_flags=-w -compiler.warning_flags.none=-w -compiler.warning_flags.default= -compiler.warning_flags.more=-Wall -compiler.warning_flags.all=-Wall -Wextra +compiler.warning_flags=@{runtime.platform.path}/tools/warnings/none +compiler.warning_flags.none=@{runtime.platform.path}/tools/warnings/none +compiler.warning_flags.default=@{runtime.platform.path}/tools/warnings/default +compiler.warning_flags.more=@{runtime.platform.path}/tools/warnings/more +compiler.warning_flags.all=@{runtime.platform.path}/tools/warnings/extra build.lwip_lib=-llwip_gcc build.lwip_include=lwip/include @@ -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= @@ -52,25 +56,44 @@ build.sdk=NONOSDK22x_190703 #build.sdk=NONOSDK22x_191024 #build.sdk=NONOSDK22x_191105 +# These are not overriden when FS is not configured +build.eeprom_start= +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__ "-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} -std=gnu17 {build.stacksmash_flags} -Os -g -free -fipa-pta -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.waveform} {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} -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} {build.stacksmash_flags} -Os -g -free -fipa-pta -mlongcalls -mtext-section-literals -fno-rtti -falign-functions=4 {build.stdcpp_level} -MMD -ffunction-sections -fdata-sections {build.exception_flags} {build.sslflags} {build.waveform} {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 @@ -82,8 +105,8 @@ compiler.elf2hex.flags= compiler.size.cmd=xtensa-lx106-elf-size -# This can be overriden in boards.txt -build.extra_flags=-DESP8266 +# This can be overridden in boards.txt +build.extra_flags= # These can be overridden in platform.local.txt compiler.c.extra_flags= @@ -96,38 +119,42 @@ compiler.elf2hex.extra_flags= ## generate file with git version number ## needs git -recipe.hooks.sketch.prebuild.pattern="{runtime.tools.python3.path}/python3" "{runtime.tools.signing}" --mode header --publickey "{build.source.path}/public.key" --out "{build.path}/core/Updater_Signing.h" +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" "{runtime.tools.makecorever}" --build_path "{build.path}" --platform_path "{runtime.platform.path}" --version "unix-{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" "{runtime.tools.mkdir}" -p "{build.path}/ld_h/" -recipe.hooks.linking.prelink.2.pattern="{runtime.tools.python3.path}/python3" "{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.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}" ## Combine gc-sections, archives, and objects -recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" {build.exception_flags} -Wl,-Map "-Wl,{build.path}/{build.project_name}.map" {compiler.c.elf.flags} {compiler.c.elf.extra_flags} -o "{build.path}/{build.project_name}.elf" -Wl,--start-group {object_files} "{archive_file_path}" {compiler.c.elf.libs} -Wl,--end-group "-L{build.path}" +recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" {build.exception_flags} -Wl,-Map "-Wl,{build.path}/{build.project_name}.map" {compiler.c.elf.flags} {compiler.c.elf.extra_flags} -o "{build.path}/{build.project_name}.elf" -Wl,--start-group {object_files} "{archive_file_path}" {compiler.c.elf.libs} {compiler.libraries.ldflags} -Wl,--end-group "-L{build.path}" ## Create eeprom recipe.objcopy.eep.pattern= ## Create hex -recipe.objcopy.hex.1.pattern="{runtime.tools.python3.path}/python3" "{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" "{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" "{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.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" -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 @@ -157,10 +184,9 @@ tools.esptool.upload.params.quiet= # First, potentially perform an erase or nothing # Next, do the binary upload -# Combined in one rule because Arduino doesn't suport upload.1.pattern/upload.3.pattern -tools.esptool.upload.pattern="{cmd}" "{runtime.platform.path}/tools/upload.py" --chip esp8266 --port "{serial.port}" --baud "{upload.speed}" "{upload.verbose}" {upload.erase_cmd} {upload.resetmethod} write_flash 0x0 "{build.path}/{build.project_name}.bin" - -tools.esptool.upload.network_pattern="{network_cmd}" "{runtime.platform.path}/tools/espota.py" -i "{serial.port}" -p "{network.port}" "--auth={network.password}" -f "{build.path}/{build.project_name}.bin" +# Combined in one rule because Arduino doesn't support upload.1.pattern/upload.3.pattern +tools.esptool.upload.pattern="{cmd}" -I "{runtime.platform.path}/tools/upload.py" --chip esp8266 --port "{serial.port}" --baud "{upload.speed}" "{upload.verbose}" {upload.erase_cmd} {upload.resetmethod} write_flash 0x0 "{build.path}/{build.project_name}.bin" +tools.esptool.upload.network_pattern="{network_cmd}" -I "{runtime.platform.path}/tools/espota.py" -i "{serial.port}" -p "{network.port}" "--auth={network.password}" -f "{build.path}/{build.project_name}.bin" tools.mkspiffs.cmd=mkspiffs tools.mkspiffs.cmd.windows=mkspiffs.exe diff --git a/tests/astyle_core.conf b/tests/astyle_core.conf deleted file mode 100644 index f9ee90adb6..0000000000 --- a/tests/astyle_core.conf +++ /dev/null @@ -1,32 +0,0 @@ -# Code formatting rules for Arduino examples, taken from: -# -# https://github.com/arduino/Arduino/blob/master/build/shared/examples_formatter.conf -# - -mode=c -lineend=linux -style=allman - -# 4 spaces indentation -indent=spaces=4 - -# also indent macros -#indent-preprocessor - -# indent classes, switches (and cases), comments starting at column 1 -indent-col1-comments - -# put a space around operators -pad-oper - -# put a space after if/for/while -pad-header - -# if you like one-liners, keep them -keep-one-line-statements - -attach-closing-while -unpad-paren -pad-oper -remove-comment-prefix -add-braces diff --git a/tests/astyle_examples.conf b/tests/astyle_examples.conf deleted file mode 100644 index f3b77f2cb4..0000000000 --- a/tests/astyle_examples.conf +++ /dev/null @@ -1,44 +0,0 @@ -# Code formatting rules for Arduino examples, taken from: -# -# https://github.com/arduino/Arduino/blob/master/build/shared/examples_formatter.conf -# - -mode=c -lineend=linux - -# 2 spaces indentation -indent=spaces=2 - -# also indent macros -#indent-preprocessor - -# indent classes, switches (and cases), comments starting at column 1 -indent-classes -indent-switches -indent-cases -indent-col1-comments - -# put a space around operators -pad-oper - -# put a space after if/for/while -pad-header - -# if you like one-liners, keep them -keep-one-line-statements -add-braces - -style=java -attach-namespaces -attach-classes -attach-inlines -attach-extern-c -indent-modifiers -indent-namespaces -indent-labels -#indent-preproc-block -#indent-preproc-define -#indent-preproc-cond -unpad-paren -add-braces -remove-comment-prefix \ No newline at end of file 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 old mode 100644 new mode 100755 index 0450b03353..7efb6bbd60 --- 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 new file mode 100755 index 0000000000..a189e1a594 --- /dev/null +++ b/tests/ci/pkgrefs_test.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +set -ev + +root=$(git rev-parse --show-toplevel) + +fail=0 +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" + rm -f file.bin + wget --quiet -O file.bin $url + calc=$(sha256sum file.bin | cut -f1 -d" ") + if [ "$sha" != "$calc" ]; then + echo "ERROR: Download failed or SHA mismatch for $url" + echo "ERROR: Expected $sha" + echo "ERROR: Received $calc" + fail=1 + fi +done + +if [ $fail -ne 0 ]; then + echo ERROR: Package file integrity check failed + exit 1 +fi diff --git a/tests/ci/style_check.sh b/tests/ci/style_check.sh index 609b7e4577..e8a1dd7697 100755 --- a/tests/ci/style_check.sh +++ b/tests/ci/style_check.sh @@ -2,14 +2,17 @@ # # CI job for checking examples style -set -ev +set -e -x -org=$(cd ${0%/*}; pwd) -${org}/../restyle.sh +git --version || true +root=$(git rev-parse --show-toplevel) -# Revert changes which astyle 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-arduino.yaml b/tests/clang-format-arduino.yaml new file mode 100644 index 0000000000..1a307caab9 --- /dev/null +++ b/tests/clang-format-arduino.yaml @@ -0,0 +1,149 @@ +# Taken from https://github.com/arduino/arduino-language-server/blob/e453c5fbd059bae673bb21d028f5ca8e690744be/handler/handler.go#L1769-L1952 +# Which will be used in the IDE 2.0+ when 'format sketch' option is selected + +Language: Cpp +# LLVM is the default style setting, used when a configuration option is not set here +BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveBitFields: false +AlignConsecutiveDeclarations: false +AlignConsecutiveMacros: false +AlignEscapedNewlines: DontAlign +AlignOperands: Align +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Always +AllowShortCaseLabelsOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: Always +AllowShortLambdasOnASingleLine: Empty +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: No +BinPackArguments: true +BinPackParameters: true +# Only used when "BreakBeforeBraces" set to "Custom" +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + #AfterObjCDeclaration: + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +# Java-specific +#BreakAfterJavaFieldAnnotations: +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Attach +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +BreakStringLiterals: false +# v12 has various problems with this set to 0 (no limit) +# possible workaround is to set this to 4294967295 aka some large number +# see https://reviews.llvm.org/D96896 +ColumnLimit: 0 +# "" matches none +CommentPragmas: "" +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 2 +ContinuationIndentWidth: 2 +Cpp11BracedListStyle: false +DeriveLineEnding: true +DerivePointerAlignment: true +DisableFormat: false +# Docs say "Do not use this in config files". The default (LLVM 11.0.1) is "false". +#ExperimentalAutoDetectBinPacking: +FixNamespaceComments: false +ForEachMacros: [] +IncludeBlocks: Preserve +IncludeCategories: [] +# "" matches none +IncludeIsMainRegex: "" +IncludeIsMainSourceRegex: "" +IndentCaseBlocks: true +IndentCaseLabels: true +IndentExternBlock: Indent +IndentGotoLabels: false +IndentPPDirectives: None +IndentWidth: 2 +IndentWrappedFunctionNames: false +InsertTrailingCommas: None +# Java-specific +#JavaImportGroups: +# JavaScript-specific +#JavaScriptQuotes: +#JavaScriptWrapImports +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: "" +MacroBlockEnd: "" +# Set to a large number to effectively disable +MaxEmptyLinesToKeep: 100000 +NamespaceIndentation: None +NamespaceMacros: [] +# Objective C-specific +#ObjCBinPackProtocolList: +#ObjCBlockIndentWidth: +#ObjCBreakBeforeNestedBlockParam: +#ObjCSpaceAfterProperty: +#ObjCSpaceBeforeProtocolList +PenaltyBreakAssignment: 1 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 1 +PenaltyBreakFirstLessLess: 1 +PenaltyBreakString: 1 +PenaltyBreakTemplateDeclaration: 1 +PenaltyExcessCharacter: 1 +PenaltyReturnTypeOnItsOwnLine: 1 +# Used as a fallback if alignment style can't be detected from code (DerivePointerAlignment: true) +PointerAlignment: Right +RawStringFormats: [] +ReflowComments: true +SortIncludes: false +SortUsingDeclarations: false +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +StatementMacros: [] +TabWidth: 2 +TypenameMacros: [] +# Default to LF if line endings can't be detected from the content (DeriveLineEnding). +UseCRLF: false +UseTab: Never +WhitespaceSensitiveMacros: [] diff --git a/tests/clang-format-core.yaml b/tests/clang-format-core.yaml new file mode 100644 index 0000000000..0df633d245 --- /dev/null +++ b/tests/clang-format-core.yaml @@ -0,0 +1,30 @@ +BasedOnStyle: WebKit +AlignTrailingComments: true +SortIncludes: false +ColumnLimit: 100 +KeepEmptyLinesAtTheStartOfBlocks: false +SpaceAfterTemplateKeyword: false +SpaceBeforeInheritanceColon: false +SpacesBeforeTrailingComments: 2 +AllowShortBlocksOnASingleLine: Empty +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: false +AllowShortLambdasOnASingleLine: Empty +AllowShortLoopsOnASingleLine: false +AlignConsecutiveAssignments: Consecutive +AlignConsecutiveBitFields: Consecutive +AlignConsecutiveDeclarations: Consecutive +AlignAfterOpenBracket: Align +AlignOperands: Align +AlwaysBreakTemplateDeclarations: Yes +BreakConstructorInitializers: AfterColon +BreakBeforeBinaryOperators: All +BreakBeforeTernaryOperators: true +BreakBeforeConceptDeclarations: true +FixNamespaceComments: true +NamespaceIndentation: Inner +BreakBeforeBraces: Allman +IndentWidth: 4 +IndentCaseLabels: false +ReflowComments: false +SkipMacroDefinitionBody: true diff --git a/tests/common.sh b/tests/common.sh index 5898196ef5..f05eb6b37a 100755 --- a/tests/common.sh +++ b/tests/common.sh @@ -1,258 +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/ -/BackwardCompatibility/ -/examplesV1/ -/ExFatFormatter/ -/ExFatLogger/ -/ExFatUnicodeTest/ -/RtcTimestampTest/ -/SoftwareSpi/ -/STM32Test/ -/TeensyRtcTimestamp/ -/TeensySdioDemo/ -/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 pwm_phase="" - [ $(( $build_rem % 2 )) -eq 0 ] && pwm_phase="--waveform_phase" + 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 "$build_cmd $pwm_phase $sketch" - time ($build_cmd $pwm_phase $sketch >build.log) - local result=$? + + echo ::group::Building $sketch + echo "$build_cmd $sketch" + + 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 c60135c81e..cbbd34728a 100644 --- a/tests/device/Makefile +++ b/tests/device/Makefile @@ -64,9 +64,9 @@ $(TEST_LIST): $(SILENT)mkdir -p $(LOCAL_BUILD_DIR) ifeq ("$(MOCK)", "1") @echo Compiling $(notdir $@) - (cd ../host; make ULIBDIRS=../device/libraries/BSTest ../device/$(@:%.ino=%)) - $(SILENT)source $(BS_DIR)/virtualenv/bin/activate && \ - $(PYTHON) $(BS_DIR)/runner.py \ + (cd ../host; make D=$(V) ULIBDIRS=../device/libraries/BSTest ../device/$(@:%.ino=%)) + $(SILENT)$(BS_DIR)/virtualenv/bin/python \ + $(BS_DIR)/runner.py \ $(RUNNER_DEBUG_FLAG) \ -e "$(ESP8266_CORE_PATH)/tests/host/bin/$(@:%.ino=%)" \ -n $(basename $(notdir $@)) \ @@ -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/BSArduino.h b/tests/device/libraries/BSTest/src/BSArduino.h index 2fd4dc66e0..10cd2c37d9 100644 --- a/tests/device/libraries/BSTest/src/BSArduino.h +++ b/tests/device/libraries/BSTest/src/BSArduino.h @@ -7,29 +7,30 @@ namespace bs class ArduinoIOHelper { public: - ArduinoIOHelper(Stream& stream) : m_stream(stream) - { - } + ArduinoIOHelper(Stream& stream) : m_stream(stream) { } - size_t printf(const char *format, ...) + size_t printf(const char* format, ...) { va_list arg; va_start(arg, format); - char temp[128]; - char* buffer = temp; - size_t len = vsnprintf(temp, sizeof(temp), format, arg); + char temp[128]; + char* buffer = temp; + size_t len = vsnprintf(temp, sizeof(temp), format, arg); va_end(arg); - if (len > sizeof(temp) - 1) { + if (len > sizeof(temp) - 1) + { buffer = new char[len + 1]; - if (!buffer) { + if (!buffer) + { return 0; } va_start(arg, format); ets_vsnprintf(buffer, len + 1, format, arg); va_end(arg); } - len = m_stream.write((const uint8_t*) buffer, len); - if (buffer != temp) { + len = m_stream.write((const uint8_t*)buffer, len); + if (buffer != temp) + { delete[] buffer; } return len; @@ -40,16 +41,20 @@ class ArduinoIOHelper size_t len = 0; // Can't use Stream::readBytesUntil here because it can't tell the // difference between timing out and receiving the terminator. - while (len < dest_size - 1) { + while (len < dest_size - 1) + { int c = m_stream.read(); - if (c < 0) { + if (c < 0) + { delay(1); continue; } - if (c == '\r') { + if (c == '\r') + { continue; } - if (c == '\n') { + if (c == '\n') + { dest[len] = 0; break; } @@ -64,10 +69,11 @@ class ArduinoIOHelper typedef ArduinoIOHelper IOHelper; -inline void fatal() { +inline void fatal() +{ ESP.restart(); } -} // namespace bs +} // namespace bs -#endif //BS_ARDUINO_H +#endif // BS_ARDUINO_H diff --git a/tests/device/libraries/BSTest/src/BSArgs.h b/tests/device/libraries/BSTest/src/BSArgs.h index 060bcdf18c..0d8c18365f 100644 --- a/tests/device/libraries/BSTest/src/BSArgs.h +++ b/tests/device/libraries/BSTest/src/BSArgs.h @@ -18,134 +18,162 @@ namespace protocol #define SS_FLAG_ESCAPE 0x8 -typedef enum { - /* parsing the space between arguments */ - SS_SPACE = 0x0, - /* parsing an argument which isn't quoted */ - SS_ARG = 0x1, - /* parsing a quoted argument */ - SS_QUOTED_ARG = 0x2, - /* parsing an escape sequence within unquoted argument */ - SS_ARG_ESCAPED = SS_ARG | SS_FLAG_ESCAPE, - /* parsing an escape sequence within a quoted argument */ - SS_QUOTED_ARG_ESCAPED = SS_QUOTED_ARG | SS_FLAG_ESCAPE, -} split_state_t; + typedef enum + { + /* parsing the space between arguments */ + SS_SPACE = 0x0, + /* parsing an argument which isn't quoted */ + SS_ARG = 0x1, + /* parsing a quoted argument */ + SS_QUOTED_ARG = 0x2, + /* parsing an escape sequence within unquoted argument */ + SS_ARG_ESCAPED = SS_ARG | SS_FLAG_ESCAPE, + /* parsing an escape sequence within a quoted argument */ + SS_QUOTED_ARG_ESCAPED = SS_QUOTED_ARG | SS_FLAG_ESCAPE, + } split_state_t; /* helper macro, called when done with an argument */ -#define END_ARG() do { \ - char_out = 0; \ - argv[argc++] = next_arg_start; \ - state = SS_SPACE; \ - } while(0); - - -/** - * @brief Split command line into arguments in place - * - * - This function finds whitespace-separated arguments in the given input line. - * - * 'abc def 1 20 .3' -> [ 'abc', 'def', '1', '20', '.3' ] - * - * - Argument which include spaces may be surrounded with quotes. In this case - * spaces are preserved and quotes are stripped. - * - * 'abc "123 456" def' -> [ 'abc', '123 456', 'def' ] - * - * - Escape sequences may be used to produce backslash, double quote, and space: - * - * 'a\ b\\c\"' -> [ 'a b\c"' ] - * - * Pointers to at most argv_size - 1 arguments are returned in argv array. - * The pointer after the last one (i.e. argv[argc]) is set to NULL. - * - * @param line pointer to buffer to parse; it is modified in place - * @param argv array where the pointers to arguments are written - * @param argv_size number of elements in argv_array (max. number of arguments will be argv_size - 1) - * @return number of arguments found (argc) - */ -inline size_t split_args(char *line, char **argv, size_t argv_size) -{ - const int QUOTE = '"'; - const int ESCAPE = '\\'; - const int SPACE = ' '; - split_state_t state = SS_SPACE; - size_t argc = 0; - char *next_arg_start = line; - char *out_ptr = line; - for (char *in_ptr = line; argc < argv_size - 1; ++in_ptr) { - int char_in = (unsigned char) *in_ptr; - if (char_in == 0) { - break; - } - int char_out = -1; - - switch (state) { - case SS_SPACE: - if (char_in == SPACE) { - /* skip space */ - } else if (char_in == QUOTE) { - next_arg_start = out_ptr; - state = SS_QUOTED_ARG; - } else if (char_in == ESCAPE) { - next_arg_start = out_ptr; - state = SS_ARG_ESCAPED; - } else { - next_arg_start = out_ptr; - state = SS_ARG; - char_out = char_in; +#define END_ARG() \ + do \ + { \ + char_out = 0; \ + argv[argc++] = next_arg_start; \ + state = SS_SPACE; \ + } while (0); + + /** + * @brief Split command line into arguments in place + * + * - This function finds whitespace-separated arguments in the given input line. + * + * 'abc def 1 20 .3' -> [ 'abc', 'def', '1', '20', '.3' ] + * + * - Argument which include spaces may be surrounded with quotes. In this case + * spaces are preserved and quotes are stripped. + * + * 'abc "123 456" def' -> [ 'abc', '123 456', 'def' ] + * + * - Escape sequences may be used to produce backslash, double quote, and space: + * + * 'a\ b\\c\"' -> [ 'a b\c"' ] + * + * Pointers to at most argv_size - 1 arguments are returned in argv array. + * The pointer after the last one (i.e. argv[argc]) is set to NULL. + * + * @param line pointer to buffer to parse; it is modified in place + * @param argv array where the pointers to arguments are written + * @param argv_size number of elements in argv_array (max. number of arguments will be argv_size + * - 1) + * @return number of arguments found (argc) + */ + inline size_t split_args(char* line, char** argv, size_t argv_size) + { + const int QUOTE = '"'; + const int ESCAPE = '\\'; + const int SPACE = ' '; + split_state_t state = SS_SPACE; + size_t argc = 0; + char* next_arg_start = line; + char* out_ptr = line; + for (char* in_ptr = line; argc < argv_size - 1; ++in_ptr) + { + int char_in = (unsigned char)*in_ptr; + if (char_in == 0) + { + break; } - break; - - case SS_QUOTED_ARG: - if (char_in == QUOTE) { - END_ARG(); - } else if (char_in == ESCAPE) { - state = SS_QUOTED_ARG_ESCAPED; - } else { - char_out = char_in; + int char_out = -1; + + switch (state) + { + case SS_SPACE: + if (char_in == SPACE) + { + /* skip space */ + } + else if (char_in == QUOTE) + { + next_arg_start = out_ptr; + state = SS_QUOTED_ARG; + } + else if (char_in == ESCAPE) + { + next_arg_start = out_ptr; + state = SS_ARG_ESCAPED; + } + else + { + next_arg_start = out_ptr; + state = SS_ARG; + char_out = char_in; + } + break; + + case SS_QUOTED_ARG: + if (char_in == QUOTE) + { + END_ARG(); + } + else if (char_in == ESCAPE) + { + state = SS_QUOTED_ARG_ESCAPED; + } + else + { + char_out = char_in; + } + break; + + case SS_ARG_ESCAPED: + case SS_QUOTED_ARG_ESCAPED: + if (char_in == ESCAPE || char_in == QUOTE || char_in == SPACE) + { + char_out = char_in; + } + else + { + /* unrecognized escape character, skip */ + } + state = (split_state_t)(state & (~SS_FLAG_ESCAPE)); + break; + + case SS_ARG: + if (char_in == SPACE) + { + END_ARG(); + } + else if (char_in == ESCAPE) + { + state = SS_ARG_ESCAPED; + } + else + { + char_out = char_in; + } + break; } - break; - - case SS_ARG_ESCAPED: - case SS_QUOTED_ARG_ESCAPED: - if (char_in == ESCAPE || char_in == QUOTE || char_in == SPACE) { - char_out = char_in; - } else { - /* unrecognized escape character, skip */ - } - state = (split_state_t) (state & (~SS_FLAG_ESCAPE)); - break; - - case SS_ARG: - if (char_in == SPACE) { - END_ARG(); - } else if (char_in == ESCAPE) { - state = SS_ARG_ESCAPED; - } else { - char_out = char_in; + /* need to output anything? */ + if (char_out >= 0) + { + *out_ptr = char_out; + ++out_ptr; } - break; } - /* need to output anything? */ - if (char_out >= 0) { - *out_ptr = char_out; - ++out_ptr; + /* make sure the final argument is terminated */ + *out_ptr = 0; + /* finalize the last argument */ + if (state != SS_SPACE && argc < argv_size - 1) + { + argv[argc++] = next_arg_start; } - } - /* make sure the final argument is terminated */ - *out_ptr = 0; - /* finalize the last argument */ - if (state != SS_SPACE && argc < argv_size - 1) { - argv[argc++] = next_arg_start; - } - /* add a NULL at the end of argv */ - argv[argc] = NULL; + /* add a NULL at the end of argv */ + argv[argc] = NULL; - return argc; -} + return argc; + } -} // namespace bs +} // namespace protocol -} // namespace protocol +} // namespace bs -#endif //BS_ARGS_H +#endif // BS_ARGS_H diff --git a/tests/device/libraries/BSTest/src/BSProtocol.h b/tests/device/libraries/BSTest/src/BSProtocol.h index 05a874f956..91eb68e580 100644 --- a/tests/device/libraries/BSTest/src/BSProtocol.h +++ b/tests/device/libraries/BSTest/src/BSProtocol.h @@ -11,108 +11,120 @@ namespace bs { namespace protocol { -template -void output_test_start(IO& io, const char* file, size_t line, const char* name, const char* desc) -{ - io.printf(BS_LINE_PREFIX "start file=\"%s\" line=%d name=\"%s\" desc=\"%s\"\n", file, line, name, desc); -} - -template -void output_check_failure(IO& io, size_t line) -{ - io.printf(BS_LINE_PREFIX "check_failure line=%d\n", line); -} - -template -void output_test_end(IO& io, bool success, size_t checks, size_t failed_checks, size_t line=0) -{ - io.printf(BS_LINE_PREFIX "end line=%d result=%d checks=%d failed_checks=%d\n", line, success, checks, failed_checks); -} + template + void output_test_start(IO& io, const char* file, size_t line, const char* name, + const char* desc) + { + io.printf(BS_LINE_PREFIX "start file=\"%s\" line=%d name=\"%s\" desc=\"%s\"\n", file, line, + name, desc); + } -template -void output_menu_begin(IO& io) -{ - io.printf(BS_LINE_PREFIX "menu_begin\n"); -} + template + void output_check_failure(IO& io, size_t line) + { + io.printf(BS_LINE_PREFIX "check_failure line=%d\n", line); + } -template -void output_menu_item(IO& io, int index, const char* name, const char* desc) -{ - io.printf(BS_LINE_PREFIX "item id=%d name=\"%s\" desc=\"%s\"\n", index, name, desc); -} + template + void output_test_end(IO& io, bool success, size_t checks, size_t failed_checks, size_t line = 0) + { + io.printf(BS_LINE_PREFIX "end line=%d result=%d checks=%d failed_checks=%d\n", line, + success, checks, failed_checks); + } -template -void output_menu_end(IO& io) -{ - io.printf(BS_LINE_PREFIX "menu_end\n"); -} + template + void output_menu_begin(IO& io) + { + io.printf(BS_LINE_PREFIX "menu_begin\n"); + } -template -void output_setenv_result(IO& io, const char* key, const char* value) -{ - io.printf(BS_LINE_PREFIX "setenv key=\"%s\" value=\"%s\"\n", key, value); -} + template + void output_menu_item(IO& io, int index, const char* name, const char* desc) + { + io.printf(BS_LINE_PREFIX "item id=%d name=\"%s\" desc=\"%s\"\n", index, name, desc); + } -template -void output_getenv_result(IO& io, const char* key, const char* value) -{ - (void) key; - io.printf(BS_LINE_PREFIX "getenv value=\"%s\"\n", value); -} + template + void output_menu_end(IO& io) + { + io.printf(BS_LINE_PREFIX "menu_end\n"); + } -template -void output_pretest_result(IO& io, bool res) -{ - io.printf(BS_LINE_PREFIX "pretest result=%d\n", res?1:0); -} + template + void output_setenv_result(IO& io, const char* key, const char* value) + { + io.printf(BS_LINE_PREFIX "setenv key=\"%s\" value=\"%s\"\n", key, value); + } -template -bool input_handle(IO& io, char* line_buf, size_t line_buf_size, int& test_num) -{ - int cb_read = io.read_line(line_buf, line_buf_size); - if (cb_read == 0 || line_buf[0] == '\n') { - return false; + template + void output_getenv_result(IO& io, const char* key, const char* value) + { + (void)key; + io.printf(BS_LINE_PREFIX "getenv value=\"%s\"\n", value); } - char* argv[4]; - size_t argc = split_args(line_buf, argv, sizeof(argv)/sizeof(argv[0])); - if (argc == 0) { - return false; + + template + void output_pretest_result(IO& io, bool res) + { + io.printf(BS_LINE_PREFIX "pretest result=%d\n", res ? 1 : 0); } - if (strcmp(argv[0], "setenv") == 0) { - if (argc != 3) { + + template + bool input_handle(IO& io, char* line_buf, size_t line_buf_size, int& test_num) + { + int cb_read = io.read_line(line_buf, line_buf_size); + if (cb_read == 0 || line_buf[0] == '\n') + { return false; } - setenv(argv[1], argv[2], 1); - output_setenv_result(io, argv[1], argv[2]); - test_num = -1; - return false; /* we didn't get the test number yet, so return false */ - } - if (strcmp(argv[0], "getenv") == 0) { - if (argc != 2) { + char* argv[4]; + size_t argc = split_args(line_buf, argv, sizeof(argv) / sizeof(argv[0])); + if (argc == 0) + { return false; } - const char* value = getenv(argv[1]); - output_getenv_result(io, argv[1], (value != NULL) ? value : ""); - return false; - } - if (strcmp(argv[0], "pretest") == 0) { - if (argc != 1) { + if (strcmp(argv[0], "setenv") == 0) + { + if (argc != 3) + { + return false; + } + setenv(argv[1], argv[2], 1); + output_setenv_result(io, argv[1], argv[2]); + test_num = -1; + return false; /* we didn't get the test number yet, so return false */ + } + if (strcmp(argv[0], "getenv") == 0) + { + if (argc != 2) + { + return false; + } + const char* value = getenv(argv[1]); + output_getenv_result(io, argv[1], (value != NULL) ? value : ""); return false; } - bool res = ::pretest(); - output_pretest_result(io, res); - return false; - } - /* not one of the commands, try to parse as test number */ - char* endptr; - test_num = (int) strtol(argv[0], &endptr, 10); - if (endptr != argv[0] + strlen(argv[0])) { - return false; + if (strcmp(argv[0], "pretest") == 0) + { + if (argc != 1) + { + return false; + } + bool res = ::pretest(); + output_pretest_result(io, res); + return false; + } + /* not one of the commands, try to parse as test number */ + char* endptr; + test_num = (int)strtol(argv[0], &endptr, 10); + if (endptr != argv[0] + strlen(argv[0])) + { + return false; + } + return true; } - return true; -} -} // ::protocol -} // ::bs +} // namespace protocol +} // namespace bs -#endif //BS_PROTOCOL_H +#endif // BS_PROTOCOL_H diff --git a/tests/device/libraries/BSTest/src/BSStdio.h b/tests/device/libraries/BSTest/src/BSStdio.h index 4d1bfb56f0..93a29ba7a1 100644 --- a/tests/device/libraries/BSTest/src/BSStdio.h +++ b/tests/device/libraries/BSTest/src/BSStdio.h @@ -9,11 +9,9 @@ namespace bs class StdIOHelper { public: - StdIOHelper() - { - } + StdIOHelper() { } - size_t printf(const char *format, ...) + size_t printf(const char* format, ...) { va_list arg; va_start(arg, format); @@ -25,11 +23,13 @@ class StdIOHelper size_t read_line(char* dest, size_t dest_size) { char* res = fgets(dest, dest_size, stdin); - if (res == NULL) { + if (res == NULL) + { return 0; } size_t len = strlen(dest); - if (dest[len - 1] == '\n') { + if (dest[len - 1] == '\n') + { dest[len - 1] = 0; len--; } @@ -39,10 +39,11 @@ class StdIOHelper typedef StdIOHelper IOHelper; -inline void fatal() { +inline void fatal() +{ throw std::runtime_error("fatal error"); } -} // namespace bs +} // namespace bs -#endif //BS_STDIO_H +#endif // BS_STDIO_H diff --git a/tests/device/libraries/BSTest/src/BSTest.h b/tests/device/libraries/BSTest/src/BSTest.h index 11b4ee9f6e..ba3c9a5c0d 100644 --- a/tests/device/libraries/BSTest/src/BSTest.h +++ b/tests/device/libraries/BSTest/src/BSTest.h @@ -20,15 +20,17 @@ namespace bs { -typedef void(*test_case_func_t)(); +typedef void (*test_case_func_t)(); 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) + 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) { - if (prev) { + if (prev) + { prev->m_next = this; } } @@ -60,36 +62,40 @@ class TestCase const char* desc() const { - return (m_desc)?m_desc:""; + return (m_desc) ? m_desc : ""; } protected: - TestCase* m_next = nullptr; + TestCase* m_next = nullptr; test_case_func_t m_func; - const char* m_file; - size_t m_line; - const char* m_name; - const char* m_desc; + const char* m_file; + size_t m_line; + const char* m_name; + const char* m_desc; }; -struct Registry { - void add(test_case_func_t func, const char* file, size_t line, const char* name, const char* desc) +struct Registry +{ + void add(test_case_func_t func, const char* file, size_t line, const char* name, + const char* desc) { TestCase* tc = new TestCase(m_last, func, file, line, name, desc); - if (!m_first) { + if (!m_first) + { m_first = tc; } m_last = tc; } TestCase* m_first = nullptr; - TestCase* m_last = nullptr; + TestCase* m_last = nullptr; }; -struct Env { - std::function m_check_pass; +struct Env +{ + std::function m_check_pass; std::function m_check_fail; std::function m_fail; - Registry m_registry; + Registry m_registry; }; extern Env g_env; @@ -98,25 +104,27 @@ template class Runner { typedef Runner Tself; + public: Runner(IO& io) : m_io(io) { g_env.m_check_pass = std::bind(&Tself::check_pass, this); g_env.m_check_fail = std::bind(&Tself::check_fail, this, std::placeholders::_1); - g_env.m_fail = std::bind(&Tself::fail, this, std::placeholders::_1); + g_env.m_fail = std::bind(&Tself::fail, this, std::placeholders::_1); } ~Runner() { g_env.m_check_pass = 0; g_env.m_check_fail = 0; - g_env.m_fail = 0; + g_env.m_fail = 0; } void run() { - do { - } while(do_menu()); + do + { + } while (do_menu()); } void check_pass() @@ -132,7 +140,8 @@ class Runner void fail(size_t line) { - protocol::output_test_end(m_io, false, m_check_pass_count + m_check_fail_count, m_check_fail_count, line); + protocol::output_test_end(m_io, false, m_check_pass_count + m_check_fail_count, + m_check_fail_count, line); bs::fatal(); } @@ -141,22 +150,28 @@ class Runner { protocol::output_menu_begin(m_io); int id = 1; - for (TestCase* tc = g_env.m_registry.m_first; tc; tc = tc->next(), ++id) { + for (TestCase* tc = g_env.m_registry.m_first; tc; tc = tc->next(), ++id) + { protocol::output_menu_item(m_io, id, tc->name(), tc->desc()); } protocol::output_menu_end(m_io); - while(true) { - int id; + while (true) + { + int id; char line_buf[BS_LINE_BUF_SIZE]; - if (!protocol::input_handle(m_io, line_buf, sizeof(line_buf), id)) { + if (!protocol::input_handle(m_io, line_buf, sizeof(line_buf), id)) + { continue; } - if (id < 0) { + if (id < 0) + { return true; } TestCase* tc = g_env.m_registry.m_first; - for (int i = 0; i != id - 1 && tc; ++i, tc = tc->next()); - if (!tc) { + for (int i = 0; i != id - 1 && tc; ++i, tc = tc->next()) + ; + if (!tc) + { bs::fatal(); } m_check_pass_count = 0; @@ -164,12 +179,13 @@ class Runner protocol::output_test_start(m_io, tc->file(), tc->line(), tc->name(), tc->desc()); tc->run(); bool success = m_check_fail_count == 0; - protocol::output_test_end(m_io, success, m_check_pass_count + m_check_fail_count, m_check_fail_count); + protocol::output_test_end(m_io, success, m_check_pass_count + m_check_fail_count, + m_check_fail_count); } } protected: - IO& m_io; + IO& m_io; size_t m_check_pass_count; size_t m_check_fail_count; }; @@ -177,7 +193,8 @@ class Runner class AutoReg { public: - AutoReg(test_case_func_t func, const char* file, size_t line, const char* name, const char* desc = nullptr) + AutoReg(test_case_func_t func, const char* file, size_t line, const char* name, + const char* desc = nullptr) { g_env.m_registry.add(func, file, line, name, desc); } @@ -185,39 +202,59 @@ class AutoReg inline void check(bool condition, size_t line) { - if (!condition) { + if (!condition) + { g_env.m_check_fail(line); - } else { + } + else + { g_env.m_check_pass(); } } inline void require(bool condition, size_t line) { - if (!condition) { + if (!condition) + { g_env.m_check_fail(line); g_env.m_fail(line); - } else { + } + else + { g_env.m_check_pass(); } } -} // ::bs +} // namespace bs -#define BS_NAME_LINE2( name, line ) name##line -#define BS_NAME_LINE( name, line ) BS_NAME_LINE2( name, line ) -#define BS_UNIQUE_NAME( name ) BS_NAME_LINE( name, __LINE__ ) +#define BS_NAME_LINE2(name, line) name##line +#define BS_NAME_LINE(name, line) BS_NAME_LINE2(name, line) +#define BS_UNIQUE_NAME(name) BS_NAME_LINE(name, __LINE__) -#define TEST_CASE( ... ) \ - static void BS_UNIQUE_NAME( TEST_FUNC__ )(); \ - namespace{ bs::AutoReg BS_UNIQUE_NAME( test_autoreg__ )( &BS_UNIQUE_NAME( TEST_FUNC__ ), __FILE__, __LINE__, __VA_ARGS__ ); }\ - static void BS_UNIQUE_NAME( TEST_FUNC__ )() +#define TEST_CASE(...) \ + static void BS_UNIQUE_NAME(TEST_FUNC__)(); \ + namespace \ + { \ + bs::AutoReg BS_UNIQUE_NAME(test_autoreg__)(&BS_UNIQUE_NAME(TEST_FUNC__), __FILE__, \ + __LINE__, __VA_ARGS__); \ + } \ + static void BS_UNIQUE_NAME(TEST_FUNC__)() #define CHECK(condition) bs::check((condition), __LINE__) #define REQUIRE(condition) bs::require((condition), __LINE__) #define FAIL() bs::g_env.m_fail(__LINE__) -#define BS_ENV_DECLARE() namespace bs { Env g_env; } -#define BS_RUN(...) do { bs::IOHelper helper = bs::IOHelper(__VA_ARGS__); bs::Runner runner(helper); runner.run(); } while(0); - -#endif //BSTEST_H +#define BS_ENV_DECLARE() \ + namespace bs \ + { \ + Env g_env; \ + } +#define BS_RUN(...) \ + do \ + { \ + bs::IOHelper helper = bs::IOHelper(__VA_ARGS__); \ + bs::Runner runner(helper); \ + runner.run(); \ + } while (0); + +#endif // BSTEST_H diff --git a/tests/device/libraries/BSTest/test/test.cpp b/tests/device/libraries/BSTest/test/test.cpp index 863373e0de..6f0c253417 100644 --- a/tests/device/libraries/BSTest/test/test.cpp +++ b/tests/device/libraries/BSTest/test/test.cpp @@ -3,21 +3,23 @@ BS_ENV_DECLARE(); - int main() { - while(true) { - try{ + while (true) + { + try + { BS_RUN(); return 0; - }catch(...) { + } + catch (...) + { printf("Exception\n\n"); } } return 1; } - TEST_CASE("this test runs successfully", "[bluesmoke]") { CHECK(1 + 1 == 2); @@ -38,17 +40,12 @@ TEST_CASE("another test which fails and crashes", "[bluesmoke][fail]") REQUIRE(false); } - TEST_CASE("third test which should be skipped", "[.]") { FAIL(); } - -TEST_CASE("this test also runs successfully", "[bluesmoke]") -{ - -} +TEST_CASE("this test also runs successfully", "[bluesmoke]") { } TEST_CASE("environment variables can be set and read from python", "[bluesmoke]") { @@ -57,4 +54,3 @@ TEST_CASE("environment variables can be set and read from python", "[bluesmoke]" CHECK(strcmp(res, "42") == 0); setenv("VAR_FROM_TEST", "24", 1); } - diff --git a/tests/device/test_libc/libm_string.c b/tests/device/test_libc/libm_string.c index 17f29098cd..9a19da58a4 100644 --- a/tests/device/test_libc/libm_string.c +++ b/tests/device/test_libc/libm_string.c @@ -3,8 +3,6 @@ #include #include - - #define memcmp memcmp_P #define memcpy memcpy_P #define memmem memmem_P @@ -18,416 +16,404 @@ #define strcmp strcmp_P #define strncmp strncmp_P - -_CONST char *it = ""; /* Routine name for message routines. */ -static int errors = 0; +_CONST char* it = ""; /* Routine name for message routines. */ +static int errors = 0; /* Complain if condition is not true. */ #define check(thing) checkit(thing, __LINE__) -static void -_DEFUN(checkit,(ok,l), - int ok _AND - int l ) +static void _DEFUN(checkit, (ok, l), int ok _AND int l) { -// newfunc(it); -// line(l); - - if (!ok) - { - printf("string.c:%d %s\n", l, it); - ++errors; - } -} - + // newfunc(it); + // line(l); + if (!ok) + { + printf("string.c:%d %s\n", l, it); + ++errors; + } +} /* Complain if first two args don't strcmp as equal. */ -#define equal(a, b) funcqual(a,b,__LINE__); +#define equal(a, b) funcqual(a, b, __LINE__); -static void -_DEFUN(funcqual,(a,b,l), - char *a _AND - char *b _AND - int l) +static void _DEFUN(funcqual, (a, b, l), char* a _AND char* b _AND int l) { -// newfunc(it); - -// line(l); - if (a == NULL && b == NULL) return; - if (strcmp(a,b)) { - printf("string.c:%d (%s)\n", l, it); + // newfunc(it); + + // line(l); + if (a == NULL && b == NULL) + return; + if (strcmp(a, b)) + { + printf("string.c:%d (%s)\n", l, it); } } - - static char one[50]; static char two[50]; - void libm_test_string() { - /* Test strcmp first because we use it to test other things. */ - it = "strcmp"; - check(strcmp("", "") == 0); /* Trivial case. */ - check(strcmp("a", "a") == 0); /* Identity. */ - check(strcmp("abc", "abc") == 0); /* Multicharacter. */ - check(strcmp("abc", "abcd") < 0); /* Length mismatches. */ - check(strcmp("abcd", "abc") > 0); - check(strcmp("abcd", "abce") < 0); /* Honest miscompares. */ - check(strcmp("abce", "abcd") > 0); - check(strcmp("a\103", "a") > 0); /* Tricky if char signed. */ - check(strcmp("a\103", "a\003") > 0); - - /* Test strcpy next because we need it to set up other tests. */ - it = "strcpy"; - check(strcpy(one, "abcd") == one); /* Returned value. */ - equal(one, "abcd"); /* Basic test. */ - - (void) strcpy(one, "x"); - equal(one, "x"); /* Writeover. */ - equal(one+2, "cd"); /* Wrote too much? */ - - (void) strcpy(two, "hi there"); - (void) strcpy(one, two); - equal(one, "hi there"); /* Basic test encore. */ - equal(two, "hi there"); /* Stomped on source? */ - - (void) strcpy(one, ""); - equal(one, ""); /* Boundary condition. */ - - /* strcat. */ - it = "strcat"; - (void) strcpy(one, "ijk"); - check(strcat(one, "lmn") == one); /* Returned value. */ - equal(one, "ijklmn"); /* Basic test. */ - - (void) strcpy(one, "x"); - (void) strcat(one, "yz"); - equal(one, "xyz"); /* Writeover. */ - equal(one+4, "mn"); /* Wrote too much? */ - - (void) strcpy(one, "gh"); - (void) strcpy(two, "ef"); - (void) strcat(one, two); - equal(one, "ghef"); /* Basic test encore. */ - equal(two, "ef"); /* Stomped on source? */ - - (void) strcpy(one, ""); - (void) strcat(one, ""); - equal(one, ""); /* Boundary conditions. */ - (void) strcpy(one, "ab"); - (void) strcat(one, ""); - equal(one, "ab"); - (void) strcpy(one, ""); - (void) strcat(one, "cd"); - equal(one, "cd"); - - /* strncat - first test it as strcat, with big counts, - then test the count mechanism. */ - it = "strncat"; - (void) strcpy(one, "ijk"); - check(strncat(one, "lmn", 99) == one); /* Returned value. */ - equal(one, "ijklmn"); /* Basic test. */ - - (void) strcpy(one, "x"); - (void) strncat(one, "yz", 99); - equal(one, "xyz"); /* Writeover. */ - equal(one+4, "mn"); /* Wrote too much? */ - - (void) strcpy(one, "gh"); - (void) strcpy(two, "ef"); - (void) strncat(one, two, 99); - equal(one, "ghef"); /* Basic test encore. */ - equal(two, "ef"); /* Stomped on source? */ - - (void) strcpy(one, ""); - (void) strncat(one, "", 99); - equal(one, ""); /* Boundary conditions. */ - (void) strcpy(one, "ab"); - (void) strncat(one, "", 99); - equal(one, "ab"); - (void) strcpy(one, ""); - (void) strncat(one, "cd", 99); - equal(one, "cd"); - - (void) strcpy(one, "ab"); - (void) strncat(one, "cdef", 2); - equal(one, "abcd"); /* Count-limited. */ - - (void) strncat(one, "gh", 0); - equal(one, "abcd"); /* Zero count. */ - - (void) strncat(one, "gh", 2); - equal(one, "abcdgh"); /* Count _AND length equal. */ - it = "strncmp"; - /* strncmp - first test as strcmp with big counts";*/ - check(strncmp("", "", 99) == 0); /* Trivial case. */ - check(strncmp("a", "a", 99) == 0); /* Identity. */ - check(strncmp("abc", "abc", 99) == 0); /* Multicharacter. */ - check(strncmp("abc", "abcd", 99) < 0); /* Length unequal. */ - check(strncmp("abcd", "abc",99) > 0); - check(strncmp("abcd", "abce", 99) < 0); /* Honestly unequal. */ - check(strncmp("abce", "abcd",99)>0); - check(strncmp("abce", "abcd", 3) == 0); /* Count limited. */ - check(strncmp("abce", "abc", 3) == 0); /* Count == length. */ - check(strncmp("abcd", "abce", 4) < 0); /* Nudging limit. */ - check(strncmp("abc", "def", 0) == 0); /* Zero count. */ - - /* strncpy - testing is a bit different because of odd semantics. */ - it = "strncpy"; - check(strncpy(one, "abc", 4) == one); /* Returned value. */ - equal(one, "abc"); /* Did the copy go right? */ - - (void) strcpy(one, "abcdefgh"); - (void) strncpy(one, "xyz", 2); - equal(one, "xycdefgh"); /* Copy cut by count. */ - - (void) strcpy(one, "abcdefgh"); - (void) strncpy(one, "xyz", 3); /* Copy cut just before NUL. */ - equal(one, "xyzdefgh"); - - (void) strcpy(one, "abcdefgh"); - (void) strncpy(one, "xyz", 4); /* Copy just includes NUL. */ - equal(one, "xyz"); - equal(one+4, "efgh"); /* Wrote too much? */ - - (void) strcpy(one, "abcdefgh"); - (void) strncpy(one, "xyz", 5); /* Copy includes padding. */ - equal(one, "xyz"); - equal(one+4, ""); - equal(one+5, "fgh"); - - (void) strcpy(one, "abc"); - (void) strncpy(one, "xyz", 0); /* Zero-length copy. */ - equal(one, "abc"); - - (void) strncpy(one, "", 2); /* Zero-length source. */ - equal(one, ""); - equal(one+1, ""); - equal(one+2, "c"); - - (void) strcpy(one, "hi there"); - (void) strncpy(two, one, 9); - equal(two, "hi there"); /* Just paranoia. */ - equal(one, "hi there"); /* Stomped on source? */ - - /* strlen. */ - it = "strlen"; - check(strlen("") == 0); /* Empty. */ - check(strlen("a") == 1); /* Single char. */ - check(strlen("abcd") == 4); /* Multiple chars. */ - - /* strchr. */ - it = "strchr"; - check(strchr("abcd", 'z') == NULL); /* Not found. */ - (void) strcpy(one, "abcd"); - check(strchr(one, 'c') == one+2); /* Basic test. */ - check(strchr(one, 'd') == one+3); /* End of string. */ - check(strchr(one, 'a') == one); /* Beginning. */ - check(strchr(one, '\0') == one+4); /* Finding NUL. */ - (void) strcpy(one, "ababa"); - check(strchr(one, 'b') == one+1); /* Finding first. */ - (void) strcpy(one, ""); - check(strchr(one, 'b') == NULL); /* Empty string. */ - check(strchr(one, '\0') == one); /* NUL in empty string. */ - - /* index - just like strchr. */ - it = "index"; - check(index("abcd", 'z') == NULL); /* Not found. */ - (void) strcpy(one, "abcd"); - check(index(one, 'c') == one+2); /* Basic test. */ - check(index(one, 'd') == one+3); /* End of string. */ - check(index(one, 'a') == one); /* Beginning. */ - check(index(one, '\0') == one+4); /* Finding NUL. */ - (void) strcpy(one, "ababa"); - check(index(one, 'b') == one+1); /* Finding first. */ - (void) strcpy(one, ""); - check(index(one, 'b') == NULL); /* Empty string. */ - check(index(one, '\0') == one); /* NUL in empty string. */ - - /* strrchr. */ - it = "strrchr"; - check(strrchr("abcd", 'z') == NULL); /* Not found. */ - (void) strcpy(one, "abcd"); - check(strrchr(one, 'c') == one+2); /* Basic test. */ - check(strrchr(one, 'd') == one+3); /* End of string. */ - check(strrchr(one, 'a') == one); /* Beginning. */ - check(strrchr(one, '\0') == one+4); /* Finding NUL. */ - (void) strcpy(one, "ababa"); - check(strrchr(one, 'b') == one+3); /* Finding last. */ - (void) strcpy(one, ""); - check(strrchr(one, 'b') == NULL); /* Empty string. */ - check(strrchr(one, '\0') == one); /* NUL in empty string. */ - - /* rindex - just like strrchr. */ - it = "rindex"; - check(rindex("abcd", 'z') == NULL); /* Not found. */ - (void) strcpy(one, "abcd"); - check(rindex(one, 'c') == one+2); /* Basic test. */ - check(rindex(one, 'd') == one+3); /* End of string. */ - check(rindex(one, 'a') == one); /* Beginning. */ - check(rindex(one, '\0') == one+4); /* Finding NUL. */ - (void) strcpy(one, "ababa"); - check(rindex(one, 'b') == one+3); /* Finding last. */ - (void) strcpy(one, ""); - check(rindex(one, 'b') == NULL); /* Empty string. */ - check(rindex(one, '\0') == one); /* NUL in empty string. */ - - /* strpbrk - somewhat like strchr. */ - it = "strpbrk"; - check(strpbrk("abcd", "z") == NULL); /* Not found. */ - (void) strcpy(one, "abcd"); - check(strpbrk(one, "c") == one+2); /* Basic test. */ - check(strpbrk(one, "d") == one+3); /* End of string. */ - check(strpbrk(one, "a") == one); /* Beginning. */ - check(strpbrk(one, "") == NULL); /* Empty search list. */ - check(strpbrk(one, "cb") == one+1); /* Multiple search. */ - (void) strcpy(one, "abcabdea"); - check(strpbrk(one, "b") == one+1); /* Finding first. */ - check(strpbrk(one, "cb") == one+1); /* With multiple search. */ - check(strpbrk(one, "db") == one+1); /* Another variant. */ - (void) strcpy(one, ""); - check(strpbrk(one, "bc") == NULL); /* Empty string. */ - check(strpbrk(one, "") == NULL); /* Both strings empty. */ - - /* strstr - somewhat like strchr. */ - it = "strstr"; - check(strstr("z", "abcd") == NULL); /* Not found. */ - check(strstr("abx", "abcd") == NULL); /* Dead end. */ - (void) strcpy(one, "abcd"); - check(strstr(one,"c") == one+2); /* Basic test. */ - check(strstr(one, "bc") == one+1); /* Multichar. */ - check(strstr(one,"d") == one+3); /* End of string. */ - check(strstr(one,"cd") == one+2); /* Tail of string. */ - check(strstr(one,"abc") == one); /* Beginning. */ - check(strstr(one,"abcd") == one); /* Exact match. */ - check(strstr(one,"de") == NULL); /* Past end. */ - check(strstr(one,"") == one); /* Finding empty. */ - (void) strcpy(one, "ababa"); - check(strstr(one,"ba") == one+1); /* Finding first. */ - (void) strcpy(one, ""); - check(strstr(one, "b") == NULL); /* Empty string. */ - check(strstr(one,"") == one); /* Empty in empty string. */ - (void) strcpy(one, "bcbca"); - check(strstr(one,"bca") == one+2); /* False start. */ - (void) strcpy(one, "bbbcabbca"); - check(strstr(one,"bbca") == one+1); /* With overlap. */ - - /* strspn. */ - it = "strspn"; - check(strspn("abcba", "abc") == 5); /* Whole string. */ - check(strspn("abcba", "ab") == 2); /* Partial. */ - check(strspn("abc", "qx") == 0); /* None. */ - check(strspn("", "ab") == 0); /* Null string. */ - check(strspn("abc", "") == 0); /* Null search list. */ - - /* strcspn. */ - it = "strcspn"; - check(strcspn("abcba", "qx") == 5); /* Whole string. */ - check(strcspn("abcba", "cx") == 2); /* Partial. */ - check(strcspn("abc", "abc") == 0); /* None. */ - check(strcspn("", "ab") == 0); /* Null string. */ - check(strcspn("abc", "") == 3); /* Null search list. */ - - /* strtok - the hard one. */ - it = "strtok"; - (void) strcpy(one, "first, second, third"); - equal(strtok(one, ", "), "first"); /* Basic test. */ - equal(one, "first"); - equal(strtok((char *)NULL, ", "), "second"); - equal(strtok((char *)NULL, ", "), "third"); - check(strtok((char *)NULL, ", ") == NULL); - (void) strcpy(one, ", first, "); - equal(strtok(one, ", "), "first"); /* Extra delims, 1 tok. */ - check(strtok((char *)NULL, ", ") == NULL); - (void) strcpy(one, "1a, 1b; 2a, 2b"); - equal(strtok(one, ", "), "1a"); /* Changing delim lists. */ - equal(strtok((char *)NULL, "; "), "1b"); - equal(strtok((char *)NULL, ", "), "2a"); - (void) strcpy(two, "x-y"); - equal(strtok(two, "-"), "x"); /* New string before done. */ - equal(strtok((char *)NULL, "-"), "y"); - check(strtok((char *)NULL, "-") == NULL); - (void) strcpy(one, "a,b, c,, ,d"); - equal(strtok(one, ", "), "a"); /* Different separators. */ - equal(strtok((char *)NULL, ", "), "b"); - equal(strtok((char *)NULL, " ,"), "c"); /* Permute list too. */ - equal(strtok((char *)NULL, " ,"), "d"); - check(strtok((char *)NULL, ", ") == NULL); - check(strtok((char *)NULL, ", ") == NULL); /* Persistence. */ - (void) strcpy(one, ", "); - check(strtok(one, ", ") == NULL); /* No tokens. */ - (void) strcpy(one, ""); - check(strtok(one, ", ") == NULL); /* Empty string. */ - (void) strcpy(one, "abc"); - equal(strtok(one, ", "), "abc"); /* No delimiters. */ - check(strtok((char *)NULL, ", ") == NULL); - (void) strcpy(one, "abc"); - equal(strtok(one, ""), "abc"); /* Empty delimiter list. */ - check(strtok((char *)NULL, "") == NULL); - (void) strcpy(one, "abcdefgh"); - (void) strcpy(one, "a,b,c"); - equal(strtok(one, ","), "a"); /* Basics again... */ - equal(strtok((char *)NULL, ","), "b"); - equal(strtok((char *)NULL, ","), "c"); - check(strtok((char *)NULL, ",") == NULL); - equal(one+6, "gh"); /* Stomped past end? */ - equal(one, "a"); /* Stomped old tokens? */ - equal(one+2, "b"); - equal(one+4, "c"); - - /* memcmp. */ - it = "memcmp"; - check(memcmp("a", "a", 1) == 0); /* Identity. */ - check(memcmp("abc", "abc", 3) == 0); /* Multicharacter. */ - check(memcmp("abcd", "abce", 4) < 0); /* Honestly unequal. */ - check(memcmp("abce", "abcd",4)); - check(memcmp("alph", "beta", 4) < 0); - check(memcmp("abce", "abcd", 3) == 0); /* Count limited. */ - check(memcmp("abc", "def", 0) == 0); /* Zero count. */ - - /* memcmp should test strings as unsigned */ - one[0] = 0xfe; - two[0] = 0x03; - check(memcmp(one, two,1) > 0); - - - /* memchr. */ - it = "memchr"; - check(memchr("abcd", 'z', 4) == NULL); /* Not found. */ - (void) strcpy(one, "abcd"); - check(memchr(one, 'c', 4) == one+2); /* Basic test. */ - check(memchr(one, 'd', 4) == one+3); /* End of string. */ - check(memchr(one, 'a', 4) == one); /* Beginning. */ - check(memchr(one, '\0', 5) == one+4); /* Finding NUL. */ - (void) strcpy(one, "ababa"); - check(memchr(one, 'b', 5) == one+1); /* Finding first. */ - check(memchr(one, 'b', 0) == NULL); /* Zero count. */ - check(memchr(one, 'a', 1) == one); /* Singleton case. */ - (void) strcpy(one, "a\203b"); - check(memchr(one, 0203, 3) == one+1); /* Unsignedness. */ - - /* memcpy - need not work for overlap. */ - it = "memcpy"; - check(memcpy(one, "abc", 4) == one); /* Returned value. */ - equal(one, "abc"); /* Did the copy go right? */ - - (void) strcpy(one, "abcdefgh"); - (void) memcpy(one+1, "xyz", 2); - equal(one, "axydefgh"); /* Basic test. */ - - (void) strcpy(one, "abc"); - (void) memcpy(one, "xyz", 0); - equal(one, "abc"); /* Zero-length copy. */ - - (void) strcpy(one, "hi there"); - (void) strcpy(two, "foo"); - (void) memcpy(two, one, 9); - equal(two, "hi there"); /* Just paranoia. */ - equal(one, "hi there"); /* Stomped on source? */ + /* Test strcmp first because we use it to test other things. */ + it = "strcmp"; + check(strcmp("", "") == 0); /* Trivial case. */ + check(strcmp("a", "a") == 0); /* Identity. */ + check(strcmp("abc", "abc") == 0); /* Multicharacter. */ + check(strcmp("abc", "abcd") < 0); /* Length mismatches. */ + check(strcmp("abcd", "abc") > 0); + check(strcmp("abcd", "abce") < 0); /* Honest miscompares. */ + check(strcmp("abce", "abcd") > 0); + check(strcmp("a\103", "a") > 0); /* Tricky if char signed. */ + check(strcmp("a\103", "a\003") > 0); + + /* Test strcpy next because we need it to set up other tests. */ + it = "strcpy"; + check(strcpy(one, "abcd") == one); /* Returned value. */ + equal(one, "abcd"); /* Basic test. */ + + (void)strcpy(one, "x"); + equal(one, "x"); /* Writeover. */ + equal(one + 2, "cd"); /* Wrote too much? */ + + (void)strcpy(two, "hi there"); + (void)strcpy(one, two); + equal(one, "hi there"); /* Basic test encore. */ + equal(two, "hi there"); /* Stomped on source? */ + + (void)strcpy(one, ""); + equal(one, ""); /* Boundary condition. */ + + /* strcat. */ + it = "strcat"; + (void)strcpy(one, "ijk"); + check(strcat(one, "lmn") == one); /* Returned value. */ + equal(one, "ijklmn"); /* Basic test. */ + + (void)strcpy(one, "x"); + (void)strcat(one, "yz"); + equal(one, "xyz"); /* Writeover. */ + equal(one + 4, "mn"); /* Wrote too much? */ + + (void)strcpy(one, "gh"); + (void)strcpy(two, "ef"); + (void)strcat(one, two); + equal(one, "ghef"); /* Basic test encore. */ + equal(two, "ef"); /* Stomped on source? */ + + (void)strcpy(one, ""); + (void)strcat(one, ""); + equal(one, ""); /* Boundary conditions. */ + (void)strcpy(one, "ab"); + (void)strcat(one, ""); + equal(one, "ab"); + (void)strcpy(one, ""); + (void)strcat(one, "cd"); + equal(one, "cd"); + + /* strncat - first test it as strcat, with big counts, + then test the count mechanism. */ + it = "strncat"; + (void)strcpy(one, "ijk"); + check(strncat(one, "lmn", 99) == one); /* Returned value. */ + equal(one, "ijklmn"); /* Basic test. */ + + (void)strcpy(one, "x"); + (void)strncat(one, "yz", 99); + equal(one, "xyz"); /* Writeover. */ + equal(one + 4, "mn"); /* Wrote too much? */ + + (void)strcpy(one, "gh"); + (void)strcpy(two, "ef"); + (void)strncat(one, two, 99); + equal(one, "ghef"); /* Basic test encore. */ + equal(two, "ef"); /* Stomped on source? */ + + (void)strcpy(one, ""); + (void)strncat(one, "", 99); + equal(one, ""); /* Boundary conditions. */ + (void)strcpy(one, "ab"); + (void)strncat(one, "", 99); + equal(one, "ab"); + (void)strcpy(one, ""); + (void)strncat(one, "cd", 99); + equal(one, "cd"); + + (void)strcpy(one, "ab"); + (void)strncat(one, "cdef", 2); + equal(one, "abcd"); /* Count-limited. */ + + (void)strncat(one, "gh", 0); + equal(one, "abcd"); /* Zero count. */ + + (void)strncat(one, "gh", 2); + equal(one, "abcdgh"); /* Count _AND length equal. */ + it = "strncmp"; + /* strncmp - first test as strcmp with big counts";*/ + check(strncmp("", "", 99) == 0); /* Trivial case. */ + check(strncmp("a", "a", 99) == 0); /* Identity. */ + check(strncmp("abc", "abc", 99) == 0); /* Multicharacter. */ + check(strncmp("abc", "abcd", 99) < 0); /* Length unequal. */ + check(strncmp("abcd", "abc", 99) > 0); + check(strncmp("abcd", "abce", 99) < 0); /* Honestly unequal. */ + check(strncmp("abce", "abcd", 99) > 0); + check(strncmp("abce", "abcd", 3) == 0); /* Count limited. */ + check(strncmp("abce", "abc", 3) == 0); /* Count == length. */ + check(strncmp("abcd", "abce", 4) < 0); /* Nudging limit. */ + check(strncmp("abc", "def", 0) == 0); /* Zero count. */ + + /* strncpy - testing is a bit different because of odd semantics. */ + it = "strncpy"; + check(strncpy(one, "abc", 4) == one); /* Returned value. */ + equal(one, "abc"); /* Did the copy go right? */ + + (void)strcpy(one, "abcdefgh"); + (void)strncpy(one, "xyz", 2); + equal(one, "xycdefgh"); /* Copy cut by count. */ + + (void)strcpy(one, "abcdefgh"); + (void)strncpy(one, "xyz", 3); /* Copy cut just before NUL. */ + equal(one, "xyzdefgh"); + + (void)strcpy(one, "abcdefgh"); + (void)strncpy(one, "xyz", 4); /* Copy just includes NUL. */ + equal(one, "xyz"); + equal(one + 4, "efgh"); /* Wrote too much? */ + + (void)strcpy(one, "abcdefgh"); + (void)strncpy(one, "xyz", 5); /* Copy includes padding. */ + equal(one, "xyz"); + equal(one + 4, ""); + equal(one + 5, "fgh"); + + (void)strcpy(one, "abc"); + (void)strncpy(one, "xyz", 0); /* Zero-length copy. */ + equal(one, "abc"); + + (void)strncpy(one, "", 2); /* Zero-length source. */ + equal(one, ""); + equal(one + 1, ""); + equal(one + 2, "c"); + + (void)strcpy(one, "hi there"); + (void)strncpy(two, one, 9); + equal(two, "hi there"); /* Just paranoia. */ + equal(one, "hi there"); /* Stomped on source? */ + + /* strlen. */ + it = "strlen"; + check(strlen("") == 0); /* Empty. */ + check(strlen("a") == 1); /* Single char. */ + check(strlen("abcd") == 4); /* Multiple chars. */ + + /* strchr. */ + it = "strchr"; + check(strchr("abcd", 'z') == NULL); /* Not found. */ + (void)strcpy(one, "abcd"); + check(strchr(one, 'c') == one + 2); /* Basic test. */ + check(strchr(one, 'd') == one + 3); /* End of string. */ + check(strchr(one, 'a') == one); /* Beginning. */ + check(strchr(one, '\0') == one + 4); /* Finding NUL. */ + (void)strcpy(one, "ababa"); + check(strchr(one, 'b') == one + 1); /* Finding first. */ + (void)strcpy(one, ""); + check(strchr(one, 'b') == NULL); /* Empty string. */ + check(strchr(one, '\0') == one); /* NUL in empty string. */ + + /* index - just like strchr. */ + it = "index"; + check(index("abcd", 'z') == NULL); /* Not found. */ + (void)strcpy(one, "abcd"); + check(index(one, 'c') == one + 2); /* Basic test. */ + check(index(one, 'd') == one + 3); /* End of string. */ + check(index(one, 'a') == one); /* Beginning. */ + check(index(one, '\0') == one + 4); /* Finding NUL. */ + (void)strcpy(one, "ababa"); + check(index(one, 'b') == one + 1); /* Finding first. */ + (void)strcpy(one, ""); + check(index(one, 'b') == NULL); /* Empty string. */ + check(index(one, '\0') == one); /* NUL in empty string. */ + + /* strrchr. */ + it = "strrchr"; + check(strrchr("abcd", 'z') == NULL); /* Not found. */ + (void)strcpy(one, "abcd"); + check(strrchr(one, 'c') == one + 2); /* Basic test. */ + check(strrchr(one, 'd') == one + 3); /* End of string. */ + check(strrchr(one, 'a') == one); /* Beginning. */ + check(strrchr(one, '\0') == one + 4); /* Finding NUL. */ + (void)strcpy(one, "ababa"); + check(strrchr(one, 'b') == one + 3); /* Finding last. */ + (void)strcpy(one, ""); + check(strrchr(one, 'b') == NULL); /* Empty string. */ + check(strrchr(one, '\0') == one); /* NUL in empty string. */ + + /* rindex - just like strrchr. */ + it = "rindex"; + check(rindex("abcd", 'z') == NULL); /* Not found. */ + (void)strcpy(one, "abcd"); + check(rindex(one, 'c') == one + 2); /* Basic test. */ + check(rindex(one, 'd') == one + 3); /* End of string. */ + check(rindex(one, 'a') == one); /* Beginning. */ + check(rindex(one, '\0') == one + 4); /* Finding NUL. */ + (void)strcpy(one, "ababa"); + check(rindex(one, 'b') == one + 3); /* Finding last. */ + (void)strcpy(one, ""); + check(rindex(one, 'b') == NULL); /* Empty string. */ + check(rindex(one, '\0') == one); /* NUL in empty string. */ + + /* strpbrk - somewhat like strchr. */ + it = "strpbrk"; + check(strpbrk("abcd", "z") == NULL); /* Not found. */ + (void)strcpy(one, "abcd"); + check(strpbrk(one, "c") == one + 2); /* Basic test. */ + check(strpbrk(one, "d") == one + 3); /* End of string. */ + check(strpbrk(one, "a") == one); /* Beginning. */ + check(strpbrk(one, "") == NULL); /* Empty search list. */ + check(strpbrk(one, "cb") == one + 1); /* Multiple search. */ + (void)strcpy(one, "abcabdea"); + check(strpbrk(one, "b") == one + 1); /* Finding first. */ + check(strpbrk(one, "cb") == one + 1); /* With multiple search. */ + check(strpbrk(one, "db") == one + 1); /* Another variant. */ + (void)strcpy(one, ""); + check(strpbrk(one, "bc") == NULL); /* Empty string. */ + check(strpbrk(one, "") == NULL); /* Both strings empty. */ + + /* strstr - somewhat like strchr. */ + it = "strstr"; + check(strstr("z", "abcd") == NULL); /* Not found. */ + check(strstr("abx", "abcd") == NULL); /* Dead end. */ + (void)strcpy(one, "abcd"); + check(strstr(one, "c") == one + 2); /* Basic test. */ + check(strstr(one, "bc") == one + 1); /* Multichar. */ + check(strstr(one, "d") == one + 3); /* End of string. */ + check(strstr(one, "cd") == one + 2); /* Tail of string. */ + check(strstr(one, "abc") == one); /* Beginning. */ + check(strstr(one, "abcd") == one); /* Exact match. */ + check(strstr(one, "de") == NULL); /* Past end. */ + check(strstr(one, "") == one); /* Finding empty. */ + (void)strcpy(one, "ababa"); + check(strstr(one, "ba") == one + 1); /* Finding first. */ + (void)strcpy(one, ""); + check(strstr(one, "b") == NULL); /* Empty string. */ + check(strstr(one, "") == one); /* Empty in empty string. */ + (void)strcpy(one, "bcbca"); + check(strstr(one, "bca") == one + 2); /* False start. */ + (void)strcpy(one, "bbbcabbca"); + check(strstr(one, "bbca") == one + 1); /* With overlap. */ + + /* strspn. */ + it = "strspn"; + check(strspn("abcba", "abc") == 5); /* Whole string. */ + check(strspn("abcba", "ab") == 2); /* Partial. */ + check(strspn("abc", "qx") == 0); /* None. */ + check(strspn("", "ab") == 0); /* Null string. */ + check(strspn("abc", "") == 0); /* Null search list. */ + + /* strcspn. */ + it = "strcspn"; + check(strcspn("abcba", "qx") == 5); /* Whole string. */ + check(strcspn("abcba", "cx") == 2); /* Partial. */ + check(strcspn("abc", "abc") == 0); /* None. */ + check(strcspn("", "ab") == 0); /* Null string. */ + check(strcspn("abc", "") == 3); /* Null search list. */ + + /* strtok - the hard one. */ + it = "strtok"; + (void)strcpy(one, "first, second, third"); + equal(strtok(one, ", "), "first"); /* Basic test. */ + equal(one, "first"); + equal(strtok((char*)NULL, ", "), "second"); + equal(strtok((char*)NULL, ", "), "third"); + check(strtok((char*)NULL, ", ") == NULL); + (void)strcpy(one, ", first, "); + equal(strtok(one, ", "), "first"); /* Extra delims, 1 tok. */ + check(strtok((char*)NULL, ", ") == NULL); + (void)strcpy(one, "1a, 1b; 2a, 2b"); + equal(strtok(one, ", "), "1a"); /* Changing delim lists. */ + equal(strtok((char*)NULL, "; "), "1b"); + equal(strtok((char*)NULL, ", "), "2a"); + (void)strcpy(two, "x-y"); + equal(strtok(two, "-"), "x"); /* New string before done. */ + equal(strtok((char*)NULL, "-"), "y"); + check(strtok((char*)NULL, "-") == NULL); + (void)strcpy(one, "a,b, c,, ,d"); + equal(strtok(one, ", "), "a"); /* Different separators. */ + equal(strtok((char*)NULL, ", "), "b"); + equal(strtok((char*)NULL, " ,"), "c"); /* Permute list too. */ + equal(strtok((char*)NULL, " ,"), "d"); + check(strtok((char*)NULL, ", ") == NULL); + check(strtok((char*)NULL, ", ") == NULL); /* Persistence. */ + (void)strcpy(one, ", "); + check(strtok(one, ", ") == NULL); /* No tokens. */ + (void)strcpy(one, ""); + check(strtok(one, ", ") == NULL); /* Empty string. */ + (void)strcpy(one, "abc"); + equal(strtok(one, ", "), "abc"); /* No delimiters. */ + check(strtok((char*)NULL, ", ") == NULL); + (void)strcpy(one, "abc"); + equal(strtok(one, ""), "abc"); /* Empty delimiter list. */ + check(strtok((char*)NULL, "") == NULL); + (void)strcpy(one, "abcdefgh"); + (void)strcpy(one, "a,b,c"); + equal(strtok(one, ","), "a"); /* Basics again... */ + equal(strtok((char*)NULL, ","), "b"); + equal(strtok((char*)NULL, ","), "c"); + check(strtok((char*)NULL, ",") == NULL); + equal(one + 6, "gh"); /* Stomped past end? */ + equal(one, "a"); /* Stomped old tokens? */ + equal(one + 2, "b"); + equal(one + 4, "c"); + + /* memcmp. */ + it = "memcmp"; + check(memcmp("a", "a", 1) == 0); /* Identity. */ + check(memcmp("abc", "abc", 3) == 0); /* Multicharacter. */ + check(memcmp("abcd", "abce", 4) < 0); /* Honestly unequal. */ + check(memcmp("abce", "abcd", 4)); + check(memcmp("alph", "beta", 4) < 0); + check(memcmp("abce", "abcd", 3) == 0); /* Count limited. */ + check(memcmp("abc", "def", 0) == 0); /* Zero count. */ + + /* memcmp should test strings as unsigned */ + one[0] = 0xfe; + two[0] = 0x03; + check(memcmp(one, two, 1) > 0); + + /* memchr. */ + it = "memchr"; + check(memchr("abcd", 'z', 4) == NULL); /* Not found. */ + (void)strcpy(one, "abcd"); + check(memchr(one, 'c', 4) == one + 2); /* Basic test. */ + check(memchr(one, 'd', 4) == one + 3); /* End of string. */ + check(memchr(one, 'a', 4) == one); /* Beginning. */ + check(memchr(one, '\0', 5) == one + 4); /* Finding NUL. */ + (void)strcpy(one, "ababa"); + check(memchr(one, 'b', 5) == one + 1); /* Finding first. */ + check(memchr(one, 'b', 0) == NULL); /* Zero count. */ + check(memchr(one, 'a', 1) == one); /* Singleton case. */ + (void)strcpy(one, "a\203b"); + check(memchr(one, 0203, 3) == one + 1); /* Unsignedness. */ + + /* memcpy - need not work for overlap. */ + it = "memcpy"; + check(memcpy(one, "abc", 4) == one); /* Returned value. */ + equal(one, "abc"); /* Did the copy go right? */ + + (void)strcpy(one, "abcdefgh"); + (void)memcpy(one + 1, "xyz", 2); + equal(one, "axydefgh"); /* Basic test. */ + + (void)strcpy(one, "abc"); + (void)memcpy(one, "xyz", 0); + equal(one, "abc"); /* Zero-length copy. */ + + (void)strcpy(one, "hi there"); + (void)strcpy(two, "foo"); + (void)memcpy(two, one, 9); + equal(two, "hi there"); /* Just paranoia. */ + equal(one, "hi there"); /* Stomped on source? */ #if 0 /* memmove - must work on overlap. */ it = "memmove"; @@ -499,68 +485,69 @@ void libm_test_string() check(memccpy(two, one, 'x', 1) == two+1); /* Singleton. */ equal(two, "xbcdlebee"); #endif - /* memset. */ - it = "memset"; - (void) strcpy(one, "abcdefgh"); - check(memset(one+1, 'x', 3) == one+1); /* Return value. */ - equal(one, "axxxefgh"); /* Basic test. */ - - (void) memset(one+2, 'y', 0); - equal(one, "axxxefgh"); /* Zero-length set. */ - - (void) memset(one+5, 0, 1); - equal(one, "axxxe"); /* Zero fill. */ - equal(one+6, "gh"); /* _AND the leftover. */ - - (void) memset(one+2, 010045, 1); - equal(one, "ax\045xe"); /* Unsigned char convert. */ - - /* bcopy - much like memcpy. - Berklix manual is silent about overlap, so don't test it. */ - it = "bcopy"; - (void) bcopy("abc", one, 4); - equal(one, "abc"); /* Simple copy. */ - - (void) strcpy(one, "abcdefgh"); - (void) bcopy("xyz", one+1, 2); - equal(one, "axydefgh"); /* Basic test. */ - - (void) strcpy(one, "abc"); - (void) bcopy("xyz", one, 0); - equal(one, "abc"); /* Zero-length copy. */ - - (void) strcpy(one, "hi there"); - (void) strcpy(two, "foo"); - (void) bcopy(one, two, 9); - equal(two, "hi there"); /* Just paranoia. */ - equal(one, "hi there"); /* Stomped on source? */ - - /* bzero. */ - it = "bzero"; - (void) strcpy(one, "abcdef"); - bzero(one+2, 2); - equal(one, "ab"); /* Basic test. */ - equal(one+3, ""); - equal(one+4, "ef"); - - (void) strcpy(one, "abcdef"); - bzero(one+2, 0); - equal(one, "abcdef"); /* Zero-length copy. */ - - /* bcmp - somewhat like memcmp. */ - it = "bcmp"; - check(bcmp("a", "a", 1) == 0); /* Identity. */ - check(bcmp("abc", "abc", 3) == 0); /* Multicharacter. */ - check(bcmp("abcd", "abce", 4) != 0); /* Honestly unequal. */ - check(bcmp("abce", "abcd",4)); - check(bcmp("alph", "beta", 4) != 0); - check(bcmp("abce", "abcd", 3) == 0); /* Count limited. */ - check(bcmp("abc", "def", 0) == 0); /* Zero count. */ - - if (errors) abort(); - printf("ok\n"); - -#if 0 /* strerror - VERY system-dependent. */ + /* memset. */ + it = "memset"; + (void)strcpy(one, "abcdefgh"); + check(memset(one + 1, 'x', 3) == one + 1); /* Return value. */ + equal(one, "axxxefgh"); /* Basic test. */ + + (void)memset(one + 2, 'y', 0); + equal(one, "axxxefgh"); /* Zero-length set. */ + + (void)memset(one + 5, 0, 1); + equal(one, "axxxe"); /* Zero fill. */ + equal(one + 6, "gh"); /* _AND the leftover. */ + + (void)memset(one + 2, 010045, 1); + equal(one, "ax\045xe"); /* Unsigned char convert. */ + + /* bcopy - much like memcpy. + Berklix manual is silent about overlap, so don't test it. */ + it = "bcopy"; + (void)bcopy("abc", one, 4); + equal(one, "abc"); /* Simple copy. */ + + (void)strcpy(one, "abcdefgh"); + (void)bcopy("xyz", one + 1, 2); + equal(one, "axydefgh"); /* Basic test. */ + + (void)strcpy(one, "abc"); + (void)bcopy("xyz", one, 0); + equal(one, "abc"); /* Zero-length copy. */ + + (void)strcpy(one, "hi there"); + (void)strcpy(two, "foo"); + (void)bcopy(one, two, 9); + equal(two, "hi there"); /* Just paranoia. */ + equal(one, "hi there"); /* Stomped on source? */ + + /* bzero. */ + it = "bzero"; + (void)strcpy(one, "abcdef"); + bzero(one + 2, 2); + equal(one, "ab"); /* Basic test. */ + equal(one + 3, ""); + equal(one + 4, "ef"); + + (void)strcpy(one, "abcdef"); + bzero(one + 2, 0); + equal(one, "abcdef"); /* Zero-length copy. */ + + /* bcmp - somewhat like memcmp. */ + it = "bcmp"; + check(bcmp("a", "a", 1) == 0); /* Identity. */ + check(bcmp("abc", "abc", 3) == 0); /* Multicharacter. */ + check(bcmp("abcd", "abce", 4) != 0); /* Honestly unequal. */ + check(bcmp("abce", "abcd", 4)); + check(bcmp("alph", "beta", 4) != 0); + check(bcmp("abce", "abcd", 3) == 0); /* Count limited. */ + check(bcmp("abc", "def", 0) == 0); /* Zero count. */ + + if (errors) + abort(); + printf("ok\n"); + +#if 0 /* strerror - VERY system-dependent. */ { extern CONST unsigned int _sys_nerr; extern CONST char *CONST _sys_errlist[]; @@ -572,4 +559,3 @@ void libm_test_string() } #endif } - diff --git a/tests/device/test_libc/memcpy-1.c b/tests/device/test_libc/memcpy-1.c index e6b6384521..02c536ed98 100644 --- a/tests/device/test_libc/memcpy-1.c +++ b/tests/device/test_libc/memcpy-1.c @@ -31,7 +31,6 @@ #include #include - #define memcmp memcmp_P #define memcpy memcpy_P #define memmem memmem_P @@ -69,106 +68,105 @@ #define TOO_MANY_ERRORS 11 static int errors = 0; -static void -print_error (char const* msg, ...) -{ - errors++; - if (errors == TOO_MANY_ERRORS) +static void print_error(char const* msg, ...) +{ + errors++; + if (errors == TOO_MANY_ERRORS) { - fprintf (stderr, "Too many errors.\n"); + fprintf(stderr, "Too many errors.\n"); } - else if (errors < TOO_MANY_ERRORS) + else if (errors < TOO_MANY_ERRORS) { - va_list ap; - va_start (ap, msg); - vfprintf (stderr, msg, ap); - va_end (ap); + va_list ap; + va_start(ap, msg); + vfprintf(stderr, msg, ap); + va_end(ap); } - else + else { - /* Further errors omitted. */ + /* Further errors omitted. */ } } extern int rand_seed; -void memcpy_main(void) +void memcpy_main(void) { - /* Allocate buffers to read and write from. */ - char src[BUFF_SIZE], dest[BUFF_SIZE], backup_src[BUFF_SIZE]; - - /* Fill the source buffer with non-null values, reproducable random data. */ - srand (rand_seed); - int i, j; - unsigned sa; - unsigned da; - unsigned n; - for (i = 0; i < BUFF_SIZE; i++) + /* Allocate buffers to read and write from. */ + char src[BUFF_SIZE], dest[BUFF_SIZE], backup_src[BUFF_SIZE]; + + /* Fill the source buffer with non-null values, reproducible random data. */ + srand(rand_seed); + int i, j; + unsigned sa; + unsigned da; + unsigned n; + for (i = 0; i < BUFF_SIZE; i++) { - src[i] = (char)rand () | 1; - backup_src[i] = src[i]; + src[i] = (char)rand() | 1; + backup_src[i] = src[i]; } - /* Make calls to memcpy with block sizes ranging between 1 and - MAX_BLOCK_SIZE bytes, aligned and misaligned source and destination. */ - for (sa = 0; sa <= MAX_OFFSET; sa++) - for (da = 0; da <= MAX_OFFSET; da++) - for (n = 1; n <= MAX_BLOCK_SIZE; n++) - { - //printf ("."); - /* Zero dest so we can check it properly after the copying. */ - for (j = 0; j < BUFF_SIZE; j++) - dest[j] = 0; - - void *ret = memcpy (dest + START_COPY + da, src + sa, n); - - /* Check return value. */ - if (ret != (dest + START_COPY + da)) - print_error ("\nFailed: wrong return value in memcpy of %u bytes " - "with src_align %u and dst_align %u. " - "Return value and dest should be the same" - "(ret is %p, dest is %p)\n", - n, sa, da, ret, dest + START_COPY + da); - - /* Check that content of the destination buffer - is the same as the source buffer, and - memory outside destination buffer is not modified. */ - for (j = 0; j < BUFF_SIZE; j++) - if ((unsigned)j < START_COPY + da) - { - if (dest[j] != 0) - print_error ("\nFailed: after memcpy of %u bytes " - "with src_align %u and dst_align %u, " - "byte %u before the start of dest is not 0.\n", - n, sa, da, START_COPY - j); - } - else if ((unsigned)j < START_COPY + da + n) - { - i = j - START_COPY - da; - if (dest[j] != (src + sa)[i]) - print_error ("\nFailed: after memcpy of %u bytes " - "with src_align %u and dst_align %u, " - "byte %u in dest and src are not the same.\n", - n, sa, da, i); - } - else if (dest[j] != 0) - { - print_error ("\nFailed: after memcpy of %u bytes " - "with src_align %u and dst_align %u, " - "byte %u after the end of dest is not 0.\n", - n, sa, da, j - START_COPY - da - n); - } - - /* Check src is not modified. */ - for (j = 0; j < BUFF_SIZE; j++) - if (src[i] != backup_src[i]) - print_error ("\nFailed: after memcpy of %u bytes " - "with src_align %u and dst_align %u, " - "byte %u of src is modified.\n", - n, sa, da, j); - } - - if (errors != 0) - abort (); - - printf("ok\n"); + /* Make calls to memcpy with block sizes ranging between 1 and + MAX_BLOCK_SIZE bytes, aligned and misaligned source and destination. */ + for (sa = 0; sa <= MAX_OFFSET; sa++) + for (da = 0; da <= MAX_OFFSET; da++) + for (n = 1; n <= MAX_BLOCK_SIZE; n++) + { + // printf ("."); + /* Zero dest so we can check it properly after the copying. */ + for (j = 0; j < BUFF_SIZE; j++) + dest[j] = 0; + + void* ret = memcpy(dest + START_COPY + da, src + sa, n); + + /* Check return value. */ + if (ret != (dest + START_COPY + da)) + print_error("\nFailed: wrong return value in memcpy of %u bytes " + "with src_align %u and dst_align %u. " + "Return value and dest should be the same" + "(ret is %p, dest is %p)\n", + n, sa, da, ret, dest + START_COPY + da); + + /* Check that content of the destination buffer + is the same as the source buffer, and + memory outside destination buffer is not modified. */ + for (j = 0; j < BUFF_SIZE; j++) + if ((unsigned)j < START_COPY + da) + { + if (dest[j] != 0) + print_error("\nFailed: after memcpy of %u bytes " + "with src_align %u and dst_align %u, " + "byte %u before the start of dest is not 0.\n", + n, sa, da, START_COPY - j); + } + else if ((unsigned)j < START_COPY + da + n) + { + i = j - START_COPY - da; + if (dest[j] != (src + sa)[i]) + print_error("\nFailed: after memcpy of %u bytes " + "with src_align %u and dst_align %u, " + "byte %u in dest and src are not the same.\n", + n, sa, da, i); + } + else if (dest[j] != 0) + { + print_error("\nFailed: after memcpy of %u bytes " + "with src_align %u and dst_align %u, " + "byte %u after the end of dest is not 0.\n", + n, sa, da, j - START_COPY - da - n); + } + + /* Check src is not modified. */ + for (j = 0; j < BUFF_SIZE; j++) + if (src[i] != backup_src[i]) + print_error("\nFailed: after memcpy of %u bytes " + "with src_align %u and dst_align %u, " + "byte %u of src is modified.\n", + n, sa, da, j); + } + + if (errors != 0) + abort(); + + printf("ok\n"); } diff --git a/tests/device/test_libc/memmove1.c b/tests/device/test_libc/memmove1.c index f64feae759..8a6403ee94 100644 --- a/tests/device/test_libc/memmove1.c +++ b/tests/device/test_libc/memmove1.c @@ -55,140 +55,132 @@ #define TOO_MANY_ERRORS 11 int errors = 0; -#define DEBUGP \ - if (errors == TOO_MANY_ERRORS) \ - printf ("Further errors omitted\n"); \ - else if (errors < TOO_MANY_ERRORS) \ - printf +#define DEBUGP \ + if (errors == TOO_MANY_ERRORS) \ + printf("Further errors omitted\n"); \ + else if (errors < TOO_MANY_ERRORS) \ + printf /* A safe target-independent memmove. */ -void -mymemmove (unsigned char *dest, unsigned char *src, size_t n) +void mymemmove(unsigned char* dest, unsigned char* src, size_t n) { - if ((src <= dest && src + n <= dest) - || src >= dest) - while (n-- > 0) - *dest++ = *src++; - else + if ((src <= dest && src + n <= dest) || src >= dest) + while (n-- > 0) + *dest++ = *src++; + else { - dest += n; - src += n; - while (n-- > 0) - *--dest = *--src; + dest += n; + src += n; + while (n-- > 0) + *--dest = *--src; } } /* It's either the noinline attribute or forcing the test framework to pass -fno-builtin-memmove. */ -void -xmemmove (unsigned char *dest, unsigned char *src, size_t n) - __attribute__ ((__noinline__)); +void xmemmove(unsigned char* dest, unsigned char* src, size_t n) __attribute__((__noinline__)); -void -xmemmove (unsigned char *dest, unsigned char *src, size_t n) +void xmemmove(unsigned char* dest, unsigned char* src, size_t n) { - void *retp; - retp = memmove (dest, src, n); + void* retp; + retp = memmove(dest, src, n); - if (retp != dest) + if (retp != dest) { - errors++; - DEBUGP ("memmove of n bytes returned %p instead of dest=%p\n", - retp, dest); + errors++; + DEBUGP("memmove of n bytes returned %p instead of dest=%p\n", retp, dest); } } - /* Fill the array with something we can associate with a position, but not exactly the same as the position index. */ -void -fill (unsigned char dest[MAX*3]) +void fill(unsigned char dest[MAX * 3]) { - size_t i; - for (i = 0; i < MAX*3; i++) - dest[i] = (10 + i) % MAX; + size_t i; + for (i = 0; i < MAX * 3; i++) + dest[i] = (10 + i) % MAX; } void memmove_main(void) { - size_t i; - int errors = 0; - - /* Leave some room before and after the area tested, so we can detect - overwrites of up to N bytes, N being the amount tested. If you - want to test using valgrind, make these malloced instead. */ - unsigned char from_test[MAX*3]; - unsigned char to_test[MAX*3]; - unsigned char from_known[MAX*3]; - unsigned char to_known[MAX*3]; - - /* Non-overlap. */ - for (i = 0; i < MAX; i++) + size_t i; + int errors = 0; + + /* Leave some room before and after the area tested, so we can detect + overwrites of up to N bytes, N being the amount tested. If you + want to test using valgrind, make these malloced instead. */ + unsigned char from_test[MAX * 3]; + unsigned char to_test[MAX * 3]; + unsigned char from_known[MAX * 3]; + unsigned char to_known[MAX * 3]; + + /* Non-overlap. */ + for (i = 0; i < MAX; i++) { - /* Do the memmove first before setting the known array, so we know - it didn't change any of the known array. */ - fill (from_test); - fill (to_test); - xmemmove (to_test + MAX, 1 + from_test + MAX, i); - - fill (from_known); - fill (to_known); - mymemmove (to_known + MAX, 1 + from_known + MAX, i); - - if (memcmp (to_known, to_test, sizeof (to_known)) != 0) - { - errors++; - DEBUGP ("memmove failed non-overlap test for %d bytes\n", i); - } + /* Do the memmove first before setting the known array, so we know + it didn't change any of the known array. */ + fill(from_test); + fill(to_test); + xmemmove(to_test + MAX, 1 + from_test + MAX, i); + + fill(from_known); + fill(to_known); + mymemmove(to_known + MAX, 1 + from_known + MAX, i); + + if (memcmp(to_known, to_test, sizeof(to_known)) != 0) + { + errors++; + DEBUGP("memmove failed non-overlap test for %d bytes\n", i); + } } - /* Overlap-from-before. */ - for (i = 0; i < MAX; i++) + /* Overlap-from-before. */ + for (i = 0; i < MAX; i++) { - size_t j; - for (j = 0; j < i; j++) - { - fill (to_test); - xmemmove (to_test + MAX * 2 - i, to_test + MAX * 2 - i - j, i); - - fill (to_known); - mymemmove (to_known + MAX * 2 - i, to_known + MAX * 2 - i - j, i); - - if (memcmp (to_known, to_test, sizeof (to_known)) != 0) - { - errors++; - DEBUGP ("memmove failed for %d bytes," - " with src %d bytes before dest\n", - i, j); - } - } + size_t j; + for (j = 0; j < i; j++) + { + fill(to_test); + xmemmove(to_test + MAX * 2 - i, to_test + MAX * 2 - i - j, i); + + fill(to_known); + mymemmove(to_known + MAX * 2 - i, to_known + MAX * 2 - i - j, i); + + if (memcmp(to_known, to_test, sizeof(to_known)) != 0) + { + errors++; + DEBUGP("memmove failed for %d bytes," + " with src %d bytes before dest\n", + i, j); + } + } } - /* Overlap-from-after. */ - for (i = 0; i < MAX; i++) + /* Overlap-from-after. */ + for (i = 0; i < MAX; i++) { - size_t j; - for (j = 0; j < i; j++) - { - fill (to_test); - xmemmove (to_test + MAX, to_test + MAX + j, i); - - fill (to_known); - mymemmove (to_known + MAX, to_known + MAX + j, i); - - if (memcmp (to_known, to_test, sizeof (to_known)) != 0) - { - errors++; - DEBUGP ("memmove failed when moving %d bytes," - " with src %d bytes after dest\n", - i, j); - } - } + size_t j; + for (j = 0; j < i; j++) + { + fill(to_test); + xmemmove(to_test + MAX, to_test + MAX + j, i); + + fill(to_known); + mymemmove(to_known + MAX, to_known + MAX + j, i); + + if (memcmp(to_known, to_test, sizeof(to_known)) != 0) + { + errors++; + DEBUGP("memmove failed when moving %d bytes," + " with src %d bytes after dest\n", + i, j); + } + } } - if (errors != 0) - abort (); - printf("ok\n"); + if (errors != 0) + abort(); + printf("ok\n"); } diff --git a/tests/device/test_libc/strcmp-1.c b/tests/device/test_libc/strcmp-1.c index 7eaede5e98..e8663b9ba9 100644 --- a/tests/device/test_libc/strcmp-1.c +++ b/tests/device/test_libc/strcmp-1.c @@ -46,7 +46,6 @@ #define BUFF_SIZE 256 - /* The macro LONG_TEST controls whether a short or a more comprehensive test of strcmp should be performed. */ #ifdef LONG_TEST @@ -106,186 +105,183 @@ #error "Buffer overrun: MAX_OFFSET + MAX_BLOCK_SIZE + MAX_DIFF + MAX_LEN + MAX_ZEROS >= BUFF_SIZE." #endif - #define TOO_MANY_ERRORS 11 static int errors = 0; -const char *testname = "strcmp"; +const char* testname = "strcmp"; -static void -print_error (char const* msg, ...) +static void print_error(char const* msg, ...) { - errors++; - if (errors == TOO_MANY_ERRORS) + errors++; + if (errors == TOO_MANY_ERRORS) { - fprintf (stderr, "Too many errors.\n"); + fprintf(stderr, "Too many errors.\n"); } - else if (errors < TOO_MANY_ERRORS) + else if (errors < TOO_MANY_ERRORS) { - va_list ap; - va_start (ap, msg); - vfprintf (stderr, msg, ap); - va_end (ap); + va_list ap; + va_start(ap, msg); + vfprintf(stderr, msg, ap); + va_end(ap); } - else + else { - /* Further errors omitted. */ + /* Further errors omitted. */ } } - extern int rand_seed; -void strcmp_main(void) +void strcmp_main(void) { - /* Allocate buffers to read and write from. */ - char src[BUFF_SIZE], dest[BUFF_SIZE]; - - /* Fill the source buffer with non-null values, reproducable random data. */ - srand (rand_seed); - int i, j, zeros; - unsigned sa; - unsigned da; - unsigned n, m, len; - char *p; - int ret; - - /* Make calls to strcmp with block sizes ranging between 1 and - MAX_BLOCK_SIZE bytes, aligned and misaligned source and destination. */ - for (sa = 0; sa <= MAX_OFFSET; sa++) - for (da = 0; da <= MAX_OFFSET; da++) - for (n = 1; n <= MAX_BLOCK_SIZE; n++) - { - for (m = 1; m < n + MAX_DIFF; m++) - for (len = 0; len < MAX_LEN; len++) - for (zeros = 1; zeros < MAX_ZEROS; zeros++) - { - if (n - m > MAX_DIFF) - continue; - /* Make a copy of the source. */ - for (i = 0; i < BUFF_SIZE; i++) - { - src[i] = 'A' + (i % 26); - dest[i] = src[i]; - } - delay(0); - memcpy (dest + da, src + sa, n); - - /* Make src 0-terminated. */ - p = src + sa + n - 1; - for (i = 0; i < zeros; i++) - { - *p++ = '\0'; - } - - /* Modify dest. */ - p = dest + da + m - 1; - for (j = 0; j < (int)len; j++) - *p++ = 'x'; - /* Make dest 0-terminated. */ - *p = '\0'; - - ret = strcmp (src + sa, dest + da); - - /* Check return value. */ - if (n == m) - { - if (len == 0) - { - if (ret != 0) - { - print_error ("\nFailed: after %s of %u bytes " - "with src_align %u and dst_align %u, " - "dest after %d bytes is modified for %d bytes, " - "return value is %d, expected 0.\n", - testname, n, sa, da, m, len, ret); - } - } - else - { - if (ret >= 0) - print_error ("\nFailed: after %s of %u bytes " - "with src_align %u and dst_align %u, " - "dest after %d bytes is modified for %d bytes, " - "return value is %d, expected negative.\n", - testname, n, sa, da, m, len, ret); - } - } - else if (m > n) - { - if (ret >= 0) - { - print_error ("\nFailed: after %s of %u bytes " - "with src_align %u and dst_align %u, " - "dest after %d bytes is modified for %d bytes, " - "return value is %d, expected negative.\n", - testname, n, sa, da, m, len, ret); - } - } - else /* m < n */ - { - if (len == 0) - { - if (ret <= 0) - print_error ("\nFailed: after %s of %u bytes " - "with src_align %u and dst_align %u, " - "dest after %d bytes is modified for %d bytes, " - "return value is %d, expected positive.\n", - testname, n, sa, da, m, len, ret); - } - else - { - if (ret >= 0) - print_error ("\nFailed: after %s of %u bytes " - "with src_align %u and dst_align %u, " - "dest after %d bytes is modified for %d bytes, " - "return value is %d, expected negative.\n", - testname, n, sa, da, m, len, ret); - } - } - } - } - - /* Check some corner cases. */ - src[1] = 'A'; - dest[1] = 'A'; - src[2] = 'B'; - dest[2] = 'B'; - src[3] = 'C'; - dest[3] = 'C'; - src[4] = '\0'; - dest[4] = '\0'; - - src[0] = 0xc1; - dest[0] = 0x41; - ret = strcmp (src, dest); - if (ret <= 0) - print_error ("\nFailed: expected positive, return %d\n", ret); - - src[0] = 0x01; - dest[0] = 0x82; - ret = strcmp (src, dest); - if (ret >= 0) - print_error ("\nFailed: expected negative, return %d\n", ret); - - dest[0] = src[0] = 'D'; - src[3] = 0xc1; - dest[3] = 0x41; - ret = strcmp (src, dest); - if (ret <= 0) - print_error ("\nFailed: expected positive, return %d\n", ret); - - src[3] = 0x01; - dest[3] = 0x82; - ret = strcmp (src, dest); - if (ret >= 0) - print_error ("\nFailed: expected negative, return %d\n", ret); - - //printf ("\n"); - if (errors != 0) + /* Allocate buffers to read and write from. */ + char src[BUFF_SIZE], dest[BUFF_SIZE]; + + /* Fill the source buffer with non-null values, reproducible random data. */ + srand(rand_seed); + int i, j, zeros; + unsigned sa; + unsigned da; + unsigned n, m, len; + char* p; + int ret; + + /* Make calls to strcmp with block sizes ranging between 1 and + MAX_BLOCK_SIZE bytes, aligned and misaligned source and destination. */ + for (sa = 0; sa <= MAX_OFFSET; sa++) + for (da = 0; da <= MAX_OFFSET; da++) + for (n = 1; n <= MAX_BLOCK_SIZE; n++) + { + for (m = 1; m < n + MAX_DIFF; m++) + for (len = 0; len < MAX_LEN; len++) + for (zeros = 1; zeros < MAX_ZEROS; zeros++) + { + if (n - m > MAX_DIFF) + continue; + /* Make a copy of the source. */ + for (i = 0; i < BUFF_SIZE; i++) + { + src[i] = 'A' + (i % 26); + dest[i] = src[i]; + } + delay(0); + memcpy(dest + da, src + sa, n); + + /* Make src 0-terminated. */ + p = src + sa + n - 1; + for (i = 0; i < zeros; i++) + { + *p++ = '\0'; + } + + /* Modify dest. */ + p = dest + da + m - 1; + for (j = 0; j < (int)len; j++) + *p++ = 'x'; + /* Make dest 0-terminated. */ + *p = '\0'; + + ret = strcmp(src + sa, dest + da); + + /* Check return value. */ + if (n == m) + { + if (len == 0) + { + if (ret != 0) + { + print_error("\nFailed: after %s of %u bytes " + "with src_align %u and dst_align %u, " + "dest after %d bytes is modified for %d bytes, " + "return value is %d, expected 0.\n", + testname, n, sa, da, m, len, ret); + } + } + else + { + if (ret >= 0) + print_error("\nFailed: after %s of %u bytes " + "with src_align %u and dst_align %u, " + "dest after %d bytes is modified for %d bytes, " + "return value is %d, expected negative.\n", + testname, n, sa, da, m, len, ret); + } + } + else if (m > n) + { + if (ret >= 0) + { + print_error("\nFailed: after %s of %u bytes " + "with src_align %u and dst_align %u, " + "dest after %d bytes is modified for %d bytes, " + "return value is %d, expected negative.\n", + testname, n, sa, da, m, len, ret); + } + } + else /* m < n */ + { + if (len == 0) + { + if (ret <= 0) + print_error("\nFailed: after %s of %u bytes " + "with src_align %u and dst_align %u, " + "dest after %d bytes is modified for %d bytes, " + "return value is %d, expected positive.\n", + testname, n, sa, da, m, len, ret); + } + else + { + if (ret >= 0) + print_error("\nFailed: after %s of %u bytes " + "with src_align %u and dst_align %u, " + "dest after %d bytes is modified for %d bytes, " + "return value is %d, expected negative.\n", + testname, n, sa, da, m, len, ret); + } + } + } + } + + /* Check some corner cases. */ + src[1] = 'A'; + dest[1] = 'A'; + src[2] = 'B'; + dest[2] = 'B'; + src[3] = 'C'; + dest[3] = 'C'; + src[4] = '\0'; + dest[4] = '\0'; + + src[0] = 0xc1; + dest[0] = 0x41; + ret = strcmp(src, dest); + if (ret <= 0) + print_error("\nFailed: expected positive, return %d\n", ret); + + src[0] = 0x01; + dest[0] = 0x82; + ret = strcmp(src, dest); + if (ret >= 0) + print_error("\nFailed: expected negative, return %d\n", ret); + + dest[0] = src[0] = 'D'; + src[3] = 0xc1; + dest[3] = 0x41; + ret = strcmp(src, dest); + if (ret <= 0) + print_error("\nFailed: expected positive, return %d\n", ret); + + src[3] = 0x01; + dest[3] = 0x82; + ret = strcmp(src, dest); + if (ret >= 0) + print_error("\nFailed: expected negative, return %d\n", ret); + + // printf ("\n"); + if (errors != 0) { - printf ("ERROR. FAILED.\n"); - abort (); + printf("ERROR. FAILED.\n"); + abort(); } - //exit (0); - printf("ok\n"); + // exit (0); + printf("ok\n"); } diff --git a/tests/device/test_libc/tstring.c b/tests/device/test_libc/tstring.c index 7e806b1027..7d508d4c6e 100644 --- a/tests/device/test_libc/tstring.c +++ b/tests/device/test_libc/tstring.c @@ -26,336 +26,297 @@ #define MAX_2 (2 * MAX_1 + MAX_1 / 10) -void eprintf (int line, char *result, char *expected, int size) +void eprintf(int line, char* result, char* expected, int size) { - if (size != 0) - printf ("Failure at line %d, result is <%.*s>, should be <%s> of size %d\n", - line, size, result, expected, size); - else - printf ("Failure at line %d, result is <%s>, should be <%s>\n", - line, result, expected); + if (size != 0) + printf("Failure at line %d, result is <%.*s>, should be <%s> of size %d\n", line, size, + result, expected, size); + else + printf("Failure at line %d, result is <%s>, should be <%s>\n", line, result, expected); } -void mycopy (char *target, char *source, int size) +void mycopy(char* target, char* source, int size) { - int i; + int i; - for (i = 0; i < size; ++i) + for (i = 0; i < size; ++i) { - target[i] = source[i]; + target[i] = source[i]; } } -void myset (char *target, char ch, int size) +void myset(char* target, char ch, int size) { - int i; - - for (i = 0; i < size; ++i) + int i; + + for (i = 0; i < size; ++i) { - target[i] = ch; + target[i] = ch; } } void tstring_main(void) { - char target[MAX_1] = "A"; - char first_char; - char second_char; - char array[] = "abcdefghijklmnopqrstuvwxz"; - char array2[] = "0123456789!@#$%^&*("; - char buffer2[MAX_1]; - char buffer3[MAX_1]; - char buffer4[MAX_1]; - char buffer5[MAX_2]; - char buffer6[MAX_2]; - char buffer7[MAX_2]; - char expected[MAX_1]; - char *tmp1, *tmp2, *tmp3, *tmp4, *tmp5, *tmp6, *tmp7; - int i, j, k, x, z, align_test_iterations; - z = 0; - - int test_failed = 0; - - tmp1 = target; - tmp2 = buffer2; - tmp3 = buffer3; - tmp4 = buffer4; - tmp5 = buffer5; - tmp6 = buffer6; - tmp7 = buffer7; - - tmp2[0] = 'Z'; - tmp2[1] = '\0'; - - if (memset (target, 'X', 0) != target || - memcpy (target, "Y", 0) != target || - memmove (target, "K", 0) != target || - strncpy (tmp2, "4", 0) != tmp2 || - strncat (tmp2, "123", 0) != tmp2 || - strcat (target, "") != target) + char target[MAX_1] = "A"; + char first_char; + char second_char; + char array[] = "abcdefghijklmnopqrstuvwxz"; + char array2[] = "0123456789!@#$%^&*("; + char buffer2[MAX_1]; + char buffer3[MAX_1]; + char buffer4[MAX_1]; + char buffer5[MAX_2]; + char buffer6[MAX_2]; + char buffer7[MAX_2]; + char expected[MAX_1]; + char *tmp1, *tmp2, *tmp3, *tmp4, *tmp5, *tmp6, *tmp7; + int i, j, k, x, z, align_test_iterations; + z = 0; + + int test_failed = 0; + + tmp1 = target; + tmp2 = buffer2; + tmp3 = buffer3; + tmp4 = buffer4; + tmp5 = buffer5; + tmp6 = buffer6; + tmp7 = buffer7; + + tmp2[0] = 'Z'; + tmp2[1] = '\0'; + + if (memset(target, 'X', 0) != target || memcpy(target, "Y", 0) != target + || memmove(target, "K", 0) != target || strncpy(tmp2, "4", 0) != tmp2 + || strncat(tmp2, "123", 0) != tmp2 || strcat(target, "") != target) { - eprintf (__LINE__, target, "A", 0); - test_failed = 1; + eprintf(__LINE__, target, "A", 0); + test_failed = 1; } - if (strcmp (target, "A") || strlen(target) != 1 || memchr (target, 'A', 0) != NULL - || memcmp (target, "J", 0) || strncmp (target, "A", 1) || strncmp (target, "J", 0) || - tmp2[0] != 'Z' || tmp2[1] != '\0') + if (strcmp(target, "A") || strlen(target) != 1 || memchr(target, 'A', 0) != NULL + || memcmp(target, "J", 0) || strncmp(target, "A", 1) || strncmp(target, "J", 0) + || tmp2[0] != 'Z' || tmp2[1] != '\0') { - eprintf (__LINE__, target, "A", 0); - test_failed = 1; + eprintf(__LINE__, target, "A", 0); + test_failed = 1; } - tmp2[2] = 'A'; - if (strcpy (target, "") != target || - strncpy (tmp2, "", 4) != tmp2 || - strcat (target, "") != target) + tmp2[2] = 'A'; + if (strcpy(target, "") != target || strncpy(tmp2, "", 4) != tmp2 + || strcat(target, "") != target) { - eprintf (__LINE__, target, "", 0); - test_failed = 1; + eprintf(__LINE__, target, "", 0); + test_failed = 1; } - if (target[0] != '\0' || strncmp (target, "", 1) || - memcmp (tmp2, "\0\0\0\0", 4)) + if (target[0] != '\0' || strncmp(target, "", 1) || memcmp(tmp2, "\0\0\0\0", 4)) { - eprintf (__LINE__, target, "", 0); - test_failed = 1; + eprintf(__LINE__, target, "", 0); + test_failed = 1; } - tmp2[2] = 'A'; - if (strncat (tmp2, "1", 3) != tmp2 || - memcmp (tmp2, "1\0A", 3)) + tmp2[2] = 'A'; + if (strncat(tmp2, "1", 3) != tmp2 || memcmp(tmp2, "1\0A", 3)) { - eprintf (__LINE__, tmp2, "1\0A", 3); - test_failed = 1; + eprintf(__LINE__, tmp2, "1\0A", 3); + test_failed = 1; } - if (strcpy (tmp3, target) != tmp3 || - strcat (tmp3, "X") != tmp3 || - strncpy (tmp2, "X", 2) != tmp2 || - memset (target, tmp2[0], 1) != target) + if (strcpy(tmp3, target) != tmp3 || strcat(tmp3, "X") != tmp3 || strncpy(tmp2, "X", 2) != tmp2 + || memset(target, tmp2[0], 1) != target) { - eprintf (__LINE__, target, "X", 0); - test_failed = 1; + eprintf(__LINE__, target, "X", 0); + test_failed = 1; } - if (strcmp (target, "X") || strlen (target) != 1 || - memchr (target, 'X', 2) != target || - strchr (target, 'X') != target || - memchr (target, 'Y', 2) != NULL || - strchr (target, 'Y') != NULL || - strcmp (tmp3, target) || - strncmp (tmp3, target, 2) || - memcmp (target, "K", 0) || - strncmp (target, tmp3, 3)) + if (strcmp(target, "X") || strlen(target) != 1 || memchr(target, 'X', 2) != target + || strchr(target, 'X') != target || memchr(target, 'Y', 2) != NULL + || strchr(target, 'Y') != NULL || strcmp(tmp3, target) || strncmp(tmp3, target, 2) + || memcmp(target, "K", 0) || strncmp(target, tmp3, 3)) { - eprintf (__LINE__, target, "X", 0); - test_failed = 1; + eprintf(__LINE__, target, "X", 0); + test_failed = 1; } - if (strcpy (tmp3, "Y") != tmp3 || - strcat (tmp3, "Y") != tmp3 || - memset (target, 'Y', 2) != target) + if (strcpy(tmp3, "Y") != tmp3 || strcat(tmp3, "Y") != tmp3 || memset(target, 'Y', 2) != target) { - eprintf (__LINE__, target, "Y", 0); - test_failed = 1; + eprintf(__LINE__, target, "Y", 0); + test_failed = 1; } - target[2] = '\0'; - if (memcmp (target, "YY", 2) || strcmp (target, "YY") || - strlen (target) != 2 || memchr (target, 'Y', 2) != target || - strcmp (tmp3, target) || - strncmp (target, tmp3, 3) || - strncmp (target, tmp3, 4) || - strncmp (target, tmp3, 2) || - strchr (target, 'Y') != target) + target[2] = '\0'; + if (memcmp(target, "YY", 2) || strcmp(target, "YY") || strlen(target) != 2 + || memchr(target, 'Y', 2) != target || strcmp(tmp3, target) || strncmp(target, tmp3, 3) + || strncmp(target, tmp3, 4) || strncmp(target, tmp3, 2) || strchr(target, 'Y') != target) { - eprintf (__LINE__, target, "YY", 2); - test_failed = 1; + eprintf(__LINE__, target, "YY", 2); + test_failed = 1; } - strcpy (target, "WW"); - if (memcmp (target, "WW", 2) || strcmp (target, "WW") || - strlen (target) != 2 || memchr (target, 'W', 2) != target || - strchr (target, 'W') != target) + strcpy(target, "WW"); + if (memcmp(target, "WW", 2) || strcmp(target, "WW") || strlen(target) != 2 + || memchr(target, 'W', 2) != target || strchr(target, 'W') != target) { - eprintf (__LINE__, target, "WW", 2); - test_failed = 1; + eprintf(__LINE__, target, "WW", 2); + test_failed = 1; } - if (strncpy (target, "XX", 16) != target || - memcmp (target, "XX\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16)) + if (strncpy(target, "XX", 16) != target || memcmp(target, "XX\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16)) { - eprintf (__LINE__, target, "XX\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16); - test_failed = 1; + eprintf(__LINE__, target, "XX\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16); + test_failed = 1; } - if (strcpy (tmp3, "ZZ") != tmp3 || - strcat (tmp3, "Z") != tmp3 || - memcpy (tmp4, "Z", 2) != tmp4 || - strcat (tmp4, "ZZ") != tmp4 || - memset (target, 'Z', 3) != target) + if (strcpy(tmp3, "ZZ") != tmp3 || strcat(tmp3, "Z") != tmp3 || memcpy(tmp4, "Z", 2) != tmp4 + || strcat(tmp4, "ZZ") != tmp4 || memset(target, 'Z', 3) != target) { - eprintf (__LINE__, target, "ZZZ", 3); - test_failed = 1; + eprintf(__LINE__, target, "ZZZ", 3); + test_failed = 1; } - target[3] = '\0'; - tmp5[0] = '\0'; - strncat (tmp5, "123", 2); - if (memcmp (target, "ZZZ", 3) || strcmp (target, "ZZZ") || - strcmp (tmp3, target) || strcmp (tmp4, target) || - strncmp (target, "ZZZ", 4) || strncmp (target, "ZZY", 3) <= 0 || - strncmp ("ZZY", target, 4) >= 0 || - memcmp (tmp5, "12", 3) || - strlen (target) != 3) + target[3] = '\0'; + tmp5[0] = '\0'; + strncat(tmp5, "123", 2); + if (memcmp(target, "ZZZ", 3) || strcmp(target, "ZZZ") || strcmp(tmp3, target) + || strcmp(tmp4, target) || strncmp(target, "ZZZ", 4) || strncmp(target, "ZZY", 3) <= 0 + || strncmp("ZZY", target, 4) >= 0 || memcmp(tmp5, "12", 3) || strlen(target) != 3) { - eprintf (__LINE__, target, "ZZZ", 3); - test_failed = 1; + eprintf(__LINE__, target, "ZZZ", 3); + test_failed = 1; } - target[2] = 'K'; - if (memcmp (target, "ZZZ", 2) || strcmp (target, "ZZZ") >= 0 || - memcmp (target, "ZZZ", 3) >= 0 || strlen (target) != 3 || - memchr (target, 'K', 3) != target + 2 || - strncmp (target, "ZZZ", 2) || strncmp (target, "ZZZ", 4) >= 0 || - strchr (target, 'K') != target + 2) + target[2] = 'K'; + if (memcmp(target, "ZZZ", 2) || strcmp(target, "ZZZ") >= 0 || memcmp(target, "ZZZ", 3) >= 0 + || strlen(target) != 3 || memchr(target, 'K', 3) != target + 2 || strncmp(target, "ZZZ", 2) + || strncmp(target, "ZZZ", 4) >= 0 || strchr(target, 'K') != target + 2) { - eprintf (__LINE__, target, "ZZK", 3); - test_failed = 1; + eprintf(__LINE__, target, "ZZK", 3); + test_failed = 1; } - - strcpy (target, "AAA"); - if (memcmp (target, "AAA", 3) || strcmp (target, "AAA") || - strncmp (target, "AAA", 3) || - strlen (target) != 3) + + strcpy(target, "AAA"); + if (memcmp(target, "AAA", 3) || strcmp(target, "AAA") || strncmp(target, "AAA", 3) + || strlen(target) != 3) { - eprintf (__LINE__, target, "AAA", 3); - test_failed = 1; + eprintf(__LINE__, target, "AAA", 3); + test_failed = 1; } - - j = 5; - while (j < MAX_1) + + j = 5; + while (j < MAX_1) { - for (i = j-1; i <= j+1; ++i) + for (i = j - 1; i <= j + 1; ++i) { - /* don't bother checking unaligned data in the larger - sizes since it will waste time without performing additional testing */ - if ((size_t)i <= 16 * sizeof(long)) - { - align_test_iterations = 2*sizeof(long); - if ((size_t)i <= 2 * sizeof(long) + 1) - z = 2; - else - z = 2 * sizeof(long); + /* don't bother checking unaligned data in the larger + sizes since it will waste time without performing additional testing */ + if ((size_t)i <= 16 * sizeof(long)) + { + align_test_iterations = 2 * sizeof(long); + if ((size_t)i <= 2 * sizeof(long) + 1) + z = 2; + else + z = 2 * sizeof(long); } - else + else { - align_test_iterations = 1; + align_test_iterations = 1; } - for (x = 0; x < align_test_iterations; ++x) - { - tmp1 = target + x; - tmp2 = buffer2 + x; - tmp3 = buffer3 + x; - tmp4 = buffer4 + x; - tmp5 = buffer5 + x; - tmp6 = buffer6 + x; - - first_char = array[i % (sizeof(array) - 1)]; - second_char = array2[i % (sizeof(array2) - 1)]; - memset (tmp1, first_char, i); - mycopy (tmp2, tmp1, i); - myset (tmp2 + z, second_char, i - z - 1); - if (memcpy (tmp1 + z, tmp2 + z, i - z - 1) != tmp1 + z) - { - printf ("error at line %d\n", __LINE__); - test_failed = 1; - } - - tmp1[i] = '\0'; - tmp2[i] = '\0'; - if (strcpy (expected, tmp2) != expected) - { - printf ("error at line %d\n", __LINE__); - test_failed = 1; - } - tmp2[i-z] = first_char + 1; - if (memmove (tmp2 + z + 1, tmp2 + z, i - z - 1) != tmp2 + z + 1 || - memset (tmp3, first_char, i) != tmp3) - { - printf ("error at line %d\n", __LINE__); - test_failed = 1; - } - - myset (tmp4, first_char, i); - tmp5[0] = '\0'; - if (strncpy (tmp5, tmp1, i+1) != tmp5 || - strcat (tmp5, tmp1) != tmp5) - { - printf ("error at line %d\n", __LINE__); - test_failed = 1; - } - mycopy (tmp6, tmp1, i); - mycopy (tmp6 + i, tmp1, i + 1); - - tmp7[2*i+z] = second_char; - strcpy (tmp7, tmp1); - - (void)strchr (tmp1, second_char); - - if (memcmp (tmp1, expected, i) || strcmp (tmp1, expected) || - strncmp (tmp1, expected, i) || - strncmp (tmp1, expected, i+1) || - strcmp (tmp1, tmp2) >= 0 || memcmp (tmp1, tmp2, i) >= 0 || - strncmp (tmp1, tmp2, i+1) >= 0 || - (int)strlen (tmp1) != i || memchr (tmp1, first_char, i) != tmp1 || - strchr (tmp1, first_char) != tmp1 || - memchr (tmp1, second_char, i) != tmp1 + z || - strchr (tmp1, second_char) != tmp1 + z || - strcmp (tmp5, tmp6) || - strncat (tmp7, tmp1, i+2) != tmp7 || - strcmp (tmp7, tmp6) || - tmp7[2*i+z] != second_char) - { - eprintf (__LINE__, tmp1, expected, 0); - printf ("x is %d\n",x); - printf ("i is %d\n", i); - printf ("tmp1 is <%p>\n", tmp1); - printf ("tmp5 is <%p> <%s>\n", tmp5, tmp5); - printf ("tmp6 is <%p> <%s>\n", tmp6, tmp6); - test_failed = 1; - } - - for (k = 1; k <= align_test_iterations && k <= i; ++k) - { - if (memcmp (tmp3, tmp4, i - k + 1) != 0 || - strncmp (tmp3, tmp4, i - k + 1) != 0) - { - printf ("Failure at line %d, comparing %.*s with %.*s\n", - __LINE__, i, tmp3, i, tmp4); - test_failed = 1; - } - tmp4[i-k] = first_char + 1; - if (memcmp (tmp3, tmp4, i) >= 0 || - strncmp (tmp3, tmp4, i) >= 0 || - memcmp (tmp4, tmp3, i) <= 0 || - strncmp (tmp4, tmp3, i) <= 0) - { - printf ("Failure at line %d, comparing %.*s with %.*s\n", - __LINE__, i, tmp3, i, tmp4); - test_failed = 1; - } - tmp4[i-k] = first_char; - } - } + for (x = 0; x < align_test_iterations; ++x) + { + tmp1 = target + x; + tmp2 = buffer2 + x; + tmp3 = buffer3 + x; + tmp4 = buffer4 + x; + tmp5 = buffer5 + x; + tmp6 = buffer6 + x; + + first_char = array[i % (sizeof(array) - 1)]; + second_char = array2[i % (sizeof(array2) - 1)]; + memset(tmp1, first_char, i); + mycopy(tmp2, tmp1, i); + myset(tmp2 + z, second_char, i - z - 1); + if (memcpy(tmp1 + z, tmp2 + z, i - z - 1) != tmp1 + z) + { + printf("error at line %d\n", __LINE__); + test_failed = 1; + } + + tmp1[i] = '\0'; + tmp2[i] = '\0'; + if (strcpy(expected, tmp2) != expected) + { + printf("error at line %d\n", __LINE__); + test_failed = 1; + } + tmp2[i - z] = first_char + 1; + if (memmove(tmp2 + z + 1, tmp2 + z, i - z - 1) != tmp2 + z + 1 + || memset(tmp3, first_char, i) != tmp3) + { + printf("error at line %d\n", __LINE__); + test_failed = 1; + } + + myset(tmp4, first_char, i); + tmp5[0] = '\0'; + if (strncpy(tmp5, tmp1, i + 1) != tmp5 || strcat(tmp5, tmp1) != tmp5) + { + printf("error at line %d\n", __LINE__); + test_failed = 1; + } + mycopy(tmp6, tmp1, i); + mycopy(tmp6 + i, tmp1, i + 1); + + tmp7[2 * i + z] = second_char; + strcpy(tmp7, tmp1); + + (void)strchr(tmp1, second_char); + + if (memcmp(tmp1, expected, i) || strcmp(tmp1, expected) + || strncmp(tmp1, expected, i) || strncmp(tmp1, expected, i + 1) + || strcmp(tmp1, tmp2) >= 0 || memcmp(tmp1, tmp2, i) >= 0 + || strncmp(tmp1, tmp2, i + 1) >= 0 || (int)strlen(tmp1) != i + || memchr(tmp1, first_char, i) != tmp1 || strchr(tmp1, first_char) != tmp1 + || memchr(tmp1, second_char, i) != tmp1 + z + || strchr(tmp1, second_char) != tmp1 + z || strcmp(tmp5, tmp6) + || strncat(tmp7, tmp1, i + 2) != tmp7 || strcmp(tmp7, tmp6) + || tmp7[2 * i + z] != second_char) + { + eprintf(__LINE__, tmp1, expected, 0); + printf("x is %d\n", x); + printf("i is %d\n", i); + printf("tmp1 is <%p>\n", tmp1); + printf("tmp5 is <%p> <%s>\n", tmp5, tmp5); + printf("tmp6 is <%p> <%s>\n", tmp6, tmp6); + test_failed = 1; + } + + for (k = 1; k <= align_test_iterations && k <= i; ++k) + { + if (memcmp(tmp3, tmp4, i - k + 1) != 0 || strncmp(tmp3, tmp4, i - k + 1) != 0) + { + printf("Failure at line %d, comparing %.*s with %.*s\n", __LINE__, i, tmp3, + i, tmp4); + test_failed = 1; + } + tmp4[i - k] = first_char + 1; + if (memcmp(tmp3, tmp4, i) >= 0 || strncmp(tmp3, tmp4, i) >= 0 + || memcmp(tmp4, tmp3, i) <= 0 || strncmp(tmp4, tmp3, i) <= 0) + { + printf("Failure at line %d, comparing %.*s with %.*s\n", __LINE__, i, tmp3, + i, tmp4); + test_failed = 1; + } + tmp4[i - k] = first_char; + } + } } - j = ((2 * j) >> 2) << 2; + j = ((2 * j) >> 2) << 2; } - if (test_failed) - abort(); + if (test_failed) + abort(); - printf("ok\n"); + printf("ok\n"); } diff --git a/tests/device/test_millis_mm/test_millis_mm.ino b/tests/device/test_millis_mm/test_millis_mm.ino index c5e25bdcdc..a159509e54 100644 --- a/tests/device/test_millis_mm/test_millis_mm.ino +++ b/tests/device/test_millis_mm/test_millis_mm.ino @@ -30,7 +30,7 @@ static bool fixed_systime = false; // Testing vars static bool debugf = false; static uint32_t us_systime = 0; static float nsf = 0; // Normalization factor -static uint32_t cntref = 0; // Ref. comparision count +static uint32_t cntref = 0; // Ref. comparison count //--------------------------------------------------------------------------- // Interrupt code lifted directly from "cores/core_esp8266_wiring.c", @@ -95,7 +95,7 @@ void viewhex( uint16_t *p, uint8_t n ) //--------------------------------------------------------------------------- // Support routine for 'millis_test_DEBUG()' -// Print accumulator value along interm summed into it +// Print accumulator value along interim summed into it void view_accsum ( const char *desc, uint16_t *acc, uint16_t *itrm ) { Serial.print( "acc: " ); @@ -109,7 +109,7 @@ void view_accsum ( const char *desc, uint16_t *acc, uint16_t *itrm ) //--------------------------------------------------------------------------- // FOR BENCHTEST // Original millis() function -unsigned long ICACHE_RAM_ATTR millis_orig ( void ) +unsigned long IRAM_ATTR millis_orig ( void ) { // Get usec system time, usec overflow conter uint32_t m = system_get_time(); @@ -123,7 +123,7 @@ unsigned long ICACHE_RAM_ATTR millis_orig ( void ) // FOR DEBUG // Corrected millis(), 64-bit arithmetic gold standard // truncated to 32-bits by return -unsigned long ICACHE_RAM_ATTR millis_corr_DEBUG( void ) +unsigned long IRAM_ATTR millis_corr_DEBUG( void ) { // Get usec system time, usec overflow conter uint32_t m = system_get_timeA(); // DEBUG @@ -135,7 +135,7 @@ unsigned long ICACHE_RAM_ATTR millis_corr_DEBUG( void ) //--------------------------------------------------------------------------- // FOR BENCHMARK -unsigned long ICACHE_RAM_ATTR millis_corr ( void ) +unsigned long IRAM_ATTR millis_corr ( void ) { // Get usec system time, usec overflow conter uint32_t m = system_get_time(); @@ -149,8 +149,8 @@ unsigned long ICACHE_RAM_ATTR millis_corr ( void ) // FOR DEBUG // millis() 'magic multiplier' approximation // -// This function corrects the cumlative (296us / usec overflow) drift -// seen in the orignal 'millis()' function. +// This function corrects the cumulative (296us / usec overflow) drift +// seen in the original 'millis()' function. // // Input: // 'm' - 32-bit usec counter, 0 <= m <= 0xFFFFFFFF @@ -229,7 +229,7 @@ unsigned long ICACHE_RAM_ATTR millis_corr ( void ) // // Reference function: corrected millis(), 64-bit arithmetic, // truncated to 32-bits by return -// unsigned long ICACHE_RAM_ATTR millis_corr_DEBUG( void ) +// unsigned long IRAM_ATTR millis_corr_DEBUG( void ) // { // // Get usec system time, usec overflow conter // ...... @@ -246,7 +246,7 @@ unsigned long ICACHE_RAM_ATTR millis_corr ( void ) #define MAGIC_1E3_wLO 0x4bc6a7f0 // LS part #define MAGIC_1E3_wHI 0x00418937 // MS part, magic multiplier -unsigned long ICACHE_RAM_ATTR millis_test_DEBUG ( void ) +unsigned long IRAM_ATTR millis_test_DEBUG ( void ) { union { uint64_t q; // Accumulator, 64-bit, little endian @@ -254,7 +254,7 @@ unsigned long ICACHE_RAM_ATTR millis_test_DEBUG ( void ) } acc; acc.a[1] = 0; // Zero high-acc - uint64_t prd; // Interm product + uint64_t prd; // Interim product // Get usec system time, usec overflow counter uint32_t m = system_get_timeA(); @@ -269,28 +269,28 @@ unsigned long ICACHE_RAM_ATTR millis_test_DEBUG ( void ) acc.q = ( (prd = (uint64_t)( m * (uint64_t)MAGIC_1E3_wLO )) >> 32 ); - // DEBUG: Show both accumulator and interm product + // DEBUG: Show both accumulator and interim product if( debugf ) view_accsum( "m kl", (uint16_t *)&acc.q, (uint16_t *)&prd ); // (b) Offset sum, low-acc acc.q += ( prd = ( m * (uint64_t)MAGIC_1E3_wHI ) ); - // DEBUG: Show both accumulator and interm product + // DEBUG: Show both accumulator and interim product if( debugf ) view_accsum( "m kh", (uint16_t *)&acc.q, (uint16_t *)&prd ); // (c) Offset sum, low-acc acc.q += ( prd = ( c * (uint64_t)MAGIC_1E3_wLO ) ); - // DEBUG: Show both accumulator and interm product + // DEBUG: Show both accumulator and interim product if( debugf ) view_accsum( "c kl", (uint16_t *)&acc.q, (uint16_t *)&prd ); // (d) Truncated sum, high-acc acc.a[1] += (uint32_t)( prd = ( c * (uint64_t)MAGIC_1E3_wHI ) ); - // DEBUG: Show both accumulator and interm product + // DEBUG: Show both accumulator and interim product if( debugf ) view_accsum( "c kh", (uint16_t *)&acc.q, (uint16_t *)&prd ); @@ -300,7 +300,7 @@ unsigned long ICACHE_RAM_ATTR millis_test_DEBUG ( void ) //--------------------------------------------------------------------------- // FOR BENCHTEST -unsigned long ICACHE_RAM_ATTR millis_test ( void ) +unsigned long IRAM_ATTR millis_test ( void ) { union { uint64_t q; // Accumulator, 64-bit, little endian diff --git a/tests/device/test_serial/test_serial.ino b/tests/device/test_serial/test_serial.ino index 6cdf2b2673..66ec154067 100644 --- a/tests/device/test_serial/test_serial.ino +++ b/tests/device/test_serial/test_serial.ino @@ -33,12 +33,6 @@ static uint64_t in_total = 0, in_prev = 0; static uint64_t start_ms, last_ms; static uint64_t timeout; -void preinit() { - // (no C++ in function) - // disable wifi - ESP8266WiFiClass::preinitWiFiOff(); -} - void setup() { Serial.begin(SSBAUD); diff --git a/tests/device/test_spi_flash/test_spi_flash.ino b/tests/device/test_spi_flash/test_spi_flash.ino index fa7ea767b8..16fa4a9750 100644 --- a/tests/device/test_spi_flash/test_spi_flash.ino +++ b/tests/device/test_spi_flash/test_spi_flash.ino @@ -1,14 +1,7 @@ #include -#include BS_ENV_DECLARE(); -void preinit() { - // (no C++ in function) - // disable wifi - ESP8266WiFiClass::preinitWiFiOff(); -} - void setup() { Serial.begin(115200); @@ -182,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/device/test_sw_StreamString/test_sw_StreamString.ino b/tests/device/test_sw_StreamString/test_sw_StreamString.ino new file mode 100644 index 0000000000..d706ee6aba --- /dev/null +++ b/tests/device/test_sw_StreamString/test_sw_StreamString.ino @@ -0,0 +1,25 @@ + +#include +#include + +#define check(what, res1, res2) CHECK(strcmp(res1, res2) == 0) + +#include "../../../libraries/esp8266/examples/StreamString/StreamString.ino" + +BS_ENV_DECLARE(); + +bool pretest () +{ + return true; +} + +void setup () +{ + Serial.begin(115200); + BS_RUN(Serial); +} + +TEST_CASE("StreamString tests", "[StreamString]") +{ + testStream(); +} diff --git a/tests/device/test_sw_WiFiServer/test_sw_WiFiServer.py b/tests/device/test_sw_WiFiServer/test_sw_WiFiServer.py index b64de33e12..86552547cd 100644 --- a/tests/device/test_sw_WiFiServer/test_sw_WiFiServer.py +++ b/tests/device/test_sw_WiFiServer/test_sw_WiFiServer.py @@ -11,6 +11,7 @@ def setup_echo_server(e): global stop_client_thread global client_thread def echo_client_thread(): + time.sleep(1) # let some time for mDNS to start server_address = socket.gethostbyname('esp8266-wfs-test.local') count = 0 while count < 5 and not stop_client_thread: diff --git a/tests/device/test_sw_newlib/test_sw_newlib.ino b/tests/device/test_sw_newlib/test_sw_newlib.ino index 32abbb9141..6052915ac6 100644 --- a/tests/device/test_sw_newlib/test_sw_newlib.ino +++ b/tests/device/test_sw_newlib/test_sw_newlib.ino @@ -1,4 +1,5 @@ #include +#include BS_ENV_DECLARE(); @@ -30,6 +31,13 @@ TEST_CASE("#612 fmod and sqrt work", "[newlib]") CHECK(fabs(fmod(-10, -3) - (-1.0)) < 1e-5); } + +TEST_CASE("#7845 std::remainder works", "[newlib]") +{ + CHECK(fabs(std::remainder((double)10.123456, (double)5.0) - (double)0.123456) < 1e-5); + CHECK(fabs(std::remainder((float)15.123456, (float)5.0) - (float)0.123456) < 1e-5); +} + void loop() { } diff --git a/tests/host/Makefile b/tests/host/Makefile index 4bb6e83b32..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,13 +68,38 @@ 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 \ - StreamString.cpp \ + StreamSend.cpp \ Stream.cpp \ WString.cpp \ Print.cpp \ + stdlib_noniso.cpp \ FS.cpp \ spiffs_api.cpp \ MD5Builder.cpp \ @@ -96,6 +116,7 @@ CORE_CPP_FILES := \ HardwareSerial.cpp \ crc32.cpp \ Updater.cpp \ + time.cpp \ ) \ $(addprefix $(abspath $(LIBRARIES_PATH)/ESP8266SdFat/src)/, \ FatLib/FatFile.cpp \ @@ -103,12 +124,16 @@ CORE_CPP_FILES := \ FatLib/FatFilePrint.cpp \ FatLib/FatFileSFN.cpp \ FatLib/FatFormatter.cpp \ + FatLib/FatName.cpp \ FatLib/FatVolume.cpp \ FatLib/FatPartition.cpp \ common/FmtNumber.cpp \ + common/FsCache.cpp \ common/FsStructs.cpp \ common/FsDateTime.cpp \ - common/PrintBasic.cpp \ + common/FsUtf.cpp \ + common/FsName.cpp \ + common/upcase.cpp \ ) \ $(abspath $(LIBRARIES_PATH)/SDFS/src/SDFS.cpp) \ $(abspath $(LIBRARIES_PATH)/SD/src/SD.cpp) \ @@ -130,7 +155,7 @@ MOCK_CPP_FILES_COMMON := \ MockUART.cpp \ MockTools.cpp \ MocklwIP.cpp \ - MockDigital.cpp \ + HostWiring.cpp \ ) MOCK_CPP_FILES := $(MOCK_CPP_FILES_COMMON) \ @@ -144,6 +169,7 @@ MOCK_CPP_FILES_EMU := $(MOCK_CPP_FILES_COMMON) \ ArduinoMainUdp.cpp \ ArduinoMainSpiffs.cpp \ ArduinoMainLittlefs.cpp \ + DhcpServer.cpp \ user_interface.cpp \ ) @@ -168,6 +194,8 @@ INC_PATHS += \ ../../tools/sdk/lwip2/include \ ) +TEST_ARGS ?= + TEST_CPP_FILES := \ fs/test_fs.cpp \ core/test_pgmspace.cpp \ @@ -230,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 @@ -340,6 +368,7 @@ OPT_ARDUINO_LIBS ?= \ DNSServer/src/DNSServer.cpp \ ESP8266AVRISP/src/ESP8266AVRISP.cpp \ ESP8266HTTPClient/src/ESP8266HTTPClient.cpp \ + Hash/src/Hash.cpp \ ) MOCK_ARDUINO_LIBS := \ @@ -349,7 +378,6 @@ MOCK_ARDUINO_LIBS := \ MockWiFiServerSocket.cpp \ MockWiFiServer.cpp \ UdpContextSocket.cpp \ - HostWiring.cpp \ MockEsp.cpp \ MockEEPROM.cpp \ MockSPI.cpp \ @@ -370,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) @@ -390,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/README.txt b/tests/host/README.txt index 07c0b276bd..feb6611d52 100644 --- a/tests/host/README.txt +++ b/tests/host/README.txt @@ -19,7 +19,7 @@ WiFiClient+WiFiServer/ClientContext and WifiUdp/UdpContext using socket posix API. Further work will optionally propose native lwIP library instead. -Serial UART0 and UART1 are emulated via stdin/stdout/stderr. Therefor +Serial UART0 and UART1 are emulated via stdin/stdout/stderr. Therefore stdin is connected to UART0(RX) and stdout is connected to UART0(TX). UART1(TX) writes to stderr. Reading from stdin happens in non-blocking raw mode, that means each character is directly injected into the UART diff --git a/tests/host/common/Arduino.cpp b/tests/host/common/Arduino.cpp index 8a0162b477..4b1d7070de 100644 --- a/tests/host/common/Arduino.cpp +++ b/tests/host/common/Arduino.cpp @@ -1,40 +1,55 @@ /* 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 "Arduino.h" - #include +#include + +#include +#include + +static struct timeval gtod0 = { 0, 0 }; + extern "C" unsigned long millis() { timeval time; gettimeofday(&time, NULL); - return (time.tv_sec * 1000) + (time.tv_usec / 1000); + if (gtod0.tv_sec == 0) + memcpy(>od0, &time, sizeof gtod0); + return ((time.tv_sec - gtod0.tv_sec) * 1000) + ((time.tv_usec - gtod0.tv_usec) / 1000); } extern "C" unsigned long micros() { timeval time; gettimeofday(&time, NULL); - return (time.tv_sec * 1000000) + time.tv_usec; + if (gtod0.tv_sec == 0) + memcpy(>od0, &time, sizeof gtod0); + return ((time.tv_sec - gtod0.tv_sec) * 1000000) + time.tv_usec - gtod0.tv_usec; } - extern "C" void yield() { + run_scheduled_recurrent_functions(); +} + +extern "C" void loop_end() +{ + run_scheduled_functions(); + run_scheduled_recurrent_functions(); } extern "C" bool can_yield() @@ -42,17 +57,35 @@ extern "C" bool can_yield() return true; } -extern "C" void optimistic_yield (uint32_t interval_us) +extern "C" void optimistic_yield(uint32_t interval_us) { (void)interval_us; } -extern "C" void esp_yield() +extern "C" void esp_suspend() { } + +extern "C" void esp_schedule() { } + +extern "C" void esp_yield() { } + +extern "C" void esp_delay(unsigned long ms) { + usleep(ms * 1000); } +bool esp_try_delay(const uint32_t start_ms, const uint32_t timeout_ms, const uint32_t intvl_ms) +{ + uint32_t expired = millis() - start_ms; + if (expired >= timeout_ms) + { + return true; + } + esp_delay(std::min((timeout_ms - expired), intvl_ms)); + return false; +} -extern "C" void __panic_func(const char* file, int line, const char* func) { +extern "C" void __panic_func(const char* file, int line, const char* func) +{ (void)file; (void)line; (void)func; @@ -61,7 +94,7 @@ extern "C" void __panic_func(const char* file, int line, const char* func) { extern "C" void delay(unsigned long ms) { - usleep(ms * 1000); + esp_delay(ms); } extern "C" void delayMicroseconds(unsigned int us) @@ -70,8 +103,14 @@ extern "C" void delayMicroseconds(unsigned int us) } #include "cont.h" -cont_t* g_pcont = NULL; -extern "C" void cont_yield(cont_t*) +cont_t* g_pcont = NULL; +extern "C" void cont_suspend(cont_t*) { } + +extern "C" int __mockverbose(const char* fmt, ...) { + (void)fmt; + return 0; } +int mockverbose(const char* fmt, ...) + __attribute__((weak, alias("__mockverbose"), format(printf, 1, 2))); diff --git a/tests/host/common/ArduinoCatch.cpp b/tests/host/common/ArduinoCatch.cpp index 6a1e3cca23..5308278979 100644 --- a/tests/host/common/ArduinoCatch.cpp +++ b/tests/host/common/ArduinoCatch.cpp @@ -1,20 +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. */ #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 a9d8d696f6..692ea97b0e 100644 --- a/tests/host/common/ArduinoMain.cpp +++ b/tests/host/common/ArduinoMain.cpp @@ -30,7 +30,7 @@ */ #include -#include // wifi_get_ip_info() +#include // wifi_get_ip_info() #include #include @@ -41,276 +41,287 @@ #define MOCK_PORT_SHIFTER 9000 -bool user_exit = false; -bool run_once = false; -const char* host_interface = nullptr; -size_t spiffs_kb = 1024; -size_t littlefs_kb = 1024; -bool ignore_sigint = false; -bool restore_tty = false; -bool mockdebug = false; -int mock_port_shifter = MOCK_PORT_SHIFTER; -const char* fspath = nullptr; +bool user_exit = false; +bool run_once = false; +const char* host_interface = nullptr; +size_t spiffs_kb = 1024; +size_t littlefs_kb = 1024; +bool ignore_sigint = false; +bool restore_tty = false; +bool mockdebug = false; +int mock_port_shifter = MOCK_PORT_SHIFTER; +const char* fspath = nullptr; #define STDIN STDIN_FILENO static struct termios initial_settings; -int mockverbose (const char* fmt, ...) +int mockverbose(const char* fmt, ...) { - va_list ap; - va_start(ap, fmt); - if (mockdebug) - return fprintf(stderr, MOCK) + vfprintf(stderr, fmt, ap); - return 0; + va_list ap; + va_start(ap, fmt); + if (mockdebug) + return fprintf(stderr, MOCK) + vfprintf(stderr, fmt, ap); + return 0; } static int mock_start_uart(void) { - struct termios settings; - - if (!isatty(STDIN)) - { - perror("setting tty in raw mode: isatty(STDIN)"); - return -1; - } - if (tcgetattr(STDIN, &initial_settings) < 0) - { - perror("setting tty in raw mode: tcgetattr(STDIN)"); - return -1; - } - settings = initial_settings; - settings.c_lflag &= ~(ignore_sigint ? ISIG : 0); - settings.c_lflag &= ~(ECHO | ICANON); - settings.c_iflag &= ~(ICRNL | INLCR | ISTRIP | IXON); - settings.c_oflag |= (ONLCR); - settings.c_cc[VMIN] = 0; - settings.c_cc[VTIME] = 0; - if (tcsetattr(STDIN, TCSANOW, &settings) < 0) - { - perror("setting tty in raw mode: tcsetattr(STDIN)"); - return -1; - } - restore_tty = true; - return 0; + struct termios settings; + + if (!isatty(STDIN)) + { + perror("setting tty in raw mode: isatty(STDIN)"); + return -1; + } + if (tcgetattr(STDIN, &initial_settings) < 0) + { + perror("setting tty in raw mode: tcgetattr(STDIN)"); + return -1; + } + settings = initial_settings; + settings.c_lflag &= ~(ignore_sigint ? ISIG : 0); + settings.c_lflag &= ~(ECHO | ICANON); + settings.c_iflag &= ~(ICRNL | INLCR | ISTRIP | IXON); + settings.c_oflag |= (ONLCR); + settings.c_cc[VMIN] = 0; + settings.c_cc[VTIME] = 0; + if (tcsetattr(STDIN, TCSANOW, &settings) < 0) + { + perror("setting tty in raw mode: tcsetattr(STDIN)"); + return -1; + } + restore_tty = true; + return 0; } static int mock_stop_uart(void) { - if (!restore_tty) return 0; - if (!isatty(STDIN)) { - perror("restoring tty: isatty(STDIN)"); - return -1; - } - if (tcsetattr(STDIN, TCSANOW, &initial_settings) < 0) - { - perror("restoring tty: tcsetattr(STDIN)"); - return -1; - } - printf("\e[?25h"); // show cursor - return (0); + if (!restore_tty) + return 0; + if (!isatty(STDIN)) + { + perror("restoring tty: isatty(STDIN)"); + return -1; + } + if (tcsetattr(STDIN, TCSANOW, &initial_settings) < 0) + { + perror("restoring tty: tcsetattr(STDIN)"); + return -1; + } + printf("\e[?25h"); // show cursor + return (0); } 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) +void help(const char* argv0, int exitcode) { - printf( - "%s - compiled with esp8266/arduino emulator\n" - "options:\n" - "\t-h\n" - "\tnetwork:\n" - "\t-i - use this interface for IP address\n" - "\t-l - bind tcp/udp servers to interface only (not 0.0.0.0)\n" - "\t-s - port shifter (default: %d, when root: 0)\n" - "\tterminal:\n" - "\t-b - blocking tty/mocked-uart (default: not blocking tty)\n" - "\t-T - show timestamp on output\n" - "\tFS:\n" - "\t-P - path for fs-persistent files (default: %s-)\n" - "\t-S - spiffs size in KBytes (default: %zd)\n" - "\t-L - littlefs size in KBytes (default: %zd)\n" - "\t (spiffs, littlefs: negative value will force mismatched size)\n" - "\tgeneral:\n" - "\t-c - ignore CTRL-C (send it via Serial)\n" - "\t-f - no throttle (possibly 100%%CPU)\n" - "\t-1 - run loop once then exit (for host testing)\n" - "\t-v - verbose\n" - , argv0, MOCK_PORT_SHIFTER, argv0, spiffs_kb, littlefs_kb); - exit(exitcode); + printf("%s - compiled with esp8266/arduino emulator\n" + "options:\n" + "\t-h\n" + "\tnetwork:\n" + "\t-i - use this interface for IP address\n" + "\t-l - bind tcp/udp servers to interface only (not 0.0.0.0)\n" + "\t-s - port shifter (default: %d, when root: 0)\n" + "\tterminal:\n" + "\t-b - blocking tty/mocked-uart (default: not blocking tty)\n" + "\t-T - show timestamp on output\n" + "\tFS:\n" + "\t-P - path for fs-persistent files (default: %s-)\n" + "\t-S - spiffs size in KBytes (default: %zd)\n" + "\t-L - littlefs size in KBytes (default: %zd)\n" + "\t (spiffs, littlefs: negative value will force mismatched size)\n" + "\tgeneral:\n" + "\t-c - ignore CTRL-C (send it via Serial)\n" + "\t-f - no throttle (possibly 100%%CPU)\n" + "\t-1 - run loop once then exit (for host testing)\n" + "\t-v - verbose\n", + argv0, MOCK_PORT_SHIFTER, argv0, spiffs_kb, littlefs_kb); + exit(exitcode); } -static struct option options[] = -{ - { "help", no_argument, NULL, 'h' }, - { "fast", no_argument, NULL, 'f' }, - { "local", no_argument, NULL, 'l' }, - { "sigint", no_argument, NULL, 'c' }, - { "blockinguart", no_argument, NULL, 'b' }, - { "verbose", no_argument, NULL, 'v' }, - { "timestamp", no_argument, NULL, 'T' }, - { "interface", required_argument, NULL, 'i' }, - { "fspath", required_argument, NULL, 'P' }, - { "spiffskb", required_argument, NULL, 'S' }, - { "littlefskb", required_argument, NULL, 'L' }, - { "portshifter", required_argument, NULL, 's' }, - { "once", no_argument, NULL, '1' }, +static struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "fast", no_argument, NULL, 'f' }, + { "local", no_argument, NULL, 'l' }, + { "sigint", no_argument, NULL, 'c' }, + { "blockinguart", no_argument, NULL, 'b' }, + { "verbose", no_argument, NULL, 'v' }, + { "timestamp", no_argument, NULL, 'T' }, + { "interface", required_argument, NULL, 'i' }, + { "fspath", required_argument, NULL, 'P' }, + { "spiffskb", required_argument, NULL, 'S' }, + { "littlefskb", required_argument, NULL, 'L' }, + { "portshifter", required_argument, NULL, 's' }, + { "once", no_argument, NULL, '1' }, }; -void cleanup () +void cleanup() { - mock_stop_spiffs(); - mock_stop_littlefs(); - mock_stop_uart(); + mock_stop_udp(); + mock_stop_spiffs(); + mock_stop_littlefs(); + mock_stop_uart(); } -void make_fs_filename (String& name, const char* fspath, const char* argv0) +void make_fs_filename(String& name, const char* fspath, const char* argv0) { - name.clear(); - if (fspath) - { - int lastSlash = -1; - for (int i = 0; argv0[i]; i++) - if (argv0[i] == '/') - lastSlash = i; - name = fspath; - name += '/'; - name += &argv0[lastSlash + 1]; - } - else - name = argv0; + name.clear(); + if (fspath) + { + int lastSlash = -1; + for (int i = 0; argv0[i]; i++) + if (argv0[i] == '/') + lastSlash = i; + name = fspath; + name += '/'; + name += &argv0[lastSlash + 1]; + } + else + name = argv0; } -void control_c (int sig) +void control_c(int sig) { - (void)sig; - - if (user_exit) - { - fprintf(stderr, MOCK "stuck, killing\n"); - cleanup(); - exit(1); - } - user_exit = true; + (void)sig; + + if (user_exit) + { + fprintf(stderr, MOCK "stuck, killing\n"); + cleanup(); + exit(1); + } + user_exit = true; } -int main (int argc, char* const argv []) +int main(int argc, char* const argv[]) { - bool fast = false; - blocking_uart = false; // global - - signal(SIGINT, control_c); - signal(SIGTERM, control_c); - if (geteuid() == 0) - mock_port_shifter = 0; - else - mock_port_shifter = MOCK_PORT_SHIFTER; - - for (;;) - { - int n = getopt_long(argc, argv, "hlcfbvTi:S:s:L:P:1", options, NULL); - if (n < 0) - break; - switch (n) - { - case 'h': - help(argv[0], EXIT_SUCCESS); - break; - case 'i': - host_interface = optarg; - break; - case 'l': - global_ipv4_netfmt = NO_GLOBAL_BINDING; - break; - case 's': - mock_port_shifter = atoi(optarg); - break; - case 'c': - ignore_sigint = true; - break; - case 'f': - fast = true; - break; - case 'S': - spiffs_kb = atoi(optarg); - break; - case 'L': - littlefs_kb = atoi(optarg); - break; - case 'P': - fspath = optarg; - break; - case 'b': - blocking_uart = true; - break; - case 'v': - mockdebug = true; - break; - case 'T': - serial_timestamp = true; - break; - case '1': - run_once = true; - break; - default: - help(argv[0], EXIT_FAILURE); - } - } - - mockverbose("server port shifter: %d\n", mock_port_shifter); - - if (spiffs_kb) - { - String name; - make_fs_filename(name, fspath, argv[0]); - name += "-spiffs"; - name += String(spiffs_kb > 0? spiffs_kb: -spiffs_kb, DEC); - name += "KB"; - mock_start_spiffs(name, spiffs_kb); - } - - if (littlefs_kb) - { - String name; - make_fs_filename(name, fspath, argv[0]); - name += "-littlefs"; - name += String(littlefs_kb > 0? littlefs_kb: -littlefs_kb, DEC); - name += "KB"; - mock_start_littlefs(name, littlefs_kb); - } - - // setup global global_ipv4_netfmt - wifi_get_ip_info(0, nullptr); - - if (!blocking_uart) - { - // set stdin to non blocking mode - mock_start_uart(); - } - - // install exit handler in case Esp.restart() is called - atexit(cleanup); - - setup(); - while (!user_exit) - { - uint8_t data = mock_read_uart(); - - if (data) - uart_new_data(UART0, data); - if (!fast) - usleep(1000); // not 100% cpu, ~1000 loops per second - loop(); - check_incoming_udp(); - - if (run_once) - user_exit = true; - } - cleanup(); - - return 0; + bool fast = false; + blocking_uart = false; // global + + signal(SIGINT, control_c); + signal(SIGTERM, control_c); + if (geteuid() == 0) + mock_port_shifter = 0; + else + mock_port_shifter = MOCK_PORT_SHIFTER; + + for (;;) + { + int n = getopt_long(argc, argv, "hlcfbvTi:S:s:L:P:1", options, NULL); + if (n < 0) + break; + switch (n) + { + case 'h': + help(argv[0], EXIT_SUCCESS); + break; + case 'i': + host_interface = optarg; + break; + case 'l': + global_ipv4_netfmt = NO_GLOBAL_BINDING; + break; + case 's': + mock_port_shifter = atoi(optarg); + break; + case 'c': + ignore_sigint = true; + break; + case 'f': + fast = true; + break; + case 'S': + spiffs_kb = atoi(optarg); + break; + case 'L': + littlefs_kb = atoi(optarg); + break; + case 'P': + fspath = optarg; + break; + case 'b': + blocking_uart = true; + break; + case 'v': + mockdebug = true; + break; + case 'T': + serial_timestamp = true; + break; + case '1': + run_once = true; + break; + default: + help(argv[0], EXIT_FAILURE); + } + } + + mockverbose("server port shifter: %d\n", mock_port_shifter); + + if (spiffs_kb) + { + String name; + make_fs_filename(name, fspath, argv[0]); + name += "-spiffs"; + name += String(spiffs_kb > 0 ? spiffs_kb : -spiffs_kb, DEC); + name += "KB"; + mock_start_spiffs(name, spiffs_kb); + } + + if (littlefs_kb) + { + String name; + make_fs_filename(name, fspath, argv[0]); + name += "-littlefs"; + name += String(littlefs_kb > 0 ? littlefs_kb : -littlefs_kb, DEC); + name += "KB"; + mock_start_littlefs(name, littlefs_kb); + } + + // setup global global_ipv4_netfmt + wifi_get_ip_info(0, nullptr); + + if (!blocking_uart) + { + // set stdin to non blocking mode + mock_start_uart(); + } + + // install exit handler in case Esp.restart() is called + atexit(cleanup); + + // first call to millis(): now is millis() and micros() beginning + millis(); + + setup(); + while (!user_exit) + { + uint8_t data = mock_read_uart(); + + if (data) + uart_new_data(UART0, data); + if (!fast) + usleep(1000); // not 100% cpu, ~1000 loops per second + loop(); + loop_end(); + check_incoming_udp(); + + if (run_once) + user_exit = true; + } + cleanup(); + + return 0; } diff --git a/tests/host/common/ArduinoMainLittlefs.cpp b/tests/host/common/ArduinoMainLittlefs.cpp index bf040a2fc5..c85e0b9dbc 100644 --- a/tests/host/common/ArduinoMainLittlefs.cpp +++ b/tests/host/common/ArduinoMainLittlefs.cpp @@ -3,15 +3,14 @@ LittleFSMock* littlefs_mock = nullptr; -void mock_start_littlefs (const String& fname, size_t size_kb, size_t block_kb, size_t page_b) +void mock_start_littlefs(const String& fname, size_t size_kb, size_t block_kb, size_t page_b) { - littlefs_mock = new LittleFSMock(size_kb * 1024, block_kb * 1024, page_b, fname); + littlefs_mock = new LittleFSMock(size_kb * 1024, block_kb * 1024, page_b, fname); } -void mock_stop_littlefs () +void mock_stop_littlefs() { - if (littlefs_mock) - delete littlefs_mock; - littlefs_mock = nullptr; + if (littlefs_mock) + delete littlefs_mock; + littlefs_mock = nullptr; } - diff --git a/tests/host/common/ArduinoMainSpiffs.cpp b/tests/host/common/ArduinoMainSpiffs.cpp index 1e5099d0ff..035cc9e752 100644 --- a/tests/host/common/ArduinoMainSpiffs.cpp +++ b/tests/host/common/ArduinoMainSpiffs.cpp @@ -3,15 +3,14 @@ SpiffsMock* spiffs_mock = nullptr; -void mock_start_spiffs (const String& fname, size_t size_kb, size_t block_kb, size_t page_b) +void mock_start_spiffs(const String& fname, size_t size_kb, size_t block_kb, size_t page_b) { - spiffs_mock = new SpiffsMock(size_kb * 1024, block_kb * 1024, page_b, fname); + spiffs_mock = new SpiffsMock(size_kb * 1024, block_kb * 1024, page_b, fname); } -void mock_stop_spiffs () +void mock_stop_spiffs() { - if (spiffs_mock) - delete spiffs_mock; - spiffs_mock = nullptr; + if (spiffs_mock) + delete spiffs_mock; + spiffs_mock = nullptr; } - diff --git a/tests/host/common/ArduinoMainUdp.cpp b/tests/host/common/ArduinoMainUdp.cpp index 6c4a034d2a..49d7c091d2 100644 --- a/tests/host/common/ArduinoMainUdp.cpp +++ b/tests/host/common/ArduinoMainUdp.cpp @@ -33,28 +33,33 @@ #include #include -std::map udps; +static std::map udps; -void register_udp (int sock, UdpContext* udp) +void register_udp(int sock, UdpContext* udp) { - if (udp) - udps[sock] = udp; - else - udps.erase(sock); + if (udp) + udps[sock] = udp; + else + udps.erase(sock); } -void check_incoming_udp () +void check_incoming_udp() { - // check incoming udp - for (auto& udp: udps) - { - pollfd p; - p.fd = udp.first; - p.events = POLLIN; - if (poll(&p, 1, 0) && p.revents == POLLIN) - { - mockverbose("UDP poll(%d) -> cb\r", p.fd); - udp.second->mock_cb(); - } - } + // check incoming udp + for (auto& udp : udps) + { + pollfd p; + p.fd = udp.first; + p.events = POLLIN; + if (poll(&p, 1, 0) && p.revents == POLLIN) + { + mockverbose("UDP poll(%d) -> cb\r", p.fd); + udp.second->mock_cb(); + } + } +} + +void mock_stop_udp() +{ + udps.clear(); } diff --git a/tests/host/common/ClientContextSocket.cpp b/tests/host/common/ClientContextSocket.cpp index b2366fa72c..a0bc7610d8 100644 --- a/tests/host/common/ClientContextSocket.cpp +++ b/tests/host/common/ClientContextSocket.cpp @@ -39,154 +39,173 @@ #include #include -int mockSockSetup (int sock) +int mockSockSetup(int sock) { - if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) - { - perror("socket fcntl(O_NONBLOCK)"); - close(sock); - return -1; - } + if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) + { + perror("socket fcntl(O_NONBLOCK)"); + close(sock); + return -1; + } #ifndef MSG_NOSIGNAL - int i = 1; - if (setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof i) == -1) - { - perror("sockopt(SO_NOSIGPIPE)(macOS)"); - close(sock); - return -1; - } + int i = 1; + if (setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &i, sizeof i) == -1) + { + perror("sockopt(SO_NOSIGPIPE)(macOS)"); + close(sock); + return -1; + } #endif - return sock; + return sock; } -int mockConnect (uint32_t ipv4, int& sock, int port) +int mockConnect(uint32_t ipv4, int& sock, int port) { - struct sockaddr_in server; - if ((sock = ::socket(AF_INET, SOCK_STREAM, 0)) == -1) - { - perror(MOCK "ClientContext:connect: ::socket()"); - return 0; - } - server.sin_family = AF_INET; - server.sin_port = htons(port); - memcpy(&server.sin_addr, &ipv4, 4); - if (::connect(sock, (struct sockaddr*)&server, sizeof(server)) == -1) - { - perror(MOCK "ClientContext::connect: ::connect()"); - return 0; - } - - return mockSockSetup(sock) == -1? 0: 1; + struct sockaddr_in server; + if ((sock = ::socket(AF_INET, SOCK_STREAM, 0)) == -1) + { + perror(MOCK "ClientContext:connect: ::socket()"); + return 0; + } + server.sin_family = AF_INET; + server.sin_port = htons(port); + memcpy(&server.sin_addr, &ipv4, 4); + if (::connect(sock, (struct sockaddr*)&server, sizeof(server)) == -1) + { + perror(MOCK "ClientContext::connect: ::connect()"); + return 0; + } + + return mockSockSetup(sock) == -1 ? 0 : 1; } -ssize_t mockFillInBuf (int sock, char* ccinbuf, size_t& ccinbufsize) +ssize_t mockFillInBuf(int sock, char* ccinbuf, size_t& ccinbufsize) { - size_t maxread = CCBUFSIZE - ccinbufsize; - ssize_t ret = ::read(sock, ccinbuf + ccinbufsize, maxread); - - if (ret == 0) - { - // connection closed - return -1; - } - - if (ret == -1) - { - if (errno != EAGAIN) - { - fprintf(stderr, MOCK "ClientContext::(read/peek fd=%i): filling buffer for %zd bytes: %s\n", sock, maxread, strerror(errno)); - return -1; - } - ret = 0; - } - ccinbufsize += ret; - return ret; + size_t maxread = CCBUFSIZE - ccinbufsize; + ssize_t ret = ::read(sock, ccinbuf + ccinbufsize, maxread); + + if (ret == 0) + { + // connection closed + // nothing is read + return 0; + } + + if (ret == -1) + { + if (errno != EAGAIN) + { + fprintf(stderr, + MOCK "ClientContext::(read/peek fd=%i): filling buffer for %zd bytes: %s\n", + sock, maxread, strerror(errno)); + // error + return -1; + } + ret = 0; + } + + ccinbufsize += ret; + return ret; } -ssize_t mockPeekBytes (int sock, char* dst, size_t usersize, int timeout_ms, char* ccinbuf, size_t& ccinbufsize) +ssize_t mockPeekBytes(int sock, char* dst, size_t usersize, int timeout_ms, char* ccinbuf, + size_t& ccinbufsize) { - if (usersize > CCBUFSIZE) - mockverbose("CCBUFSIZE(%d) should be increased by %zd bytes (-> %zd)\n", CCBUFSIZE, usersize - CCBUFSIZE, usersize); - - struct pollfd p; - size_t retsize = 0; - do - { - if (usersize <= ccinbufsize) - { - // data already buffered - retsize = usersize; - break; - } - - // check incoming data data - if (mockFillInBuf(sock, ccinbuf, ccinbufsize) < 0) - return -1; - if (usersize <= ccinbufsize) - { - // data just received - retsize = usersize; - break; - } - - // wait for more data until timeout - p.fd = sock; - p.events = POLLIN; - } while (poll(&p, 1, timeout_ms) == 1); - + // usersize==0: peekAvailable() + + if (usersize > CCBUFSIZE) + mockverbose("CCBUFSIZE(%d) should be increased by %zd bytes (-> %zd)\n", CCBUFSIZE, + usersize - CCBUFSIZE, usersize); + + struct pollfd p; + size_t retsize = 0; + do + { + if (usersize && usersize <= ccinbufsize) + { + // data already buffered + retsize = usersize; + break; + } + + // check incoming data data + if (mockFillInBuf(sock, ccinbuf, ccinbufsize) < 0) + { + return -1; + } + + if (usersize == 0 && ccinbufsize) + // peekAvailable + return ccinbufsize; + + if (usersize <= ccinbufsize) + { + // data just received + retsize = usersize; + break; + } + + // wait for more data until timeout + p.fd = sock; + p.events = POLLIN; + } while (poll(&p, 1, timeout_ms) == 1); + if (dst) { memcpy(dst, ccinbuf, retsize); } - return retsize; + return retsize; } -ssize_t mockRead (int sock, char* dst, size_t size, int timeout_ms, char* ccinbuf, size_t& ccinbufsize) +ssize_t mockRead(int sock, char* dst, size_t size, int timeout_ms, char* ccinbuf, + size_t& ccinbufsize) { - ssize_t copied = mockPeekBytes(sock, dst, size, timeout_ms, ccinbuf, ccinbufsize); - if (copied < 0) - return -1; - // swallow (XXX use a circular buffer) - memmove(ccinbuf, ccinbuf + copied, ccinbufsize - copied); - ccinbufsize -= copied; - return copied; + ssize_t copied = mockPeekBytes(sock, dst, size, timeout_ms, ccinbuf, ccinbufsize); + if (copied < 0) + return -1; + // swallow (XXX use a circular buffer) + memmove(ccinbuf, ccinbuf + copied, ccinbufsize - copied); + ccinbufsize -= copied; + return copied; } - -ssize_t mockWrite (int sock, const uint8_t* data, size_t size, int timeout_ms) + +ssize_t mockWrite(int sock, const uint8_t* data, size_t size, int timeout_ms) { - size_t sent = 0; - while (sent < size) - { - - struct pollfd p; - p.fd = sock; - p.events = POLLOUT; - int ret = poll(&p, 1, timeout_ms); - if (ret == -1) - { - fprintf(stderr, MOCK "ClientContext::write(%d): %s\n", sock, strerror(errno)); - return -1; - } - if (ret) - { + size_t sent = 0; + while (sent < size) + { + struct pollfd p; + p.fd = sock; + p.events = POLLOUT; + int ret = poll(&p, 1, timeout_ms); + if (ret == -1) + { + fprintf(stderr, MOCK "ClientContext::write(%d): %s\n", sock, strerror(errno)); + return -1; + } + if (ret) + { #ifndef MSG_NOSIGNAL - ret = ::write(sock, data + sent, size - sent); + ret = ::write(sock, data + sent, size - sent); #else - ret = ::send(sock, data + sent, size - sent, MSG_NOSIGNAL); + ret = ::send(sock, data + sent, size - sent, MSG_NOSIGNAL); +#endif + if (ret == -1) + { + fprintf(stderr, MOCK "ClientContext::write/send(%d): %s\n", sock, strerror(errno)); + return -1; + } + sent += ret; + if (sent < size) + fprintf(stderr, MOCK "ClientContext::write: sent %d bytes (%zd / %zd)\n", ret, sent, + size); + } + } +#ifdef DEBUG_ESP_WIFI + mockverbose(MOCK "ClientContext::write: total sent %zd bytes\n", sent); #endif - if (ret == -1) - { - fprintf(stderr, MOCK "ClientContext::read: write(%d): %s\n", sock, strerror(errno)); - return -1; - } - sent += ret; - if (sent < size) - fprintf(stderr, MOCK "ClientContext::write: sent %d bytes (%zd / %zd)\n", ret, sent, size); - } - } - fprintf(stderr, MOCK "ClientContext::write: total sent %zd bytes\n", sent); - return sent; + return sent; } diff --git a/tests/host/common/ClientContextTools.cpp b/tests/host/common/ClientContextTools.cpp index ccc06b56d9..74f66fd6bb 100644 --- a/tests/host/common/ClientContextTools.cpp +++ b/tests/host/common/ClientContextTools.cpp @@ -35,23 +35,33 @@ #include #include -#include // gethostbyname +#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); - if (!hbn) - return ERR_TIMEOUT; - addr->addr = *(uint32_t*)hbn->h_addr_list[0]; - return ERR_OK; + auto* hbn = gethostbyname(hostname); + if (!hbn) + return ERR_TIMEOUT; + + 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) +tcp_pcb* tcp_new(void) { - // this is useless - // ClientContext is setting the source port and we don't care here - return &mock_tcp_pcb; + // this is useless + // ClientContext is setting the source port and we don't care here + return &mock_tcp_pcb; } 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/EEPROM.h b/tests/host/common/EEPROM.h index a1e47546de..c6bf0faa53 100644 --- a/tests/host/common/EEPROM.h +++ b/tests/host/common/EEPROM.h @@ -32,46 +32,53 @@ #ifndef EEPROM_MOCK #define EEPROM_MOCK -class EEPROMClass { +class EEPROMClass +{ public: - EEPROMClass(uint32_t sector); - EEPROMClass(void); - ~EEPROMClass(); + EEPROMClass(uint32_t sector); + EEPROMClass(void); + ~EEPROMClass(); - void begin(size_t size); - uint8_t read(int address); - void write(int address, uint8_t val); - bool commit(); - void end(); + void begin(size_t size); + uint8_t read(int address); + void write(int address, uint8_t val); + bool commit(); + void end(); - template - T& get(int const address, T& t) - { - if (address < 0 || address + sizeof(T) > _size) - return t; - for (size_t i = 0; i < sizeof(T); i++) - ((uint8_t*)&t)[i] = read(i); - return t; - } + template + T& get(int const address, T& t) + { + if (address < 0 || address + sizeof(T) > _size) + return t; + for (size_t i = 0; i < sizeof(T); i++) + ((uint8_t*)&t)[i] = read(i); + return t; + } - template - const T& put(int const address, const T& t) - { - if (address < 0 || address + sizeof(T) > _size) - return t; - for (size_t i = 0; i < sizeof(T); i++) - write(i, ((uint8_t*)&t)[i]); - return t; - } + template + const T& put(int const address, const T& t) + { + if (address < 0 || address + sizeof(T) > _size) + return t; + for (size_t i = 0; i < sizeof(T); i++) + write(i, ((uint8_t*)&t)[i]); + return t; + } - size_t length() { return _size; } + size_t length() + { + return _size; + } - //uint8_t& operator[](int const address) { return read(address); } - uint8_t operator[] (int address) { return read(address); } + // uint8_t& operator[](int const address) { return read(address); } + uint8_t operator[](int address) + { + return read(address); + } protected: - size_t _size = 0; - int _fd = -1; + size_t _size = 0; + int _fd = -1; }; #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_EEPROM) diff --git a/tests/host/common/HostWiring.cpp b/tests/host/common/HostWiring.cpp index 765d4297b6..3c40de0684 100644 --- a/tests/host/common/HostWiring.cpp +++ b/tests/host/common/HostWiring.cpp @@ -37,49 +37,87 @@ #define VERBOSE(x...) mockverbose(x) #endif -void pinMode (uint8_t pin, uint8_t mode) +#define GPIONUM 17 + +static uint8_t _mode[GPIONUM]; +static uint8_t _gpio[GPIONUM]; + +void pinMode(uint8_t pin, uint8_t mode) { - #define xxx(mode) case mode: m=STRHELPER(mode); break - const char* m; - switch (mode) - { - case INPUT: m="INPUT"; break; - case OUTPUT: m="OUTPUT"; break; - case INPUT_PULLUP: m="INPUT_PULLUP"; break; - case OUTPUT_OPEN_DRAIN: m="OUTPUT_OPEN_DRAIN"; break; - case INPUT_PULLDOWN_16: m="INPUT_PULLDOWN_16"; break; - case WAKEUP_PULLUP: m="WAKEUP_PULLUP"; break; - case WAKEUP_PULLDOWN: m="WAKEUP_PULLDOWN"; break; - default: m="(special)"; - } - VERBOSE("gpio%d: mode='%s'\n", pin, m); +#define xxx(mode) \ + case mode: \ + m = STRHELPER(mode); \ + break + const char* m; + switch (mode) + { + case INPUT: + m = "INPUT"; + break; + case OUTPUT: + m = "OUTPUT"; + break; + case INPUT_PULLUP: + m = "INPUT_PULLUP"; + break; + case OUTPUT_OPEN_DRAIN: + m = "OUTPUT_OPEN_DRAIN"; + break; + case INPUT_PULLDOWN_16: + m = "INPUT_PULLDOWN_16"; + break; + case WAKEUP_PULLUP: + m = "WAKEUP_PULLUP"; + break; + case WAKEUP_PULLDOWN: + m = "WAKEUP_PULLDOWN"; + break; + default: + m = "(special)"; + } + VERBOSE("gpio%d: mode='%s'\n", pin, m); + + if (pin < GPIONUM) + { + _mode[pin] = mode; + } } void digitalWrite(uint8_t pin, uint8_t val) { - VERBOSE("digitalWrite(pin=%d val=%d)\n", pin, val); + VERBOSE("digitalWrite(pin=%d val=%d)\n", pin, val); + if (pin < GPIONUM) + { + _gpio[pin] = val; + } } void analogWrite(uint8_t pin, int val) { - VERBOSE("analogWrite(pin=%d, val=%d\n", pin, val); + VERBOSE("analogWrite(pin=%d, val=%d\n", pin, val); } int analogRead(uint8_t pin) { - (void)pin; - return 512; + (void)pin; + return 512; } void analogWriteRange(uint32_t range) { - VERBOSE("analogWriteRange(range=%d)\n", range); + VERBOSE("analogWriteRange(range=%d)\n", range); } int digitalRead(uint8_t pin) { - VERBOSE("digitalRead(%d)\n", pin); + VERBOSE("digitalRead(%d)\n", pin); - // pin 0 is most likely a low active input - return pin ? 0 : 1; + if (pin < GPIONUM) + { + return _gpio[pin] != 0; + } + else + { + return 0; + } } diff --git a/tests/host/common/MockDigital.cpp b/tests/host/common/MockDigital.cpp deleted file mode 100644 index aa04527ab5..0000000000 --- a/tests/host/common/MockDigital.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - digital.c - wiring digital implementation for esp8266 - - Copyright (c) 2015 Hristo Gochkov. 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 -*/ -#define ARDUINO_MAIN -#include "wiring_private.h" -#include "pins_arduino.h" -#include "c_types.h" -#include "eagle_soc.h" -#include "ets_sys.h" -#include "user_interface.h" -#include "core_esp8266_waveform.h" -#include "interrupts.h" - -extern "C" { - -static uint8_t _mode[17]; -static uint8_t _gpio[17]; - -extern void pinMode(uint8_t pin, uint8_t mode) { - if (pin < 17) { - _mode[pin] = mode; - } -} - -extern void digitalWrite(uint8_t pin, uint8_t val) { - if (pin < 17) { - _gpio[pin] = val; - } -} - -extern int digitalRead(uint8_t pin) { - if (pin < 17) { - return _gpio[pin] != 0; - } else { - return 0; - } -} - -}; diff --git a/tests/host/common/MockEEPROM.cpp b/tests/host/common/MockEEPROM.cpp index 6357c73dd2..ff44b6b222 100644 --- a/tests/host/common/MockEEPROM.cpp +++ b/tests/host/common/MockEEPROM.cpp @@ -42,52 +42,52 @@ #define EEPROM_FILE_NAME "eeprom" -EEPROMClass::EEPROMClass () -{ -} +EEPROMClass::EEPROMClass() { } -EEPROMClass::~EEPROMClass () +EEPROMClass::~EEPROMClass() { - if (_fd >= 0) - close(_fd); + if (_fd >= 0) + close(_fd); } void EEPROMClass::begin(size_t size) { - _size = size; - if ( (_fd = open(EEPROM_FILE_NAME, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1 - || ftruncate(_fd, size) == -1) - { - fprintf(stderr, MOCK "EEPROM: cannot open/create '%s' for r/w: %s\n\r", EEPROM_FILE_NAME, strerror(errno)); - _fd = -1; - } + _size = size; + if ((_fd = open(EEPROM_FILE_NAME, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) + == -1 + || ftruncate(_fd, size) == -1) + { + fprintf(stderr, MOCK "EEPROM: cannot open/create '%s' for r/w: %s\n\r", EEPROM_FILE_NAME, + strerror(errno)); + _fd = -1; + } } void EEPROMClass::end() { - if (_fd != -1) - close(_fd); + if (_fd != -1) + close(_fd); } bool EEPROMClass::commit() { - return true; + return true; } -uint8_t EEPROMClass::read (int x) +uint8_t EEPROMClass::read(int x) { - char c = 0; - if (pread(_fd, &c, 1, x) != 1) - fprintf(stderr, MOCK "eeprom: %s\n\r", strerror(errno)); - return c; + char c = 0; + if (pread(_fd, &c, 1, x) != 1) + fprintf(stderr, MOCK "eeprom: %s\n\r", strerror(errno)); + return c; } -void EEPROMClass::write (int x, uint8_t c) +void EEPROMClass::write(int x, uint8_t c) { - if (x > (int)_size) - fprintf(stderr, MOCK "### eeprom beyond\r\n"); - else if (pwrite(_fd, &c, 1, x) != 1) - fprintf(stderr, MOCK "eeprom: %s\n\r", strerror(errno)); + if (x > (int)_size) + fprintf(stderr, MOCK "### eeprom beyond\r\n"); + else if (pwrite(_fd, &c, 1, x) != 1) + fprintf(stderr, MOCK "eeprom: %s\n\r", strerror(errno)); } #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_EEPROM) diff --git a/tests/host/common/MockEsp.cpp b/tests/host/common/MockEsp.cpp index f06d829f3e..18e7be6c83 100644 --- a/tests/host/common/MockEsp.cpp +++ b/tests/host/common/MockEsp.cpp @@ -36,195 +36,226 @@ #include -unsigned long long operator"" _kHz(unsigned long long x) { +#include +struct rst_info resetInfo; + +unsigned long long operator"" _kHz(unsigned long long x) +{ return x * 1000; } -unsigned long long operator"" _MHz(unsigned long long x) { +unsigned long long operator"" _MHz(unsigned long long x) +{ return x * 1000 * 1000; } -unsigned long long operator"" _GHz(unsigned long long x) { +unsigned long long operator"" _GHz(unsigned long long x) +{ return x * 1000 * 1000 * 1000; } -unsigned long long operator"" _kBit(unsigned long long x) { +unsigned long long operator"" _kBit(unsigned long long x) +{ return x * 1024; } -unsigned long long operator"" _MBit(unsigned long long x) { +unsigned long long operator"" _MBit(unsigned long long x) +{ return x * 1024 * 1024; } -unsigned long long operator"" _GBit(unsigned long long x) { +unsigned long long operator"" _GBit(unsigned long long x) +{ return x * 1024 * 1024 * 1024; } -unsigned long long operator"" _kB(unsigned long long x) { +unsigned long long operator"" _kB(unsigned long long x) +{ return x * 1024; } -unsigned long long operator"" _MB(unsigned long long x) { +unsigned long long operator"" _MB(unsigned long long x) +{ return x * 1024 * 1024; } -unsigned long long operator"" _GB(unsigned long long x) { +unsigned long long operator"" _GB(unsigned long long x) +{ return x * 1024 * 1024 * 1024; } uint32_t _SPIFFS_start; -void eboot_command_write (struct eboot_command* cmd) +void eboot_command_write(struct eboot_command* cmd) { - (void)cmd; + (void)cmd; } EspClass ESP; -void EspClass::restart () +void EspClass::restart() { - mockverbose("Esp.restart(): exiting\n"); - exit(EXIT_SUCCESS); + mockverbose("Esp.restart(): exiting\n"); + exit(EXIT_SUCCESS); } uint32_t EspClass::getChipId() { - return 0xee1337; + return 0xee1337; } bool EspClass::checkFlashConfig(bool needsEquals) { - (void) needsEquals; - return true; + (void)needsEquals; + return true; } uint32_t EspClass::getSketchSize() { - return 400000; + return 400000; } uint32_t EspClass::getFreeHeap() { - return 30000; + return 30000; } -uint16_t EspClass::getMaxFreeBlockSize() +uint32_t EspClass::getMaxFreeBlockSize() { - return 20000; + return 20000; } String EspClass::getResetReason() { - return "Power on"; + return "Power on"; } uint32_t EspClass::getFreeSketchSpace() { - return 4 * 1024 * 1024; + return 4 * 1024 * 1024; } -const char *EspClass::getSdkVersion() +const char* EspClass::getSdkVersion() { - return "2.5.0"; + return "2.5.0"; } uint32_t EspClass::getFlashChipSpeed() { - return 40; + return 40; } -void EspClass::getHeapStats(uint32_t* hfree, uint16_t* hmax, uint8_t* hfrag) { - uint32_t hf = 10 * 1024; - float hm = 1 * 1024; +void EspClass::getHeapStats(uint32_t* hfree, uint16_t* hmax, uint8_t* hfrag) +{ + uint32_t hf = 10 * 1024; + float hm = 1 * 1024; - if (hfree) *hfree = hf; - if (hmax) *hmax = hm; - if (hfrag) *hfrag = 100 - (sqrt(hm) * 100) / hf; + if (hfree) + *hfree = hf; + if (hmax) + *hmax = hm; + if (hfrag) + *hfrag = 100 - (sqrt(hm) * 100) / hf; +} + +void EspClass::getHeapStats(uint32_t* hfree, uint32_t* hmax, uint8_t* hfrag) +{ + uint32_t hf = 10 * 1024; + float hm = 1 * 1024; + + if (hfree) + *hfree = hf; + if (hmax) + *hmax = hm; + if (hfrag) + *hfrag = 100 - (sqrt(hm) * 100) / hf; } bool EspClass::flashEraseSector(uint32_t sector) { - (void) sector; - return true; + (void)sector; + return true; } FlashMode_t EspClass::getFlashChipMode() { - return FM_DOUT; + return FM_DOUT; } FlashMode_t EspClass::magicFlashChipMode(uint8_t byte) { - (void) byte; - return FM_DOUT; + (void)byte; + return FM_DOUT; } -bool EspClass::flashWrite(uint32_t offset, const uint32_t *data, size_t size) +bool EspClass::flashWrite(uint32_t offset, const uint32_t* data, size_t size) { - (void)offset; - (void)data; - (void)size; - return true; + (void)offset; + (void)data; + (void)size; + return true; } -bool EspClass::flashWrite(uint32_t offset, const uint8_t *data, size_t size) +bool EspClass::flashWrite(uint32_t offset, const uint8_t* data, size_t size) { - (void)offset; - (void)data; - (void)size; - return true; + (void)offset; + (void)data; + (void)size; + return true; } -bool EspClass::flashRead(uint32_t offset, uint32_t *data, size_t size) +bool EspClass::flashRead(uint32_t offset, uint32_t* data, size_t size) { - (void)offset; - (void)data; - (void)size; - return true; + (void)offset; + (void)data; + (void)size; + return true; } -bool EspClass::flashRead(uint32_t offset, uint8_t *data, size_t size) +bool EspClass::flashRead(uint32_t offset, uint8_t* data, size_t size) { - (void)offset; - (void)data; - (void)size; - return true; + (void)offset; + (void)data; + (void)size; + return true; } -uint32_t EspClass::magicFlashChipSize(uint8_t byte) { - switch(byte & 0x0F) { - case 0x0: // 4 Mbit (512KB) - return (512_kB); - case 0x1: // 2 MBit (256KB) - return (256_kB); - case 0x2: // 8 MBit (1MB) - return (1_MB); - case 0x3: // 16 MBit (2MB) - return (2_MB); - case 0x4: // 32 MBit (4MB) - return (4_MB); - case 0x8: // 64 MBit (8MB) - return (8_MB); - case 0x9: // 128 MBit (16MB) - return (16_MB); - default: // fail? - return 0; +uint32_t EspClass::magicFlashChipSize(uint8_t byte) +{ + switch (byte & 0x0F) + { + case 0x0: // 4 Mbit (512KB) + return (512_kB); + case 0x1: // 2 MBit (256KB) + return (256_kB); + case 0x2: // 8 MBit (1MB) + return (1_MB); + case 0x3: // 16 MBit (2MB) + return (2_MB); + case 0x4: // 32 MBit (4MB) + return (4_MB); + case 0x8: // 64 MBit (8MB) + return (8_MB); + case 0x9: // 128 MBit (16MB) + return (16_MB); + default: // fail? + return 0; } } uint32_t EspClass::getFlashChipRealSize(void) { - return magicFlashChipSize(4); + return magicFlashChipSize(4); } uint32_t EspClass::getFlashChipSize(void) { - return magicFlashChipSize(4); + return magicFlashChipSize(4); } -String EspClass::getFullVersion () +String EspClass::getFullVersion() { - return "emulation-on-host"; + return "emulation-on-host"; } uint32_t EspClass::getFreeContStack() @@ -232,9 +263,7 @@ uint32_t EspClass::getFreeContStack() return 4000; } -void EspClass::resetFreeContStack() -{ -} +void EspClass::resetFreeContStack() { } uint32_t EspClass::getCycleCount() { @@ -248,18 +277,15 @@ uint32_t esp_get_cycle_count() return (((uint64_t)t.tv_sec) * 1000000 + t.tv_usec) * (F_CPU / 1000000); } -void EspClass::setDramHeap() -{ -} +void EspClass::setDramHeap() { } -void EspClass::setIramHeap() -{ -} +void EspClass::setIramHeap() { } -void EspClass::setExternalHeap() -{ -} +void EspClass::setExternalHeap() { } + +void EspClass::resetHeap() { } -void EspClass::resetHeap() +void EspClass::reset() { + abort(); } diff --git a/tests/host/common/MockSPI.cpp b/tests/host/common/MockSPI.cpp index 251cad914a..94422ddd2e 100644 --- a/tests/host/common/MockSPI.cpp +++ b/tests/host/common/MockSPI.cpp @@ -35,29 +35,23 @@ SPIClass SPI; #endif -SPIClass::SPIClass () -{ -} +SPIClass::SPIClass() { } uint8_t SPIClass::transfer(uint8_t data) { - return data; + return data; } -void SPIClass::begin() -{ -} +void SPIClass::begin() { } -void SPIClass::end() -{ -} +void SPIClass::end() { } void SPIClass::setFrequency(uint32_t freq) { - (void)freq; + (void)freq; } void SPIClass::setHwCs(bool use) { - (void)use; + (void)use; } diff --git a/tests/host/common/MockTools.cpp b/tests/host/common/MockTools.cpp index 5eb7969de7..600462feed 100644 --- a/tests/host/common/MockTools.cpp +++ b/tests/host/common/MockTools.cpp @@ -34,47 +34,97 @@ extern "C" { - -uint32_t lwip_htonl (uint32_t hostlong) { return htonl(hostlong); } -uint16_t lwip_htons (uint16_t hostshort) { return htons(hostshort); } -uint32_t lwip_ntohl (uint32_t netlong) { return ntohl(netlong); } -uint16_t lwip_ntohs (uint16_t netshort) { return ntohs(netshort); } - -char* ets_strcpy (char* d, const char* s) { return strcpy(d, s); } -char* ets_strncpy (char* d, const char* s, size_t n) { return strncpy(d, s, n); } -size_t ets_strlen (const char* s) { return strlen(s); } - -int ets_printf (const char* fmt, ...) -{ + uint32_t lwip_htonl(uint32_t hostlong) + { + return htonl(hostlong); + } + uint16_t lwip_htons(uint16_t hostshort) + { + return htons(hostshort); + } + uint32_t lwip_ntohl(uint32_t netlong) + { + return ntohl(netlong); + } + uint16_t lwip_ntohs(uint16_t netshort) + { + return ntohs(netshort); + } + + char* ets_strcpy(char* d, const char* s) + { + return strcpy(d, s); + } + char* ets_strncpy(char* d, const char* s, size_t n) + { + return strncpy(d, s, n); + } + size_t ets_strlen(const char* s) + { + return strlen(s); + } + + int ets_printf(const char* fmt, ...) + { va_list ap; va_start(ap, fmt); - int len = vprintf(fmt, ap); - va_end(ap); - return len; -} - -void stack_thunk_add_ref() { } -void stack_thunk_del_ref() { } -void stack_thunk_repaint() { } - -uint32_t stack_thunk_get_refcnt() { return 0; } -uint32_t stack_thunk_get_stack_top() { return 0; } -uint32_t stack_thunk_get_stack_bot() { return 0; } -uint32_t stack_thunk_get_cont_sp() { return 0; } -uint32_t stack_thunk_get_max_usage() { return 0; } -void stack_thunk_dump_stack() { } + int len = vprintf(fmt, ap); + va_end(ap); + return len; + } + + void stack_thunk_add_ref() { } + void stack_thunk_del_ref() { } + void stack_thunk_repaint() { } + + uint32_t stack_thunk_get_refcnt() + { + return 0; + } + uint32_t stack_thunk_get_stack_top() + { + return 0; + } + uint32_t stack_thunk_get_stack_bot() + { + return 0; + } + uint32_t stack_thunk_get_cont_sp() + { + return 0; + } + uint32_t stack_thunk_get_max_usage() + { + return 0; + } + 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) +{ + (void)server1; + (void)server2; + (void)server3; + + mockverbose("configTime: TODO (tz=%dH offset=%dS) (time will be host's)\n", timezone, + daylightOffset_sec); +} -void configTime(int timezone, int daylightOffset_sec, - const char* server1, const char* server2, const char* server3) +void configTime(const char* tz, const char* server1, const char* server2, const char* server3) { - (void)server1; - (void)server2; - (void)server3; + (void)server1; + (void)server2; + (void)server3; - mockverbose("configTime: TODO (tz=%dH offset=%dS) (time will be host's)\n", timezone, daylightOffset_sec); + mockverbose("configTime: TODO (tz='%s') (time will be host's)\n", tz); } diff --git a/tests/host/common/MockUART.cpp b/tests/host/common/MockUART.cpp index 825a4dc0d6..cce3648c58 100644 --- a/tests/host/common/MockUART.cpp +++ b/tests/host/common/MockUART.cpp @@ -28,466 +28,455 @@ is responsible for feeding the RX FIFO new data by calling uart_new_data(). */ -#include // write -#include // gettimeofday -#include // localtime +#include // write +#include // gettimeofday +#include // localtime #include "Arduino.h" #include "uart.h" //#define UART_DISCARD_NEWEST -extern "C" { - -bool blocking_uart = true; // system default - -static int s_uart_debug_nr = UART1; - -static uart_t *UART[2] = { NULL, NULL }; - -struct uart_rx_buffer_ +extern "C" { - size_t size; - size_t rpos; - size_t wpos; - uint8_t * buffer; -}; + bool blocking_uart = true; // system default -struct uart_ -{ - int uart_nr; - int baud_rate; - bool rx_enabled; - bool tx_enabled; - bool rx_overrun; - struct uart_rx_buffer_ * rx_buffer; -}; + static int s_uart_debug_nr = UART1; -bool serial_timestamp = false; + static uart_t* UART[2] = { NULL, NULL }; -// write one byte to the emulated UART -static void -uart_do_write_char(const int uart_nr, char c) -{ - static bool w = false; - - if (uart_nr >= UART0 && uart_nr <= UART1) - { - if (serial_timestamp && (c == '\n' || c == '\r')) - { - if (w) - { - FILE* out = uart_nr == UART0? stdout: stderr; - timeval tv; - gettimeofday(&tv, nullptr); - const tm* tm = localtime(&tv.tv_sec); - fprintf(out, "\r\n%d:%02d:%02d.%06d: ", tm->tm_hour, tm->tm_min, tm->tm_sec, (int)tv.tv_usec); - fflush(out); - w = false; - } - } - else - { + struct uart_rx_buffer_ + { + size_t size; + size_t rpos; + size_t wpos; + uint8_t* buffer; + }; + + struct uart_ + { + int uart_nr; + int baud_rate; + bool rx_enabled; + bool tx_enabled; + bool rx_overrun; + struct uart_rx_buffer_* rx_buffer; + }; + + bool serial_timestamp = false; + + // write one byte to the emulated UART + static void uart_do_write_char(const int uart_nr, char c) + { + static bool w = false; + + if (uart_nr >= UART0 && uart_nr <= UART1) + { + if (serial_timestamp && (c == '\n' || c == '\r')) + { + if (w) + { + FILE* out = uart_nr == UART0 ? stdout : stderr; + timeval tv; + gettimeofday(&tv, nullptr); + const tm* tm = localtime(&tv.tv_sec); + fprintf(out, "\r\n%d:%02d:%02d.%06d: ", tm->tm_hour, tm->tm_min, tm->tm_sec, + (int)tv.tv_usec); + fflush(out); + w = false; + } + } + else + { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-result" - write(uart_nr + 1, &c, 1); + write(uart_nr + 1, &c, 1); #pragma GCC diagnostic pop - w = true; - } - } -} + w = true; + } + } + } -// write a new byte into the RX FIFO buffer -static void -uart_handle_data(uart_t* uart, uint8_t data) -{ - struct uart_rx_buffer_ *rx_buffer = uart->rx_buffer; + // write a new byte into the RX FIFO buffer + static void uart_handle_data(uart_t* uart, uint8_t data) + { + struct uart_rx_buffer_* rx_buffer = uart->rx_buffer; - size_t nextPos = (rx_buffer->wpos + 1) % rx_buffer->size; - if(nextPos == rx_buffer->rpos) - { - uart->rx_overrun = true; + size_t nextPos = (rx_buffer->wpos + 1) % rx_buffer->size; + if (nextPos == rx_buffer->rpos) + { + uart->rx_overrun = true; #ifdef UART_DISCARD_NEWEST - return; + return; #else - if (++rx_buffer->rpos == rx_buffer->size) - rx_buffer->rpos = 0; + if (++rx_buffer->rpos == rx_buffer->size) + rx_buffer->rpos = 0; #endif - } - rx_buffer->buffer[rx_buffer->wpos] = data; - rx_buffer->wpos = nextPos; -} + } + rx_buffer->buffer[rx_buffer->wpos] = data; + rx_buffer->wpos = nextPos; + } -// insert a new byte into the RX FIFO nuffer -void -uart_new_data(const int uart_nr, uint8_t data) -{ - uart_t* uart = UART[uart_nr]; + // insert a new byte into the RX FIFO nuffer + void uart_new_data(const int uart_nr, uint8_t data) + { + uart_t* uart = UART[uart_nr]; - if(uart == NULL || !uart->rx_enabled) { - return; - } + if (uart == NULL || !uart->rx_enabled) + { + return; + } - uart_handle_data(uart, data); -} + uart_handle_data(uart, data); + } -static size_t -uart_rx_available_unsafe(const struct uart_rx_buffer_ * rx_buffer) -{ - size_t ret = rx_buffer->wpos - rx_buffer->rpos; + static size_t uart_rx_available_unsafe(const struct uart_rx_buffer_* rx_buffer) + { + size_t ret = rx_buffer->wpos - rx_buffer->rpos; - if(rx_buffer->wpos < rx_buffer->rpos) - ret = (rx_buffer->wpos + rx_buffer->size) - rx_buffer->rpos; + if (rx_buffer->wpos < rx_buffer->rpos) + ret = (rx_buffer->wpos + rx_buffer->size) - rx_buffer->rpos; - return ret; -} + return ret; + } -// taking data straight from fifo, only needed in uart_resize_rx_buffer() -static int -uart_read_char_unsafe(uart_t* uart) -{ - if (uart_rx_available_unsafe(uart->rx_buffer)) - { - // take oldest sw data - int ret = uart->rx_buffer->buffer[uart->rx_buffer->rpos]; - uart->rx_buffer->rpos = (uart->rx_buffer->rpos + 1) % uart->rx_buffer->size; - return ret; - } - // unavailable - return -1; -} + // taking data straight from fifo, only needed in uart_resize_rx_buffer() + static int uart_read_char_unsafe(uart_t* uart) + { + if (uart_rx_available_unsafe(uart->rx_buffer)) + { + // take oldest sw data + int ret = uart->rx_buffer->buffer[uart->rx_buffer->rpos]; + uart->rx_buffer->rpos = (uart->rx_buffer->rpos + 1) % uart->rx_buffer->size; + return ret; + } + // unavailable + return -1; + } -/**********************************************************/ -/************ UART API FUNCTIONS **************************/ -/**********************************************************/ + /**********************************************************/ + /************ UART API FUNCTIONS **************************/ + /**********************************************************/ -size_t -uart_rx_available(uart_t* uart) -{ - if(uart == NULL || !uart->rx_enabled) - return 0; + size_t uart_rx_available(uart_t* uart) + { + if (uart == NULL || !uart->rx_enabled) + return 0; - return uart_rx_available_unsafe(uart->rx_buffer); -} + return uart_rx_available_unsafe(uart->rx_buffer); + } -int -uart_peek_char(uart_t* uart) -{ - if(uart == NULL || !uart->rx_enabled) - return -1; + int uart_peek_char(uart_t* uart) + { + if (uart == NULL || !uart->rx_enabled) + return -1; - if (!uart_rx_available_unsafe(uart->rx_buffer)) - return -1; + if (!uart_rx_available_unsafe(uart->rx_buffer)) + return -1; - return uart->rx_buffer->buffer[uart->rx_buffer->rpos]; -} + return uart->rx_buffer->buffer[uart->rx_buffer->rpos]; + } -int -uart_read_char(uart_t* uart) -{ - uint8_t ret; - return uart_read(uart, (char*)&ret, 1) ? ret : -1; -} + int uart_read_char(uart_t* uart) + { + uint8_t ret; + return uart_read(uart, (char*)&ret, 1) ? ret : -1; + } -size_t -uart_read(uart_t* uart, char* userbuffer, size_t usersize) -{ - if(uart == NULL || !uart->rx_enabled) - return 0; - - if (!blocking_uart) - { - char c; - if (read(0, &c, 1) == 1) - uart_new_data(0, c); - } - - size_t ret = 0; - while (ret < usersize && uart_rx_available_unsafe(uart->rx_buffer)) - { - // pour sw buffer to user's buffer - // get largest linear length from sw buffer - size_t chunk = uart->rx_buffer->rpos < uart->rx_buffer->wpos ? - uart->rx_buffer->wpos - uart->rx_buffer->rpos : - uart->rx_buffer->size - uart->rx_buffer->rpos; - if (ret + chunk > usersize) - chunk = usersize - ret; - memcpy(userbuffer + ret, uart->rx_buffer->buffer + uart->rx_buffer->rpos, chunk); - uart->rx_buffer->rpos = (uart->rx_buffer->rpos + chunk) % uart->rx_buffer->size; - ret += chunk; - } - return ret; -} + size_t uart_read(uart_t* uart, char* userbuffer, size_t usersize) + { + if (uart == NULL || !uart->rx_enabled) + return 0; + + if (!blocking_uart) + { + char c; + if (read(0, &c, 1) == 1) + uart_new_data(0, c); + } + + size_t ret = 0; + while (ret < usersize && uart_rx_available_unsafe(uart->rx_buffer)) + { + // pour sw buffer to user's buffer + // get largest linear length from sw buffer + size_t chunk = uart->rx_buffer->rpos < uart->rx_buffer->wpos + ? uart->rx_buffer->wpos - uart->rx_buffer->rpos + : uart->rx_buffer->size - uart->rx_buffer->rpos; + if (ret + chunk > usersize) + chunk = usersize - ret; + memcpy(userbuffer + ret, uart->rx_buffer->buffer + uart->rx_buffer->rpos, chunk); + uart->rx_buffer->rpos = (uart->rx_buffer->rpos + chunk) % uart->rx_buffer->size; + ret += chunk; + } + return ret; + } -size_t -uart_resize_rx_buffer(uart_t* uart, size_t new_size) -{ - if(uart == NULL || !uart->rx_enabled) - return 0; - - if(uart->rx_buffer->size == new_size) - return uart->rx_buffer->size; - - uint8_t * new_buf = (uint8_t*)malloc(new_size); - if(!new_buf) - return uart->rx_buffer->size; - - size_t new_wpos = 0; - // if uart_rx_available_unsafe() returns non-0, uart_read_char_unsafe() can't return -1 - while(uart_rx_available_unsafe(uart->rx_buffer) && new_wpos < new_size) - new_buf[new_wpos++] = uart_read_char_unsafe(uart); - if (new_wpos == new_size) - new_wpos = 0; - - uint8_t * old_buf = uart->rx_buffer->buffer; - uart->rx_buffer->rpos = 0; - uart->rx_buffer->wpos = new_wpos; - uart->rx_buffer->size = new_size; - uart->rx_buffer->buffer = new_buf; - free(old_buf); - return uart->rx_buffer->size; -} + size_t uart_resize_rx_buffer(uart_t* uart, size_t new_size) + { + if (uart == NULL || !uart->rx_enabled) + return 0; + + if (uart->rx_buffer->size == new_size) + return uart->rx_buffer->size; + + uint8_t* new_buf = (uint8_t*)malloc(new_size); + if (!new_buf) + return uart->rx_buffer->size; + + size_t new_wpos = 0; + // if uart_rx_available_unsafe() returns non-0, uart_read_char_unsafe() can't return -1 + while (uart_rx_available_unsafe(uart->rx_buffer) && new_wpos < new_size) + new_buf[new_wpos++] = uart_read_char_unsafe(uart); + if (new_wpos == new_size) + new_wpos = 0; + + uint8_t* old_buf = uart->rx_buffer->buffer; + uart->rx_buffer->rpos = 0; + uart->rx_buffer->wpos = new_wpos; + uart->rx_buffer->size = new_size; + uart->rx_buffer->buffer = new_buf; + free(old_buf); + return uart->rx_buffer->size; + } -size_t -uart_get_rx_buffer_size(uart_t* uart) -{ - return uart && uart->rx_enabled ? uart->rx_buffer->size : 0; -} + size_t uart_get_rx_buffer_size(uart_t* uart) + { + return uart && uart->rx_enabled ? uart->rx_buffer->size : 0; + } -size_t -uart_write_char(uart_t* uart, char c) -{ - if(uart == NULL || !uart->tx_enabled) - return 0; + size_t uart_write_char(uart_t* uart, char c) + { + if (uart == NULL || !uart->tx_enabled) + return 0; - uart_do_write_char(uart->uart_nr, c); + uart_do_write_char(uart->uart_nr, c); - return 1; -} + return 1; + } -size_t -uart_write(uart_t* uart, const char* buf, size_t size) -{ - if(uart == NULL || !uart->tx_enabled) - return 0; + size_t uart_write(uart_t* uart, const char* buf, size_t size) + { + if (uart == NULL || !uart->tx_enabled) + return 0; - size_t ret = size; - const int uart_nr = uart->uart_nr; - while (size--) - uart_do_write_char(uart_nr, *buf++); + size_t ret = size; + const int uart_nr = uart->uart_nr; + while (size--) + uart_do_write_char(uart_nr, *buf++); - return ret; -} + return ret; + } -size_t -uart_tx_free(uart_t* uart) -{ - if(uart == NULL || !uart->tx_enabled) - return 0; + size_t uart_tx_free(uart_t* uart) + { + if (uart == NULL || !uart->tx_enabled) + return 0; - return UART_TX_FIFO_SIZE; -} + return UART_TX_FIFO_SIZE; + } -void -uart_wait_tx_empty(uart_t* uart) -{ - (void) uart; -} + void uart_wait_tx_empty(uart_t* uart) + { + (void)uart; + } -void -uart_flush(uart_t* uart) -{ - if(uart == NULL) - return; - - if(uart->rx_enabled) - { - uart->rx_buffer->rpos = 0; - uart->rx_buffer->wpos = 0; - } -} + void uart_flush(uart_t* uart) + { + if (uart == NULL) + return; + + if (uart->rx_enabled) + { + uart->rx_buffer->rpos = 0; + uart->rx_buffer->wpos = 0; + } + } -void -uart_set_baudrate(uart_t* uart, int baud_rate) -{ - if(uart == NULL) - return; + void uart_set_baudrate(uart_t* uart, int baud_rate) + { + if (uart == NULL) + return; - uart->baud_rate = baud_rate; -} + uart->baud_rate = baud_rate; + } -int -uart_get_baudrate(uart_t* uart) -{ - if(uart == NULL) - return 0; + int uart_get_baudrate(uart_t* uart) + { + if (uart == NULL) + return 0; - return uart->baud_rate; -} + return uart->baud_rate; + } -uint8_t -uart_get_bit_length(const int uart_nr) -{ - uint8_t width = ((uart_nr % 16) >> 2) + 5; - uint8_t parity = (uart_nr >> 5) + 1; - uint8_t stop = uart_nr % 4; - return (width + parity + stop + 1); -} + uint8_t uart_get_bit_length(const int uart_nr) + { + uint8_t width = ((uart_nr % 16) >> 2) + 5; + uint8_t parity = (uart_nr >> 5) + 1; + uint8_t stop = uart_nr % 4; + return (width + parity + stop + 1); + } -uart_t* -uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, size_t rx_size, bool invert) -{ - (void) config; - (void) tx_pin; - (void) invert; - uart_t* uart = (uart_t*) malloc(sizeof(uart_t)); - if(uart == NULL) - return NULL; - - uart->uart_nr = uart_nr; - uart->rx_overrun = false; - - switch(uart->uart_nr) - { - case UART0: - uart->rx_enabled = (mode != UART_TX_ONLY); - uart->tx_enabled = (mode != UART_RX_ONLY); - if(uart->rx_enabled) - { - struct uart_rx_buffer_ * rx_buffer = (struct uart_rx_buffer_ *)malloc(sizeof(struct uart_rx_buffer_)); - if(rx_buffer == NULL) - { - free(uart); - return NULL; - } - rx_buffer->size = rx_size;//var this - rx_buffer->rpos = 0; - rx_buffer->wpos = 0; - rx_buffer->buffer = (uint8_t *)malloc(rx_buffer->size); - if(rx_buffer->buffer == NULL) - { - free(rx_buffer); - free(uart); - return NULL; - } - uart->rx_buffer = rx_buffer; - } - break; - - case UART1: - // Note: uart_interrupt_handler does not support RX on UART 1. - uart->rx_enabled = false; - uart->tx_enabled = (mode != UART_RX_ONLY); - break; - - case UART_NO: - default: - // big fail! - free(uart); - return NULL; - } - - uart_set_baudrate(uart, baudrate); - - UART[uart_nr] = uart; - - return uart; -} + uart_t* uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, size_t rx_size, + bool invert) + { + (void)config; + (void)tx_pin; + (void)invert; + uart_t* uart = (uart_t*)malloc(sizeof(uart_t)); + if (uart == NULL) + return NULL; + + uart->uart_nr = uart_nr; + uart->rx_overrun = false; + + switch (uart->uart_nr) + { + case UART0: + uart->rx_enabled = (mode != UART_TX_ONLY); + uart->tx_enabled = (mode != UART_RX_ONLY); + if (uart->rx_enabled) + { + struct uart_rx_buffer_* rx_buffer + = (struct uart_rx_buffer_*)malloc(sizeof(struct uart_rx_buffer_)); + if (rx_buffer == NULL) + { + free(uart); + return NULL; + } + rx_buffer->size = rx_size; // var this + rx_buffer->rpos = 0; + rx_buffer->wpos = 0; + rx_buffer->buffer = (uint8_t*)malloc(rx_buffer->size); + if (rx_buffer->buffer == NULL) + { + free(rx_buffer); + free(uart); + return NULL; + } + uart->rx_buffer = rx_buffer; + } + break; + + case UART1: + // Note: uart_interrupt_handler does not support RX on UART 1. + uart->rx_enabled = false; + uart->tx_enabled = (mode != UART_RX_ONLY); + break; + + case UART_NO: + default: + // big fail! + free(uart); + return NULL; + } + + uart_set_baudrate(uart, baudrate); + + UART[uart_nr] = uart; + + return uart; + } -void -uart_uninit(uart_t* uart) -{ - if(uart == NULL) - return; - - if(uart->rx_enabled) { - free(uart->rx_buffer->buffer); - free(uart->rx_buffer); - } - free(uart); -} + void uart_uninit(uart_t* uart) + { + if (uart == NULL) + return; + + if (uart->rx_enabled) + { + free(uart->rx_buffer->buffer); + free(uart->rx_buffer); + } + free(uart); + } -void -uart_swap(uart_t* uart, int tx_pin) -{ - (void) uart; - (void) tx_pin; -} + bool uart_swap(uart_t* uart, int tx_pin) + { + (void)uart; + (void)tx_pin; + return true; + } -void -uart_set_tx(uart_t* uart, int tx_pin) -{ - (void) uart; - (void) tx_pin; -} + bool uart_set_tx(uart_t* uart, int tx_pin) + { + (void)uart; + (void)tx_pin; + return true; + } -void -uart_set_pins(uart_t* uart, int tx, int rx) -{ - (void) uart; - (void) tx; - (void) rx; -} + bool uart_set_pins(uart_t* uart, int tx, int rx) + { + (void)uart; + (void)tx; + (void)rx; + return true; + } -bool -uart_tx_enabled(uart_t* uart) -{ - if(uart == NULL) - return false; + bool uart_tx_enabled(uart_t* uart) + { + if (uart == NULL) + return false; - return uart->tx_enabled; -} + return uart->tx_enabled; + } -bool -uart_rx_enabled(uart_t* uart) -{ - if(uart == NULL) - return false; + bool uart_rx_enabled(uart_t* uart) + { + if (uart == NULL) + return false; - return uart->rx_enabled; -} + return uart->rx_enabled; + } -bool -uart_has_overrun(uart_t* uart) -{ - if(uart == NULL || !uart->rx_overrun) - return false; + bool uart_has_overrun(uart_t* uart) + { + if (uart == NULL || !uart->rx_overrun) + return false; - // clear flag - uart->rx_overrun = false; - return true; -} + // clear flag + uart->rx_overrun = false; + return true; + } -bool -uart_has_rx_error(uart_t* uart) -{ - (void) uart; - return false; -} + bool uart_has_rx_error(uart_t* uart) + { + (void)uart; + return false; + } -void -uart_set_debug(int uart_nr) -{ - (void)uart_nr; -} + void uart_set_debug(int uart_nr) + { + (void)uart_nr; + } + + int uart_get_debug() + { + return s_uart_debug_nr; + } + + void uart_start_detect_baudrate(int uart_nr) + { + (void)uart_nr; + } -int -uart_get_debug() + int uart_detect_baudrate(int uart_nr) + { + (void)uart_nr; + return 115200; + } +}; + +size_t uart_peek_available(uart_t* uart) { - return s_uart_debug_nr; + return 0; } - -void -uart_start_detect_baudrate(int uart_nr) +const char* uart_peek_buffer(uart_t* uart) { - (void) uart_nr; + return nullptr; } - -int -uart_detect_baudrate(int uart_nr) +void uart_peek_consume(uart_t* uart, size_t consume) { - (void) uart_nr; - return 115200; + (void)uart; + (void)consume; } - -}; diff --git a/tests/host/common/MockWiFiServer.cpp b/tests/host/common/MockWiFiServer.cpp index 132ca5e88a..8fb2b91031 100644 --- a/tests/host/common/MockWiFiServer.cpp +++ b/tests/host/common/MockWiFiServer.cpp @@ -44,27 +44,43 @@ extern "C" const ip_addr_t ip_addr_any = IPADDR4_INIT(IPADDR_ANY); // lwIP API side of WiFiServer -WiFiServer::WiFiServer (const IPAddress& addr, uint16_t port) +WiFiServer::WiFiServer(const IPAddress& addr, uint16_t port) { - (void)addr; - _port = port; + (void)addr; + _port = port; } -WiFiServer::WiFiServer (uint16_t port) +WiFiServer::WiFiServer(uint16_t port) { - _port = port; + _port = port; } -WiFiClient WiFiServer::available (uint8_t* status) +WiFiClient WiFiServer::available(uint8_t* status) { - (void)status; - if (hasClient()) - return WiFiClient(new ClientContext(serverAccept(pcb2int(_listen_pcb)))); - return WiFiClient(); + (void)status; + return accept(); +} + +void WiFiServer::_mockUnclaimed() +{ + if (hasClient()) + _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(); } // static declaration #include uint32_t UdpContext::staticMCastAddr = 0; - diff --git a/tests/host/common/MockWiFiServerSocket.cpp b/tests/host/common/MockWiFiServerSocket.cpp index 13569b96a2..0023c6e2a1 100644 --- a/tests/host/common/MockWiFiServerSocket.cpp +++ b/tests/host/common/MockWiFiServerSocket.cpp @@ -44,26 +44,26 @@ // host socket internal side of WiFiServer -int serverAccept (int srvsock) +int serverAccept(int srvsock) { - int clisock; - socklen_t n; - struct sockaddr_in client; - n = sizeof(client); - if ((clisock = accept(srvsock, (struct sockaddr*)&client, &n)) == -1) - { - perror(MOCK "accept()"); - exit(EXIT_FAILURE); - } - return mockSockSetup(clisock); + int clisock; + socklen_t n; + struct sockaddr_in client; + n = sizeof(client); + if ((clisock = accept(srvsock, (struct sockaddr*)&client, &n)) == -1) + { + perror(MOCK "accept()"); + exit(EXIT_FAILURE); + } + return mockSockSetup(clisock); } -void WiFiServer::begin (uint16_t port) +void WiFiServer::begin(uint16_t port) { return begin(port, !0); } -void WiFiServer::begin (uint16_t port, uint8_t backlog) +void WiFiServer::begin(uint16_t port, uint8_t backlog) { if (!backlog) return; @@ -71,82 +71,91 @@ void WiFiServer::begin (uint16_t port, uint8_t backlog) return begin(); } -void WiFiServer::begin () +void WiFiServer::begin() { - int sock; - int mockport; - struct sockaddr_in server; - - mockport = _port; - if (mockport < 1024 && mock_port_shifter) - { - mockport += mock_port_shifter; - fprintf(stderr, MOCK "=====> WiFiServer port: %d shifted to %d (use option -s) <=====\n", _port, mockport); - } - else - fprintf(stderr, MOCK "=====> WiFiServer port: %d <=====\n", mockport); - - if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) - { - perror(MOCK "socket()"); - exit(EXIT_FAILURE); - } - - int optval = 1; - if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)) == -1) - { - perror(MOCK "reuseport"); - exit(EXIT_FAILURE); - } - - server.sin_family = AF_INET; - server.sin_port = htons(mockport); - server.sin_addr.s_addr = htonl(global_source_address); - if (bind(sock, (struct sockaddr*)&server, sizeof(server)) == -1) - { - perror(MOCK "bind()"); - exit(EXIT_FAILURE); - } - - if (listen(sock, 1) == -1) - { - perror(MOCK "listen()"); - exit(EXIT_FAILURE); - } - - - // store int into pointer - _listen_pcb = int2pcb(sock); + int sock; + int mockport; + struct sockaddr_in server; + + mockport = _port; + if (mockport < 1024 && mock_port_shifter) + { + mockport += mock_port_shifter; + fprintf(stderr, MOCK "=====> WiFiServer port: %d shifted to %d (use option -s) <=====\n", + _port, mockport); + } + else + fprintf(stderr, MOCK "=====> WiFiServer port: %d <=====\n", mockport); + + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) + { + perror(MOCK "socket()"); + exit(EXIT_FAILURE); + } + + int optval = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)) == -1) + { + perror(MOCK "reuseport"); + exit(EXIT_FAILURE); + } + + server.sin_family = AF_INET; + server.sin_port = htons(mockport); + server.sin_addr.s_addr = htonl(global_source_address); + if (bind(sock, (struct sockaddr*)&server, sizeof(server)) == -1) + { + perror(MOCK "bind()"); + exit(EXIT_FAILURE); + } + + if (listen(sock, 1) == -1) + { + perror(MOCK "listen()"); + exit(EXIT_FAILURE); + } + + // store int into pointer + _listen_pcb = int2pcb(sock); } -bool WiFiServer::hasClient () +bool WiFiServer::hasClient() { - struct pollfd p; - p.fd = pcb2int(_listen_pcb); - p.events = POLLIN; - return poll(&p, 1, 0) && p.revents == POLLIN; + struct pollfd p; + p.fd = pcb2int(_listen_pcb); + p.events = POLLIN; + return poll(&p, 1, 0) && p.revents == POLLIN; } -size_t WiFiServer::write (uint8_t c) +void WiFiServer::close() { - return write(&c, 1); + if (pcb2int(_listen_pcb) >= 3) // 0=stdin 1=stdout 2=stderr + ::close(pcb2int(_listen_pcb)); + _listen_pcb = int2pcb(-1); } -size_t WiFiServer::write (const uint8_t *buf, size_t size) +void WiFiServer::stop() { - fprintf(stderr, MOCK "todo: WiFiServer::write(%p, %zd)\n", buf, size); - exit(EXIT_FAILURE); - return 0; + close(); } -void WiFiServer::close () +size_t WiFiServer::hasClientData() { - if (pcb2int(_listen_pcb) >= 0) - ::close(pcb2int(_listen_pcb)); - _listen_pcb = int2pcb(-1); + // Trivial Mocking: + // There is no waiting list of clients in this trivial mocking code, + // so the code has to act as if the tcp backlog list is full, + // and nothing is known about potential further clients. + // It could be implemented by accepting new clients and store their data until the current one + // is closed. + return 0; } -void WiFiServer::stop () +bool WiFiServer::hasMaxPendingClients() { - close(); + // Mocking code does not consider the waiting client list, + // so it will return ::hasClient() here meaning: + // - our waiting client list does not exist + // - we consider pending number is max if a new client is waiting + // or not max if there's no new client. + return hasClient(); } diff --git a/tests/host/common/MocklwIP.cpp b/tests/host/common/MocklwIP.cpp index 1e19f20fc7..8df8929d98 100644 --- a/tests/host/common/MocklwIP.cpp +++ b/tests/host/common/MocklwIP.cpp @@ -3,62 +3,74 @@ #include "MocklwIP.h" +#include + esp8266::AddressListImplementation::AddressList addrList; extern "C" { - -extern netif netif0; - -netif* netif_list = &netif0; - -err_t dhcp_renew(struct netif *netif) -{ - (void)netif; - return ERR_OK; -} - -void sntp_setserver(u8_t, const ip_addr_t) -{ -} - -const ip_addr_t* sntp_getserver(u8_t) -{ - return IP_ADDR_ANY; -} - -err_t etharp_request(struct netif *netif, const ip4_addr_t *ipaddr) -{ - (void)netif; - (void)ipaddr; - return ERR_OK; -} - -err_t igmp_start(struct netif* netif) -{ - (void)netif; - return ERR_OK; -} - -err_t igmp_joingroup_netif(struct netif *netif, const ip4_addr_t *groupaddr) -{ - (void)netif; - (void)groupaddr; - return ERR_OK; -} - -err_t igmp_leavegroup_netif(struct netif *netif, const ip4_addr_t *groupaddr) -{ - (void)netif; - (void)groupaddr; - return ERR_OK; -} - -struct netif* netif_get_by_index(u8_t idx) -{ - (void)idx; - return &netif0; -} - - -} // extern "C" + extern netif netif0; + + netif* netif_list = &netif0; + + err_t dhcp_renew(struct netif* netif) + { + (void)netif; + return ERR_OK; + } + + void sntp_setserver(u8_t, const ip_addr_t) { } + + const ip_addr_t* sntp_getserver(u8_t) + { + return IP_ADDR_ANY; + } + + err_t etharp_request(struct netif* netif, const ip4_addr_t* ipaddr) + { + (void)netif; + (void)ipaddr; + return ERR_OK; + } + + err_t igmp_start(struct netif* netif) + { + (void)netif; + return ERR_OK; + } + + err_t igmp_joingroup_netif(struct netif* netif, const ip4_addr_t* groupaddr) + { + (void)netif; + (void)groupaddr; + return ERR_OK; + } + + err_t igmp_leavegroup_netif(struct netif* netif, const ip4_addr_t* groupaddr) + { + (void)netif; + (void)groupaddr; + return ERR_OK; + } + + struct netif* netif_get_by_index(u8_t idx) + { + (void)idx; + 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/MocklwIP.h b/tests/host/common/MocklwIP.h index e7fead4cb7..9f0b4718f5 100644 --- a/tests/host/common/MocklwIP.h +++ b/tests/host/common/MocklwIP.h @@ -4,12 +4,11 @@ extern "C" { - #include #include -extern netif netif0; + extern netif netif0; -} // extern "C" +} // extern "C" -#endif // __MOCKLWIP_H +#endif // __MOCKLWIP_H diff --git a/tests/host/common/UdpContextSocket.cpp b/tests/host/common/UdpContextSocket.cpp index 4212bb0ee4..2ffa4eb22f 100644 --- a/tests/host/common/UdpContextSocket.cpp +++ b/tests/host/common/UdpContextSocket.cpp @@ -39,176 +39,174 @@ #include #include -int mockUDPSocket () +int mockUDPSocket() { - int s; - if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1 || fcntl(s, F_SETFL, O_NONBLOCK) == -1) - { - fprintf(stderr, MOCK "UDP socket: %s", strerror(errno)); - exit(EXIT_FAILURE); - } - return s; + int s; + if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1 || fcntl(s, F_SETFL, O_NONBLOCK) == -1) + { + fprintf(stderr, MOCK "UDP socket: %s", strerror(errno)); + exit(EXIT_FAILURE); + } + return s; } -bool mockUDPListen (int sock, uint32_t dstaddr, uint16_t port, uint32_t mcast) +bool mockUDPListen(int sock, uint32_t dstaddr, uint16_t port, uint32_t mcast) { - int optval; - int mockport; - - mockport = port; - if (mockport < 1024 && mock_port_shifter) - { - mockport += mock_port_shifter; - fprintf(stderr, MOCK "=====> UdpServer port: %d shifted to %d (use option -s) <=====\n", port, mockport); - } - else - fprintf(stderr, MOCK "=====> UdpServer port: %d <=====\n", mockport); - - optval = 1; - if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)) == -1) - fprintf(stderr, MOCK "SO_REUSEPORT failed\n"); - optval = 1; - if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1) - fprintf(stderr, MOCK "SO_REUSEADDR failed\n"); - - struct sockaddr_in servaddr; - memset(&servaddr, 0, sizeof(servaddr)); - - // Filling server information - servaddr.sin_family = AF_INET; - (void) dstaddr; - //servaddr.sin_addr.s_addr = htonl(global_source_address); - servaddr.sin_addr.s_addr = htonl(INADDR_ANY); - servaddr.sin_port = htons(mockport); - - // Bind the socket with the server address - if (bind(sock, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) - { - fprintf(stderr, MOCK "UDP bind on port %d failed: %s\n", mockport, strerror(errno)); - return false; - } - else - mockverbose("UDP server on port %d (sock=%d)\n", mockport, sock); - - if (!mcast) - mcast = inet_addr("224.0.0.1"); // all hosts group - if (mcast) - { - // https://web.cs.wpi.edu/~claypool/courses/4514-B99/samples/multicast.c - // https://stackoverflow.com/questions/12681097/c-choose-interface-for-udp-multicast-socket - - struct ip_mreq mreq; - mreq.imr_multiaddr.s_addr = mcast; - //mreq.imr_interface.s_addr = htonl(global_source_address); - mreq.imr_interface.s_addr = htonl(INADDR_ANY); - - if (host_interface) - { + int optval; + int mockport; + + mockport = port; + if (mockport < 1024 && mock_port_shifter) + { + mockport += mock_port_shifter; + fprintf(stderr, MOCK "=====> UdpServer port: %d shifted to %d (use option -s) <=====\n", + port, mockport); + } + else + fprintf(stderr, MOCK "=====> UdpServer port: %d <=====\n", mockport); + + optval = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)) == -1) + fprintf(stderr, MOCK "SO_REUSEPORT failed\n"); + optval = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1) + fprintf(stderr, MOCK "SO_REUSEADDR failed\n"); + + struct sockaddr_in servaddr; + memset(&servaddr, 0, sizeof(servaddr)); + + // Filling server information + servaddr.sin_family = AF_INET; + (void)dstaddr; + // servaddr.sin_addr.s_addr = htonl(global_source_address); + servaddr.sin_addr.s_addr = htonl(INADDR_ANY); + servaddr.sin_port = htons(mockport); + + // Bind the socket with the server address + if (bind(sock, (const struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) + { + fprintf(stderr, MOCK "UDP bind on port %d failed: %s\n", mockport, strerror(errno)); + return false; + } + else + mockverbose("UDP server on port %d (sock=%d)\n", mockport, sock); + + if (!mcast) + mcast = inet_addr("224.0.0.1"); // all hosts group + if (mcast) + { + // https://web.cs.wpi.edu/~claypool/courses/4514-B99/samples/multicast.c + // https://stackoverflow.com/questions/12681097/c-choose-interface-for-udp-multicast-socket + + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = mcast; + // mreq.imr_interface.s_addr = htonl(global_source_address); + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + + if (host_interface) + { #if __APPLE__ - int idx = if_nametoindex(host_interface); - if (setsockopt(sock, IPPROTO_TCP, IP_BOUND_IF, &idx, sizeof(idx)) == -1) + int idx = if_nametoindex(host_interface); + if (setsockopt(sock, IPPROTO_TCP, IP_BOUND_IF, &idx, sizeof(idx)) == -1) #else - if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, host_interface, strlen(host_interface)) == -1) + if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, host_interface, + strlen(host_interface)) + == -1) #endif - fprintf(stderr, MOCK "UDP multicast: can't setup bind/output on interface %s: %s\n", host_interface, strerror(errno)); - if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &mreq.imr_interface, sizeof(struct in_addr)) == -1) - fprintf(stderr, MOCK "UDP multicast: can't setup bind/input on interface %s: %s\n", host_interface, strerror(errno)); - } - - if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) - { - fprintf(stderr, MOCK "can't join multicast group addr %08x\n", (int)mcast); - return false; - } - else - mockverbose("joined multicast group addr %08lx\n", (long)ntohl(mcast)); - } - - return true; + fprintf(stderr, MOCK "UDP multicast: can't setup bind/output on interface %s: %s\n", + host_interface, strerror(errno)); + if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &mreq.imr_interface, + sizeof(struct in_addr)) + == -1) + fprintf(stderr, MOCK "UDP multicast: can't setup bind/input on interface %s: %s\n", + host_interface, strerror(errno)); + } + + if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) + { + fprintf(stderr, MOCK "can't join multicast group addr %08x\n", (int)mcast); + return false; + } + else + mockverbose("joined multicast group addr %08lx\n", (long)ntohl(mcast)); + } + + return true; } - -size_t mockUDPFillInBuf (int sock, char* ccinbuf, size_t& ccinbufsize, uint8_t& addrsize, uint8_t addr[16], uint16_t& port) -{ - struct sockaddr_storage addrbuf; - socklen_t addrbufsize = std::min((socklen_t)sizeof(addrbuf), (socklen_t)16); - - size_t maxread = CCBUFSIZE - ccinbufsize; - ssize_t ret = ::recvfrom(sock, ccinbuf + ccinbufsize, maxread, 0/*flags*/, (sockaddr*)&addrbuf, &addrbufsize); - if (ret == -1) - { - if (errno != EAGAIN) - fprintf(stderr, MOCK "UDPContext::(read/peek): filling buffer for %zd bytes: %s\n", maxread, strerror(errno)); - ret = 0; - } - - if (ret > 0) - { - port = ntohs(((sockaddr_in*)&addrbuf)->sin_port); - if (addrbuf.ss_family == AF_INET) - memcpy(&addr[0], &(((sockaddr_in*)&addrbuf)->sin_addr.s_addr), addrsize = 4); - else - { - fprintf(stderr, MOCK "TODO UDP+IPv6\n"); - exit(EXIT_FAILURE); - } - } - - return ccinbufsize += ret; -} - -size_t mockUDPPeekBytes (int sock, char* dst, size_t usersize, int timeout_ms, char* ccinbuf, size_t& ccinbufsize) -{ - (void) sock; - (void) timeout_ms; - if (usersize > CCBUFSIZE) - fprintf(stderr, MOCK "CCBUFSIZE(%d) should be increased by %zd bytes (-> %zd)\n", CCBUFSIZE, usersize - CCBUFSIZE, usersize); - - size_t retsize = 0; - if (ccinbufsize) - { - // data already buffered - retsize = usersize; - if (retsize > ccinbufsize) - retsize = ccinbufsize; - } - memcpy(dst, ccinbuf, retsize); - return retsize; -} - -void mockUDPSwallow (size_t copied, char* ccinbuf, size_t& ccinbufsize) +size_t mockUDPFillInBuf(int sock, char* ccinbuf, size_t& ccinbufsize, uint8_t& addrsize, + uint8_t addr[16], uint16_t& port) { - // poor man buffer - memmove(ccinbuf, ccinbuf + copied, ccinbufsize - copied); - ccinbufsize -= copied; + struct sockaddr_storage addrbuf; + socklen_t addrbufsize = std::min((socklen_t)sizeof(addrbuf), (socklen_t)16); + + size_t maxread = CCBUFSIZE - ccinbufsize; + ssize_t ret = ::recvfrom(sock, ccinbuf + ccinbufsize, maxread, 0 /*flags*/, (sockaddr*)&addrbuf, + &addrbufsize); + if (ret == -1) + { + if (errno != EAGAIN) + fprintf(stderr, MOCK "UDPContext::(read/peek): filling buffer for %zd bytes: %s\n", + maxread, strerror(errno)); + ret = 0; + } + + if (ret > 0) + { + port = ntohs(((sockaddr_in*)&addrbuf)->sin_port); + if (addrbuf.ss_family == AF_INET) + memcpy(&addr[0], &(((sockaddr_in*)&addrbuf)->sin_addr.s_addr), addrsize = 4); + else + { + fprintf(stderr, MOCK "TODO UDP+IPv6\n"); + exit(EXIT_FAILURE); + } + } + + return ccinbufsize += ret; } -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 copied = mockUDPPeekBytes(sock, dst, size, timeout_ms, ccinbuf, ccinbufsize); - mockUDPSwallow(copied, ccinbuf, ccinbufsize); - return copied; + (void)sock; + (void)timeout_ms; + if (offset + usersize > CCBUFSIZE) + fprintf(stderr, MOCK "CCBUFSIZE(%d) should be increased by %zd bytes (-> %zd)\n", CCBUFSIZE, + offset + usersize - CCBUFSIZE, offset + usersize); + + size_t retsize = 0; + if (ccinbufsize) + { + // data already buffered + retsize = usersize; + if (retsize > ccinbufsize) + retsize = ccinbufsize; + } + memcpy(dst, ccinbuf + offset, retsize); + return retsize; } -size_t mockUDPWrite (int sock, const uint8_t* data, size_t size, int timeout_ms, uint32_t ipv4, uint16_t port) +size_t mockUDPWrite(int sock, const uint8_t* data, size_t size, int timeout_ms, uint32_t ipv4, + uint16_t port) { - (void) timeout_ms; - // Filling server information - struct sockaddr_in peer; - peer.sin_family = AF_INET; - peer.sin_addr.s_addr = ipv4; //XXFIXME should use lwip_htonl? - peer.sin_port = htons(port); - int ret = ::sendto(sock, data, size, 0/*flags*/, (const sockaddr*)&peer, sizeof(peer)); - if (ret == -1) - { - fprintf(stderr, MOCK "UDPContext::write: write(%d): %s\n", sock, strerror(errno)); - return 0; - } - if (ret != (int)size) - { - fprintf(stderr, MOCK "UDPContext::write: short write (%d < %zd) (TODO)\n", ret, size); - exit(EXIT_FAILURE); - } - - return ret; + (void)timeout_ms; + // Filling server information + struct sockaddr_in peer; + peer.sin_family = AF_INET; + peer.sin_addr.s_addr = ipv4; // XXFIXME should use lwip_htonl? + peer.sin_port = htons(port); + int ret = ::sendto(sock, data, size, 0 /*flags*/, (const sockaddr*)&peer, sizeof(peer)); + if (ret == -1) + { + fprintf(stderr, MOCK "UDPContext::write: write(%d): %s\n", sock, strerror(errno)); + return 0; + } + if (ret != (int)size) + { + fprintf(stderr, MOCK "UDPContext::write: short write (%d < %zd) (TODO)\n", ret, size); + exit(EXIT_FAILURE); + } + + return ret; } diff --git a/tests/host/common/WMath.cpp b/tests/host/common/WMath.cpp index 4fdf07fa2e..bc0016658b 100644 --- a/tests/host/common/WMath.cpp +++ b/tests/host/common/WMath.cpp @@ -4,59 +4,69 @@ Part of the Wiring project - http://wiring.org.co Copyright (c) 2004-06 Hernando Barragan Modified 13 August 2006, David A. Mellis for Arduino - http://www.arduino.cc/ - + 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$ */ -extern "C" { +extern "C" +{ #include #include } -void randomSeed(unsigned long seed) { - if(seed != 0) { +void randomSeed(unsigned long seed) +{ + if (seed != 0) + { srand(seed); } } -long random(long howbig) { - if(howbig == 0) { +long random(long howbig) +{ + if (howbig == 0) + { return 0; } return (rand()) % howbig; } -long random(long howsmall, long howbig) { - if(howsmall >= howbig) { +long random(long howsmall, long howbig) +{ + if (howsmall >= howbig) + { return howsmall; } long diff = howbig - howsmall; return random(diff) + howsmall; } -long map(long x, long in_min, long in_max, long out_min, long out_max) { +long map(long x, long in_min, long in_max, long out_min, long out_max) +{ return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } -uint16_t makeWord(unsigned int w) { +uint16_t makeWord(unsigned int w) +{ return w; } -uint16_t makeWord(unsigned char h, unsigned char l) { +uint16_t makeWord(unsigned char h, unsigned char l) +{ return (h << 8) | l; } diff --git a/tests/host/common/c_types.h b/tests/host/common/c_types.h index 867f2f7726..9ba8968aeb 100644 --- a/tests/host/common/c_types.h +++ b/tests/host/common/c_types.h @@ -36,45 +36,46 @@ #include #include -typedef signed char sint8_t; -typedef signed short sint16_t; -typedef signed long sint32_t; -typedef signed long long sint64_t; +typedef signed char sint8_t; +typedef signed short sint16_t; +typedef signed long sint32_t; +typedef signed long long sint64_t; // CONFLICT typedef unsigned long long u_int64_t; -typedef float real32_t; -typedef double real64_t; +typedef float real32_t; +typedef double real64_t; // CONFLICT typedef unsigned char uint8; -typedef unsigned char u8; -typedef signed char sint8; -typedef signed char int8; -typedef signed char s8; -typedef unsigned short uint16; -typedef unsigned short u16; -typedef signed short sint16; -typedef signed short s16; +typedef unsigned char u8; +typedef signed char sint8; +typedef signed char int8; +typedef signed char s8; +typedef unsigned short uint16; +typedef unsigned short u16; +typedef signed short sint16; +typedef signed short s16; // CONFLICT typedef unsigned int uint32; -typedef unsigned int u_int; -typedef unsigned int u32; -typedef signed int sint32; -typedef signed int s32; -typedef int int32; -typedef signed long long sint64; -typedef unsigned long long uint64; -typedef unsigned long long u64; -typedef float real32; -typedef double real64; +typedef unsigned int u_int; +typedef unsigned int u32; +typedef signed int sint32; +typedef signed int s32; +typedef int int32; +typedef signed long long sint64; +typedef unsigned long long uint64; +typedef unsigned long long u64; +typedef float real32; +typedef double real64; -#define __le16 u16 +#define __le16 u16 -#define LOCAL static +#define LOCAL static #ifndef NULL -#define NULL (void *)0 +#define NULL (void*)0 #endif /* NULL */ /* probably should not put STATUS here */ -typedef enum { +typedef enum +{ OK = 0, FAIL, PENDING, @@ -82,10 +83,10 @@ typedef enum { CANCEL, } STATUS; -#define BIT(nr) (1UL << (nr)) +#define BIT(nr) (1UL << (nr)) -#define REG_SET_BIT(_r, _b) (*(volatile uint32_t*)(_r) |= (_b)) -#define REG_CLR_BIT(_r, _b) (*(volatile uint32_t*)(_r) &= ~(_b)) +#define REG_SET_BIT(_r, _b) (*(volatile uint32_t*)(_r) |= (_b)) +#define REG_CLR_BIT(_r, _b) (*(volatile uint32_t*)(_r) &= ~(_b)) #define DMEM_ATTR __attribute__((section(".bss"))) #define SHMEM_ATTR @@ -93,25 +94,30 @@ typedef enum { #ifdef ICACHE_FLASH #define __ICACHE_STRINGIZE_NX(A) #A #define __ICACHE_STRINGIZE(A) __ICACHE_STRINGIZE_NX(A) -#define ICACHE_FLASH_ATTR __attribute__((section("\".irom0.text." __FILE__ "." __ICACHE_STRINGIZE(__LINE__) "." __ICACHE_STRINGIZE(__COUNTER__) "\""))) -#define ICACHE_RAM_ATTR __attribute__((section("\".iram.text." __FILE__ "." __ICACHE_STRINGIZE(__LINE__) "." __ICACHE_STRINGIZE(__COUNTER__) "\""))) -#define ICACHE_RODATA_ATTR __attribute__((section("\".irom.text." __FILE__ "." __ICACHE_STRINGIZE(__LINE__) "." __ICACHE_STRINGIZE(__COUNTER__) "\""))) +#define ICACHE_FLASH_ATTR \ + __attribute__((section("\".irom0.text." __FILE__ "." __ICACHE_STRINGIZE( \ + __LINE__) "." __ICACHE_STRINGIZE(__COUNTER__) "\""))) +#define IRAM_ATTR \ + __attribute__((section("\".iram.text." __FILE__ "." __ICACHE_STRINGIZE( \ + __LINE__) "." __ICACHE_STRINGIZE(__COUNTER__) "\""))) +#define ICACHE_RODATA_ATTR \ + __attribute__((section("\".irom.text." __FILE__ "." __ICACHE_STRINGIZE( \ + __LINE__) "." __ICACHE_STRINGIZE(__COUNTER__) "\""))) #else #define ICACHE_FLASH_ATTR -#define ICACHE_RAM_ATTR +#define IRAM_ATTR #define ICACHE_RODATA_ATTR #endif /* ICACHE_FLASH */ // counterpart https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp8266-compat.h -#define IRAM_ATTR ICACHE_RAM_ATTR +#define ICACHE_RAM_ATTR IRAM_ATTR #define STORE_ATTR __attribute__((aligned(4))) #ifndef __cplusplus -#define BOOL bool -#define TRUE true -#define FALSE false - +#define BOOL bool +#define TRUE true +#define FALSE false #endif /* !__cplusplus */ diff --git a/tests/host/common/catch.hpp b/tests/host/common/catch.hpp index 391c7ab4af..bc67674562 100644 --- a/tests/host/common/catch.hpp +++ b/tests/host/common/catch.hpp @@ -4225,7 +4225,7 @@ namespace Catch { ss << seed; ss >> config.rngSeed; if( ss.fail() ) - throw std::runtime_error( "Argment to --rng-seed should be the word 'time' or a number" ); + throw std::runtime_error( "Argument to --rng-seed should be the word 'time' or a number" ); } } inline void setVerbosity( ConfigData& config, int level ) { @@ -6649,7 +6649,7 @@ namespace Catch { Colour colourGuard( Colour::Red ); Catch::cerr() << "Tag name [" << tag << "] not allowed.\n" - << "Tag names starting with non alpha-numeric characters are reserved\n"; + << "Tag names starting with non alphanumeric characters are reserved\n"; } { Colour colourGuard( Colour::FileName ); diff --git a/tests/host/common/esp8266_peri.h b/tests/host/common/esp8266_peri.h index 4a1a3cd031..4dcd6cb9cd 100644 --- a/tests/host/common/esp8266_peri.h +++ b/tests/host/common/esp8266_peri.h @@ -2,8 +2,8 @@ #ifndef FAKE_ESP8266_PERI_H #define FAKE_ESP8266_PERI_H -const int GPI = 0; -const int GPO = 0; +const int GPI = 0; +const int GPO = 0; const int GP16I = 0; #endif diff --git a/tests/host/common/flash_hal.h b/tests/host/common/flash_hal.h new file mode 100644 index 0000000000..dddaa65523 --- /dev/null +++ b/tests/host/common/flash_hal.h @@ -0,0 +1,25 @@ +#ifndef flash_hal_mock_h +#define flash_hal_mock_h + +#include +#include <../../cores/esp8266/flash_hal.h> + +#undef FS_start +#undef FS_end +#define FS_start 0 +#define FS_end 0 + +extern "C" +{ + extern uint32_t s_phys_addr; + extern uint32_t s_phys_size; + extern uint32_t s_phys_page; + extern uint32_t s_phys_block; + extern uint8_t* s_phys_data; + + extern int32_t flash_hal_read(uint32_t addr, uint32_t size, uint8_t* dst); + extern int32_t flash_hal_write(uint32_t addr, uint32_t size, const uint8_t* src); + extern int32_t flash_hal_erase(uint32_t addr, uint32_t size); +} + +#endif diff --git a/tests/host/common/flash_hal_mock.cpp b/tests/host/common/flash_hal_mock.cpp index 5304d7553a..60b2620f1b 100644 --- a/tests/host/common/flash_hal_mock.cpp +++ b/tests/host/common/flash_hal_mock.cpp @@ -3,33 +3,39 @@ #include #include +#include "flash_hal.h" + extern "C" { - uint32_t s_phys_addr = 0; - uint32_t s_phys_size = 0; - uint32_t s_phys_page = 0; + uint32_t s_phys_addr = 0; + uint32_t s_phys_size = 0; + uint32_t s_phys_page = 0; uint32_t s_phys_block = 0; - uint8_t* s_phys_data = nullptr; + uint8_t* s_phys_data = nullptr; } -int32_t flash_hal_read(uint32_t addr, uint32_t size, uint8_t *dst) { +int32_t flash_hal_read(uint32_t addr, uint32_t size, uint8_t* dst) +{ memcpy(dst, s_phys_data + addr, size); return 0; } -int32_t flash_hal_write(uint32_t addr, uint32_t size, const uint8_t *src) { +int32_t flash_hal_write(uint32_t addr, uint32_t size, const uint8_t* src) +{ memcpy(s_phys_data + addr, src, size); return 0; } -int32_t flash_hal_erase(uint32_t addr, uint32_t size) { - if ((size & (FLASH_SECTOR_SIZE - 1)) != 0 || - (addr & (FLASH_SECTOR_SIZE - 1)) != 0) { +int32_t flash_hal_erase(uint32_t addr, uint32_t size) +{ + if ((size & (FLASH_SECTOR_SIZE - 1)) != 0 || (addr & (FLASH_SECTOR_SIZE - 1)) != 0) + { abort(); } - const uint32_t sector = addr / FLASH_SECTOR_SIZE; + const uint32_t sector = addr / FLASH_SECTOR_SIZE; const uint32_t sectorCount = size / FLASH_SECTOR_SIZE; - for (uint32_t i = 0; i < sectorCount; ++i) { + for (uint32_t i = 0; i < sectorCount; ++i) + { memset(s_phys_data + (sector + i) * FLASH_SECTOR_SIZE, 0xff, FLASH_SECTOR_SIZE); } return 0; diff --git a/tests/host/common/flash_hal_mock.h b/tests/host/common/flash_hal_mock.h deleted file mode 100644 index af5035eaa5..0000000000 --- a/tests/host/common/flash_hal_mock.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef flash_hal_mock_h -#define flash_hal_mock_h - -#include - -extern "C" -{ - extern uint32_t s_phys_addr; - extern uint32_t s_phys_size; - extern uint32_t s_phys_page; - extern uint32_t s_phys_block; - extern uint8_t* s_phys_data; -} - -extern int32_t flash_hal_read(uint32_t addr, uint32_t size, uint8_t *dst); -extern int32_t flash_hal_write(uint32_t addr, uint32_t size, const uint8_t *src); -extern int32_t flash_hal_erase(uint32_t addr, uint32_t size); - -#endif diff --git a/tests/host/common/include/ClientContext.h b/tests/host/common/include/ClientContext.h index 31366ac0dd..4c8afb6b0b 100644 --- a/tests/host/common/include/ClientContext.h +++ b/tests/host/common/include/ClientContext.h @@ -24,12 +24,9 @@ class ClientContext; class WiFiClient; -extern "C" void esp_yield(); -extern "C" void esp_schedule(); +#include -#include - -bool getDefaultPrivateGlobalSyncValue (); +bool getDefaultPrivateGlobalSyncValue(); typedef void (*discard_cb_t)(void*, ClientContext*); @@ -42,19 +39,19 @@ class ClientContext { (void)pcb; } - - ClientContext (int sock) : + + ClientContext(int sock) : _discard_cb(nullptr), _discard_cb_arg(nullptr), _refcnt(0), _next(nullptr), _sync(::getDefaultPrivateGlobalSyncValue()), _sock(sock) { } - + err_t abort() { if (_sock >= 0) { ::close(_sock); - mockverbose("socket %d closed\n", _sock); + mockverbose("socket %d closed\n", _sock); } _sock = -1; return ERR_ABRT; @@ -91,11 +88,12 @@ class ClientContext void unref() { DEBUGV(":ur %d\r\n", _refcnt); - if(--_refcnt == 0) { + if (--_refcnt == 0) + { discard_received(); close(); if (_discard_cb) - _discard_cb(_discard_cb_arg, this); + _discard_cb(_discard_cb_arg, this); DEBUGV(":del\r\n"); delete this; } @@ -175,10 +173,10 @@ class ClientContext int read() { char c; - return read(&c, 1)? (unsigned char)c: -1; + return read(&c, 1) ? (unsigned char)c : -1; } - size_t read (char* dst, size_t size) + size_t read(char* dst, size_t size) { ssize_t ret = mockRead(_sock, dst, size, 0, _inbuf, _inbufsize); if (ret < 0) @@ -192,10 +190,10 @@ class ClientContext int peek() { char c; - return peekBytes(&c, 1)? c: -1; + return peekBytes(&c, 1) ? c : -1; } - size_t peekBytes(char *dst, size_t size) + size_t peekBytes(char* dst, size_t size) { ssize_t ret = mockPeekBytes(_sock, dst, size, _timeout_ms, _inbuf, _inbufsize); if (ret < 0) @@ -211,7 +209,7 @@ class ClientContext mockverbose("TODO: ClientContext::discard_received()\n"); } - bool wait_until_sent(int max_wait_ms = WIFICLIENT_MAX_FLUSH_WAIT_MS) + bool wait_until_acked(int max_wait_ms = WIFICLIENT_MAX_FLUSH_WAIT_MS) { (void)max_wait_ms; return true; @@ -219,104 +217,110 @@ class ClientContext uint8_t state() { - (void)getSize(); // read on socket to force detect closed peer - return _sock >= 0? ESTABLISHED: CLOSED; + (void)getSize(); // read on socket to force detect closed peer + return _sock >= 0 ? ESTABLISHED : CLOSED; } - size_t write(const uint8_t* data, size_t size) + size_t write(const char* data, size_t size) { - ssize_t ret = mockWrite(_sock, data, size, _timeout_ms); - if (ret < 0) - { - abort(); - return 0; - } - return ret; - } - - size_t write(Stream& stream) - { - size_t avail = stream.available(); - uint8_t buf [avail]; - avail = stream.readBytes(buf, avail); - size_t totwrote = 0; - uint8_t* w = buf; - while (avail && _sock >= 0) + ssize_t ret = mockWrite(_sock, (const uint8_t*)data, size, _timeout_ms); + if (ret < 0) { - size_t wrote = write(w, avail); - w += wrote; - avail -= wrote; - totwrote += wrote; - } - return totwrote; - } - - size_t write_P(PGM_P buf, size_t size) - { - return write((const uint8_t*)buf, size); + abort(); + return 0; + } + return ret; } - 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) + 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) { - (void) idle_sec; - (void) intv_sec; - (void) count; + (void)idle_sec; + (void)intv_sec; + (void)count; mockverbose("TODO ClientContext::keepAlive()\n"); } - bool isKeepAliveEnabled () const + bool isKeepAliveEnabled() const { mockverbose("TODO ClientContext::isKeepAliveEnabled()\n"); return false; } - uint16_t getKeepAliveIdle () const + uint16_t getKeepAliveIdle() const { mockverbose("TODO ClientContext::getKeepAliveIdle()\n"); return 0; } - uint16_t getKeepAliveInterval () const + uint16_t getKeepAliveInterval() const { mockverbose("TODO ClientContext::getKeepAliveInternal()\n"); return 0; } - uint8_t getKeepAliveCount () const + uint8_t getKeepAliveCount() const { mockverbose("TODO ClientContext::getKeepAliveCount()\n"); return 0; } - bool getSync () const + bool getSync() const { mockverbose("TODO ClientContext::getSync()\n"); return _sync; } - void setSync (bool sync) + void setSync(bool sync) { mockverbose("TODO ClientContext::setSync()\n"); _sync = sync; } -private: + // return a pointer to available data buffer (size = peekAvailable()) + // semantic forbids any kind of read() before calling peekConsume() + const char* peekBuffer() + { + return _inbuf; + } + + // return number of byte accessible by peekBuffer() + size_t peekAvailable() + { + ssize_t ret = mockPeekBytes(_sock, nullptr, 0, 0, _inbuf, _inbufsize); + if (ret < 0) + { + abort(); + return 0; + } + return _inbufsize; + } + + // consume bytes after use (see peekBuffer) + void peekConsume(size_t consume) + { + assert(consume <= _inbufsize); + memmove(_inbuf, _inbuf + consume, _inbufsize - consume); + _inbufsize -= consume; + } - discard_cb_t _discard_cb = nullptr; - void* _discard_cb_arg = nullptr; +private: + discard_cb_t _discard_cb = nullptr; + void* _discard_cb_arg = nullptr; - int8_t _refcnt; + int8_t _refcnt; ClientContext* _next; - + bool _sync; - + // MOCK - - int _sock = -1; + + int _sock = -1; int _timeout_ms = 5000; - char _inbuf [CCBUFSIZE]; + char _inbuf[CCBUFSIZE]; size_t _inbufsize = 0; }; -#endif //CLIENTCONTEXT_H +#endif // CLIENTCONTEXT_H diff --git a/tests/host/common/include/UdpContext.h b/tests/host/common/include/UdpContext.h index bf104bc40f..e8ae9461e9 100644 --- a/tests/host/common/include/UdpContext.h +++ b/tests/host/common/include/UdpContext.h @@ -37,17 +37,14 @@ extern netif netif0; class UdpContext { public: - typedef std::function rxhandler_t; - UdpContext(): _on_rx(nullptr), _refcnt(0) + UdpContext() : _on_rx(nullptr), _refcnt(0) { _sock = mockUDPSocket(); } - ~UdpContext() - { - } + ~UdpContext() { } void ref() { @@ -64,7 +61,7 @@ class UdpContext bool connect(const ip_addr_t* addr, uint16_t port) { - _dst = *addr; + _dst = *addr; _dstport = port; return true; } @@ -103,7 +100,7 @@ class UdpContext void setMulticastTTL(int ttl) { (void)ttl; - //mockverbose("TODO: UdpContext::setMulticastTTL\n"); + // mockverbose("TODO: UdpContext::setMulticastTTL\n"); } netif* getInputNetif() const @@ -120,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) @@ -135,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 @@ -156,18 +153,19 @@ class UdpContext IPAddress getDestAddress() { mockverbose("TODO: implement UDP getDestAddress\n"); - return 0; //ip_hdr* iphdr = GET_IP_HDR(_rx_buf); + return 0; // ip_hdr* iphdr = GET_IP_HDR(_rx_buf); } uint16_t getLocalPort() { mockverbose("TODO: implement UDP getLocalPort\n"); - return 0; // + return 0; // } bool next() { _inbufsize = 0; + _inoffset = 0; mockUDPFillInBuf(_sock, _inbuf, _inbufsize, addrsize, addr, _dstport); if (_inbufsize > 0) { @@ -185,20 +183,23 @@ 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() { - //mockverbose("UdpContext::flush() does not follow arduino's flush concept\n"); - //exit(EXIT_FAILURE); - // would be: + // mockverbose("UdpContext::flush() does not follow arduino's flush concept\n"); + // exit(EXIT_FAILURE); + // would be: _inbufsize = 0; } @@ -206,7 +207,8 @@ class UdpContext { if (size + _outbufsize > sizeof _outbuf) { - mockverbose("UdpContext::append: increase CCBUFSIZE (%d -> %zd)\n", CCBUFSIZE, (size + _outbufsize)); + mockverbose("UdpContext::append: increase CCBUFSIZE (%d -> %zd)\n", CCBUFSIZE, + (size + _outbufsize)); exit(EXIT_FAILURE); } @@ -217,9 +219,10 @@ class UdpContext err_t trySend(ip_addr_t* addr = 0, uint16_t port = 0, bool keepBuffer = true) { - uint32_t dst = addr ? addr->addr : _dst.addr; - uint16_t dstport = port ? : _dstport; - size_t wrt = mockUDPWrite(_sock, (const uint8_t*)_outbuf, _outbufsize, _timeout_ms, dst, dstport); + uint32_t dst = addr ? addr->addr : _dst.addr; + uint16_t dstport = port ?: _dstport; + size_t wrt + = mockUDPWrite(_sock, (const uint8_t*)_outbuf, _outbufsize, _timeout_ms, dst, dstport); err_t ret = _outbufsize ? ERR_OK : ERR_ABRT; if (!keepBuffer || wrt == _outbufsize) cancelBuffer(); @@ -239,7 +242,7 @@ class UdpContext bool sendTimeout(ip_addr_t* addr, uint16_t port, esp8266::polledTimeout::oneShotFastMs::timeType timeoutMs) { - err_t err; + err_t err; esp8266::polledTimeout::oneShotFastMs timeout(timeoutMs); while (((err = trySend(addr, port)) != ERR_OK) && !timeout) delay(0); @@ -250,15 +253,14 @@ class UdpContext void mock_cb(void) { - if (_on_rx) _on_rx(); + if (_on_rx) + _on_rx(); } public: - static uint32_t staticMCastAddr; private: - void translate_addr() { if (addrsize == 4) @@ -267,22 +269,23 @@ class UdpContext memcpy(&ipv4, addr, 4); ip4_addr_set_u32(&ip_2_ip4(_dst), ipv4); // ^ this is a workaround for "type-punned pointer" with "*(uint32*)addr" - //ip4_addr_set_u32(&ip_2_ip4(_dst), *(uint32_t*)addr); + // ip4_addr_set_u32(&ip_2_ip4(_dst), *(uint32_t*)addr); } else mockverbose("TODO unhandled udp address of size %d\n", (int)addrsize); } - int _sock = -1; + int _sock = -1; rxhandler_t _on_rx; - int _refcnt = 0; + int _refcnt = 0; ip_addr_t _dst; - uint16_t _dstport; + uint16_t _dstport; - char _inbuf [CCBUFSIZE]; + char _inbuf[CCBUFSIZE]; size_t _inbufsize = 0; - char _outbuf [CCBUFSIZE]; + size_t _inoffset = 0; + char _outbuf[CCBUFSIZE]; size_t _outbufsize = 0; int _timeout_ms = 0; @@ -291,11 +294,11 @@ class UdpContext uint8_t addr[16]; }; -extern "C" inline err_t igmp_joingroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr) +extern "C" inline err_t igmp_joingroup(const ip4_addr_t* ifaddr, const ip4_addr_t* groupaddr) { (void)ifaddr; UdpContext::staticMCastAddr = groupaddr->addr; return ERR_OK; } -#endif//UDPCONTEXT_H +#endif // UDPCONTEXT_H diff --git a/tests/host/common/littlefs_mock.cpp b/tests/host/common/littlefs_mock.cpp index 0d99e82b1e..d7521d4589 100644 --- a/tests/host/common/littlefs_mock.cpp +++ b/tests/host/common/littlefs_mock.cpp @@ -16,7 +16,6 @@ all copies or substantial portions of the Software. */ - #include "littlefs_mock.h" #include "spiffs_mock.h" #include "spiffs/spiffs.h" @@ -32,7 +31,6 @@ #include #include #include -#include "flash_hal_mock.h" #define LITTLEFS_FILE_NAME "littlefs.bin" @@ -57,7 +55,8 @@ LittleFSMock::LittleFSMock(ssize_t fs_size, size_t fs_block, size_t fs_page, con void LittleFSMock::reset() { - LittleFS = FS(FSImplPtr(new littlefs_impl::LittleFSImpl(0, s_phys_size, s_phys_page, s_phys_block, 5))); + LittleFS = FS( + FSImplPtr(new littlefs_impl::LittleFSImpl(0, s_phys_size, s_phys_page, s_phys_block, 5))); load(); } @@ -73,47 +72,52 @@ LittleFSMock::~LittleFSMock() LittleFS = FS(FSImplPtr(nullptr)); } -void LittleFSMock::load () +void LittleFSMock::load() { if (!m_fs.size() || !m_storage.length()) return; - + int fs = ::open(m_storage.c_str(), O_RDONLY); if (fs == -1) { fprintf(stderr, "LittleFS: loading '%s': %s\n", m_storage.c_str(), strerror(errno)); return; } - + off_t flen = lseek(fs, 0, SEEK_END); if (flen == (off_t)-1) { - fprintf(stderr, "LittleFS: checking size of '%s': %s\n", m_storage.c_str(), strerror(errno)); + fprintf(stderr, "LittleFS: checking size of '%s': %s\n", m_storage.c_str(), + strerror(errno)); return; } lseek(fs, 0, SEEK_SET); - + if (flen != (off_t)m_fs.size()) { - fprintf(stderr, "LittleFS: size of '%s': %d does not match requested size %zd\n", m_storage.c_str(), (int)flen, m_fs.size()); + fprintf(stderr, "LittleFS: size of '%s': %d does not match requested size %zd\n", + m_storage.c_str(), (int)flen, m_fs.size()); if (!m_overwrite && flen > 0) { fprintf(stderr, "LittleFS: aborting at user request\n"); exit(1); } - fprintf(stderr, "LittleFS: continuing without loading at user request, '%s' will be overwritten\n", m_storage.c_str()); + fprintf(stderr, + "LittleFS: continuing without loading at user request, '%s' will be overwritten\n", + m_storage.c_str()); } else { fprintf(stderr, "LittleFS: loading %zi bytes from '%s'\n", m_fs.size(), m_storage.c_str()); ssize_t r = ::read(fs, m_fs.data(), m_fs.size()); if (r != (ssize_t)m_fs.size()) - fprintf(stderr, "LittleFS: reading %zi bytes: returned %zd: %s\n", m_fs.size(), r, strerror(errno)); + fprintf(stderr, "LittleFS: reading %zi bytes: returned %zd: %s\n", m_fs.size(), r, + strerror(errno)); } ::close(fs); } -void LittleFSMock::save () +void LittleFSMock::save() { if (!m_fs.size() || !m_storage.length()) return; diff --git a/tests/host/common/littlefs_mock.h b/tests/host/common/littlefs_mock.h index 0905fa9322..af027e7488 100644 --- a/tests/host/common/littlefs_mock.h +++ b/tests/host/common/littlefs_mock.h @@ -4,14 +4,14 @@ Based on spiffs_mock: 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. */ @@ -23,26 +23,29 @@ #include #include #include -#include "flash_hal_mock.h" +#include "flash_hal.h" #define DEFAULT_LITTLEFS_FILE_NAME "littlefs.bin" -class LittleFSMock { +class LittleFSMock +{ public: - LittleFSMock(ssize_t fs_size, size_t fs_block, size_t fs_page, const String& storage = emptyString); + LittleFSMock(ssize_t fs_size, size_t fs_block, size_t fs_page, + const String& storage = emptyString); void reset(); ~LittleFSMock(); - + protected: - void load (); - void save (); + void load(); + void save(); std::vector m_fs; - String m_storage; - bool m_overwrite; + String m_storage; + bool m_overwrite; }; -#define LITTLEFS_MOCK_DECLARE(size_kb, block_kb, page_b, storage) LittleFSMock littlefs_mock(size_kb * 1024, block_kb * 1024, page_b, storage) +#define LITTLEFS_MOCK_DECLARE(size_kb, block_kb, page_b, storage) \ + LittleFSMock littlefs_mock(size_kb * 1024, block_kb * 1024, page_b, storage) #define LITTLEFS_MOCK_RESET() littlefs_mock.reset() #endif /* littlefs_mock_hpp */ diff --git a/tests/host/common/md5.c b/tests/host/common/md5.c index 4b58f261f3..b41c49afae 100644 --- a/tests/host/common/md5.c +++ b/tests/host/common/md5.c @@ -1,18 +1,18 @@ /* * Copyright (c) 2007, Cameron Rich - * + * * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without + * + * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * - * * Redistributions of source code must retain the above copyright notice, + * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * * Neither the name of the axTLS project nor the names of its contributors - * may be used to endorse or promote products derived from this software + * * Neither the name of the axTLS project nor the names of its contributors + * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS @@ -39,16 +39,15 @@ #define EXP_FUNC extern #define STDCALL -#define MD5_SIZE 16 +#define MD5_SIZE 16 -typedef struct +typedef struct { - uint32_t state[4]; /* state (ABCD) */ - uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */ - uint8_t buffer[64]; /* input buffer */ + uint32_t state[4]; /* state (ABCD) */ + uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */ + uint8_t buffer[64]; /* input buffer */ } MD5_CTX; - /* Constants for MD5Transform routine. */ #define S11 7 @@ -70,15 +69,13 @@ typedef struct /* ----- static functions ----- */ static void MD5Transform(uint32_t state[4], const uint8_t block[64]); -static void Encode(uint8_t *output, uint32_t *input, uint32_t len); -static void Decode(uint32_t *output, const uint8_t *input, uint32_t len); +static void Encode(uint8_t* output, uint32_t* input, uint32_t len); +static void Decode(uint32_t* output, const uint8_t* input, uint32_t len); -static const uint8_t PADDING[64] = -{ - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; +static const uint8_t PADDING[64] + = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* F, G, H and I are basic MD5 functions. */ @@ -88,35 +85,39 @@ static const uint8_t PADDING[64] = #define I(x, y, z) ((y) ^ ((x) | (~z))) /* ROTATE_LEFT rotates x left n bits. */ -#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) /* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. Rotation is separate from addition to prevent recomputation. */ -#define FF(a, b, c, d, x, s, ac) { \ - (a) += F ((b), (c), (d)) + (x) + (uint32_t)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } -#define GG(a, b, c, d, x, s, ac) { \ - (a) += G ((b), (c), (d)) + (x) + (uint32_t)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } -#define HH(a, b, c, d, x, s, ac) { \ - (a) += H ((b), (c), (d)) + (x) + (uint32_t)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } -#define II(a, b, c, d, x, s, ac) { \ - (a) += I ((b), (c), (d)) + (x) + (uint32_t)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } +#define FF(a, b, c, d, x, s, ac) \ + { \ + (a) += F((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) \ + { \ + (a) += G((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) \ + { \ + (a) += H((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) \ + { \ + (a) += I((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT((a), (s)); \ + (a) += (b); \ + } /** * MD5 initialization - begins an MD5 operation, writing a new ctx. */ -EXP_FUNC void STDCALL MD5Init(MD5_CTX *ctx) +EXP_FUNC void STDCALL MD5Init(MD5_CTX* ctx) { ctx->count[0] = ctx->count[1] = 0; @@ -131,10 +132,10 @@ EXP_FUNC void STDCALL MD5Init(MD5_CTX *ctx) /** * Accepts an array of octets as the next portion of the message. */ -EXP_FUNC void STDCALL MD5Update(MD5_CTX *ctx, const uint8_t * msg, int len) +EXP_FUNC void STDCALL MD5Update(MD5_CTX* ctx, const uint8_t* msg, int len) { uint32_t x; - int i, partLen; + int i, partLen; /* Compute number of bytes mod 64 */ x = (uint32_t)((ctx->count[0] >> 3) & 0x3F); @@ -147,7 +148,7 @@ EXP_FUNC void STDCALL MD5Update(MD5_CTX *ctx, const uint8_t * msg, int len) partLen = 64 - x; /* Transform as many times as possible. */ - if (len >= partLen) + if (len >= partLen) { memcpy(&ctx->buffer[x], msg, partLen); MD5Transform(ctx->state, ctx->buffer); @@ -161,15 +162,15 @@ EXP_FUNC void STDCALL MD5Update(MD5_CTX *ctx, const uint8_t * msg, int len) i = 0; /* Buffer remaining input */ - memcpy(&ctx->buffer[x], &msg[i], len-i); + memcpy(&ctx->buffer[x], &msg[i], len - i); } /** * Return the 128-bit message digest into the user's array */ -EXP_FUNC void STDCALL MD5Final(uint8_t *digest, MD5_CTX *ctx) +EXP_FUNC void STDCALL MD5Final(uint8_t* digest, MD5_CTX* ctx) { - uint8_t bits[8]; + uint8_t bits[8]; uint32_t x, padLen; /* Save number of bits */ @@ -177,7 +178,7 @@ EXP_FUNC void STDCALL MD5Final(uint8_t *digest, MD5_CTX *ctx) /* Pad out to 56 mod 64. */ - x = (uint32_t)((ctx->count[0] >> 3) & 0x3f); + x = (uint32_t)((ctx->count[0] >> 3) & 0x3f); padLen = (x < 56) ? (56 - x) : (120 - x); MD5Update(ctx, PADDING, padLen); @@ -193,82 +194,81 @@ EXP_FUNC void STDCALL MD5Final(uint8_t *digest, MD5_CTX *ctx) */ static void MD5Transform(uint32_t state[4], const uint8_t block[64]) { - uint32_t a = state[0], b = state[1], c = state[2], - d = state[3], x[MD5_SIZE]; + uint32_t a = state[0], b = state[1], c = state[2], d = state[3], x[MD5_SIZE]; Decode(x, block, 64); /* Round 1 */ - FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ - FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ - FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ - FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ - FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ - FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ - FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ - FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ - FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ - FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ - FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ - FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ - FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ - FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ - FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ - FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */ + FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */ + FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */ + FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */ + FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */ + FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */ + FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */ + FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */ + FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */ + FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */ + FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ /* Round 2 */ - GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ - GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ - GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ - GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ - GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ - GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ - GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ - GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ - GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ - GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ - GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ - GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ - GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ - GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ - GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ - GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */ + GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */ + GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */ + GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */ + GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */ + GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */ + GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */ + GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */ + GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */ + GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */ + GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ /* Round 3 */ - HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ - HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ - HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ - HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ - HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ - HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ - HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ - HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ - HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ - HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ - HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ - HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ - HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ - HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ - HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ - HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */ + HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */ + HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */ + HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */ + HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */ + HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */ + HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */ + HH(b, c, d, a, x[6], S34, 0x4881d05); /* 44 */ + HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */ + HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */ /* Round 4 */ - II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ - II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ - II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ - II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ - II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ - II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ - II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ - II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ - II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ - II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ - II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ - II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ - II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ - II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ - II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ - II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */ + II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */ + II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */ + II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */ + II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */ + II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */ + II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */ + II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */ + II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */ + II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */ state[0] += a; state[1] += b; @@ -280,16 +280,16 @@ static void MD5Transform(uint32_t state[4], const uint8_t block[64]) * Encodes input (uint32_t) into output (uint8_t). Assumes len is * a multiple of 4. */ -static void Encode(uint8_t *output, uint32_t *input, uint32_t len) +static void Encode(uint8_t* output, uint32_t* input, uint32_t len) { uint32_t i, j; - for (i = 0, j = 0; j < len; i++, j += 4) + for (i = 0, j = 0; j < len; i++, j += 4) { - output[j] = (uint8_t)(input[i] & 0xff); - output[j+1] = (uint8_t)((input[i] >> 8) & 0xff); - output[j+2] = (uint8_t)((input[i] >> 16) & 0xff); - output[j+3] = (uint8_t)((input[i] >> 24) & 0xff); + output[j] = (uint8_t)(input[i] & 0xff); + output[j + 1] = (uint8_t)((input[i] >> 8) & 0xff); + output[j + 2] = (uint8_t)((input[i] >> 16) & 0xff); + output[j + 3] = (uint8_t)((input[i] >> 24) & 0xff); } } @@ -297,11 +297,11 @@ static void Encode(uint8_t *output, uint32_t *input, uint32_t len) * Decodes input (uint8_t) into output (uint32_t). Assumes len is * a multiple of 4. */ -static void Decode(uint32_t *output, const uint8_t *input, uint32_t len) +static void Decode(uint32_t* output, const uint8_t* input, uint32_t len) { uint32_t i, j; for (i = 0, j = 0; j < len; i++, j += 4) - output[i] = ((uint32_t)input[j]) | (((uint32_t)input[j+1]) << 8) | - (((uint32_t)input[j+2]) << 16) | (((uint32_t)input[j+3]) << 24); + output[i] = ((uint32_t)input[j]) | (((uint32_t)input[j + 1]) << 8) + | (((uint32_t)input[j + 2]) << 16) | (((uint32_t)input[j + 3]) << 24); } diff --git a/tests/host/common/mock.h b/tests/host/common/mock.h index da58c889aa..344ce6b10c 100644 --- a/tests/host/common/mock.h +++ b/tests/host/common/mock.h @@ -30,7 +30,8 @@ */ #define CORE_MOCK 1 -#define MOCK "(mock) " // TODO: provide common logging API instead of adding this string everywhere? +#define MOCK \ + "(mock) " // TODO: provide common logging API instead of adding this string everywhere? // @@ -55,18 +56,23 @@ #define D8 8 #include +#include +#include + +#include #ifdef __cplusplus -extern "C" { +extern "C" +{ +#endif + 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 -// TODO: #include ? -char* itoa (int val, char *s, int radix); -char* ltoa (long val, char *s, int radix); - - -size_t strlcat(char *dst, const char *src, size_t size); -size_t strlcpy(char *dst, const char *src, size_t size); - #ifdef __cplusplus } #endif @@ -74,7 +80,7 @@ size_t strlcpy(char *dst, const char *src, size_t size); // exotic typedefs used in the sdk #include -typedef uint8_t uint8; +typedef uint8_t uint8; typedef uint32_t uint32; // @@ -97,23 +103,30 @@ uint32_t esp_get_cycle_count(); // #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif #include -int ets_printf (const char* fmt, ...) __attribute__ ((format (printf, 1, 2))); + int ets_printf(const char* fmt, ...) __attribute__((format(printf, 1, 2))); #define os_printf_plus printf #define ets_vsnprintf vsnprintf + inline void ets_putc(char c) + { + putchar(c); + } -int mockverbose (const char* fmt, ...) __attribute__ ((format (printf, 1, 2))); + int mockverbose(const char* fmt, ...) __attribute__((format(printf, 1, 2))); -extern const char* host_interface; // cmdline parameter -extern bool serial_timestamp; -extern int mock_port_shifter; -extern bool blocking_uart; -extern uint32_t global_source_address; // 0 = INADDR_ANY by default + extern const char* host_interface; // cmdline parameter + extern bool serial_timestamp; + extern int mock_port_shifter; + extern bool blocking_uart; + extern uint32_t global_source_address; // 0 = INADDR_ANY by default #define NO_GLOBAL_BINDING 0xffffffff -extern uint32_t global_ipv4_netfmt; // selected interface addresse to bind to + extern uint32_t global_ipv4_netfmt; // selected interface addresse to bind to + + void loop_end(); #ifdef __cplusplus } @@ -129,41 +142,46 @@ extern uint32_t global_ipv4_netfmt; // selected interface addresse to bind to // uart #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif -void uart_new_data(const int uart_nr, uint8_t data); + void uart_new_data(const int uart_nr, uint8_t data); #ifdef __cplusplus } #endif // tcp -int mockSockSetup (int sock); -int mockConnect (uint32_t addr, int& sock, int port); -ssize_t mockFillInBuf (int sock, char* ccinbuf, size_t& ccinbufsize); -ssize_t mockPeekBytes (int sock, char* dst, size_t size, int timeout_ms, char* buf, size_t& bufsize); -ssize_t mockRead (int sock, char* dst, size_t size, int timeout_ms, char* buf, size_t& bufsize); -ssize_t mockWrite (int sock, const uint8_t* data, size_t size, int timeout_ms); -int serverAccept (int sock); +int mockSockSetup(int sock); +int mockConnect(uint32_t addr, int& sock, int port); +ssize_t mockFillInBuf(int sock, char* ccinbuf, size_t& ccinbufsize); +ssize_t mockPeekBytes(int sock, char* dst, size_t size, int timeout_ms, char* buf, size_t& bufsize); +ssize_t mockRead(int sock, char* dst, size_t size, int timeout_ms, char* buf, size_t& bufsize); +ssize_t mockWrite(int sock, const uint8_t* data, size_t size, int timeout_ms); +int serverAccept(int sock); // udp -void check_incoming_udp (); -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 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); +void check_incoming_udp(); +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 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); class UdpContext; -void register_udp (int sock, UdpContext* udp = nullptr); +void register_udp(int sock, UdpContext* udp = nullptr); +void mock_stop_udp(); // -void mock_start_spiffs (const String& fname, size_t size_kb, size_t block_kb = 8, size_t page_b = 512); -void mock_stop_spiffs (); -void mock_start_littlefs (const String& fname, size_t size_kb, size_t block_kb = 8, size_t page_b = 512); -void mock_stop_littlefs (); +void mock_start_spiffs(const String& fname, size_t size_kb, size_t block_kb = 8, + size_t page_b = 512); +void mock_stop_spiffs(); +void mock_start_littlefs(const String& fname, size_t size_kb, size_t block_kb = 8, + size_t page_b = 512); +void mock_stop_littlefs(); // @@ -171,4 +189,4 @@ void mock_stop_littlefs (); // -#endif // __cplusplus +#endif // __cplusplus diff --git a/tests/host/common/noniso.c b/tests/host/common/noniso.c index 869a4b7f8e..20fd3d1d5a 100644 --- a/tests/host/common/noniso.c +++ b/tests/host/common/noniso.c @@ -1,99 +1,87 @@ /* noniso.cpp - replacements for non-ISO functions used by Arduino core 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 #include -#include "stdlib_noniso.h" +#include -void reverse(char* begin, char* end) { - char *is = begin; - char *ie = end - 1; - while(is < ie) { +static void reverse(char* begin, char* end) +{ + char* is = begin; + char* ie = end - 1; + while (is < ie) + { char tmp = *ie; - *ie = *is; - *is = tmp; + *ie = *is; + *is = tmp; ++is; --ie; } } -char* utoa(unsigned value, char* result, int base) { - if(base < 2 || base > 16) { +char* utoa(unsigned value, char* result, int base) +{ + if (base < 2 || base > 16) + { *result = 0; return result; } - char* out = result; + char* out = result; unsigned quotient = value; - do { + do + { const unsigned tmp = quotient / base; - *out = "0123456789abcdef"[quotient - (tmp * base)]; + *out = "0123456789abcdef"[quotient - (tmp * base)]; ++out; quotient = tmp; - } while(quotient); + } while (quotient); reverse(result, out); *out = 0; return result; } -char* itoa(int value, char* result, int base) { - if(base < 2 || base > 16) { +char* itoa(int value, char* result, int base) +{ + if (base < 2 || base > 16) + { *result = 0; return result; } - if (base != 10) { - return utoa((unsigned)value, result, base); - } - - char* out = result; - int quotient = abs(value); - - do { - 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); -} + unsigned uvalue; + char* out = result; -long atol(const char* s) { - char * tmp; - return strtol(s, &tmp, 10); -} + // 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)) + { + *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/pins_arduino.h b/tests/host/common/pins_arduino.h index ad051ed24a..5674fc61c8 100644 --- a/tests/host/common/pins_arduino.h +++ b/tests/host/common/pins_arduino.h @@ -1,19 +1,18 @@ /* pins_arduino.h 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. */ #ifndef pins_arduino_h #define pins_arduino_h - #endif /* pins_arduino_h */ diff --git a/tests/host/common/queue.h b/tests/host/common/queue.h index af637ca030..4919ae9fc4 100644 --- a/tests/host/common/queue.h +++ b/tests/host/common/queue.h @@ -35,9 +35,9 @@ */ #ifndef _SYS_QUEUE_H_ -#define _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ -#include /* for __offsetof */ +#include /* for __offsetof */ /* * This file defines four types of data structures: singly-linked lists, @@ -106,322 +106,373 @@ /* * Singly-linked List declarations. */ -#define SLIST_HEAD(name, type) \ -struct name { \ - struct type *slh_first; /* first element */ \ -} +#define SLIST_HEAD(name, type) \ + struct name \ + { \ + struct type* slh_first; /* first element */ \ + } + +#define SLIST_HEAD_INITIALIZER(head) \ + { \ + NULL \ + } + +#define SLIST_ENTRY(type) \ + struct \ + { \ + struct type* sle_next; /* next element */ \ + } -#define SLIST_HEAD_INITIALIZER(head) \ - { NULL } - -#define SLIST_ENTRY(type) \ -struct { \ - struct type *sle_next; /* next element */ \ -} - /* * Singly-linked List functions. */ -#define SLIST_EMPTY(head) ((head)->slh_first == NULL) - -#define SLIST_FIRST(head) ((head)->slh_first) - -#define SLIST_FOREACH(var, head, field) \ - for ((var) = SLIST_FIRST((head)); \ - (var); \ - (var) = SLIST_NEXT((var), field)) - -#define SLIST_INIT(head) do { \ - SLIST_FIRST((head)) = NULL; \ -} while (0) - -#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ - SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \ - SLIST_NEXT((slistelm), field) = (elm); \ -} while (0) - -#define SLIST_INSERT_HEAD(head, elm, field) do { \ - SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \ - SLIST_FIRST((head)) = (elm); \ -} while (0) - -#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) - -#define SLIST_REMOVE(head, elm, type, field) do { \ - if (SLIST_FIRST((head)) == (elm)) { \ - SLIST_REMOVE_HEAD((head), field); \ - } \ - else { \ - struct type *curelm = SLIST_FIRST((head)); \ - while (SLIST_NEXT(curelm, field) != (elm)) \ - curelm = SLIST_NEXT(curelm, field); \ - SLIST_NEXT(curelm, field) = \ - SLIST_NEXT(SLIST_NEXT(curelm, field), field); \ - } \ -} while (0) - -#define SLIST_REMOVE_HEAD(head, field) do { \ - SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \ -} while (0) +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) + +#define SLIST_FIRST(head) ((head)->slh_first) + +#define SLIST_FOREACH(var, head, field) \ + for ((var) = SLIST_FIRST((head)); (var); (var) = SLIST_NEXT((var), field)) + +#define SLIST_INIT(head) \ + do \ + { \ + SLIST_FIRST((head)) = NULL; \ + } while (0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) \ + do \ + { \ + SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \ + SLIST_NEXT((slistelm), field) = (elm); \ + } while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) \ + do \ + { \ + SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \ + SLIST_FIRST((head)) = (elm); \ + } while (0) + +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_REMOVE(head, elm, type, field) \ + do \ + { \ + if (SLIST_FIRST((head)) == (elm)) \ + { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else \ + { \ + struct type* curelm = SLIST_FIRST((head)); \ + while (SLIST_NEXT(curelm, field) != (elm)) \ + curelm = SLIST_NEXT(curelm, field); \ + SLIST_NEXT(curelm, field) = SLIST_NEXT(SLIST_NEXT(curelm, field), field); \ + } \ + } while (0) + +#define SLIST_REMOVE_HEAD(head, field) \ + do \ + { \ + SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \ + } while (0) /* * Singly-linked Tail queue declarations. */ -#define STAILQ_HEAD(name, type) \ -struct name { \ - struct type *stqh_first;/* first element */ \ - struct type **stqh_last;/* addr of last next element */ \ -} - -#define STAILQ_HEAD_INITIALIZER(head) \ - { NULL, &(head).stqh_first } - -#define STAILQ_ENTRY(type) \ -struct { \ - struct type *stqe_next; /* next element */ \ -} +#define STAILQ_HEAD(name, type) \ + struct name \ + { \ + struct type* stqh_first; /* first element */ \ + struct type** stqh_last; /* addr of last next element */ \ + } + +#define STAILQ_HEAD_INITIALIZER(head) \ + { \ + NULL, &(head).stqh_first \ + } + +#define STAILQ_ENTRY(type) \ + struct \ + { \ + struct type* stqe_next; /* next element */ \ + } /* * Singly-linked Tail queue functions. */ -#define STAILQ_CONCAT(head1, head2) do { \ - if (!STAILQ_EMPTY((head2))) { \ - *(head1)->stqh_last = (head2)->stqh_first; \ - (head1)->stqh_last = (head2)->stqh_last; \ - STAILQ_INIT((head2)); \ - } \ -} while (0) - -#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) - -#define STAILQ_FIRST(head) ((head)->stqh_first) - -#define STAILQ_FOREACH(var, head, field) \ - for((var) = STAILQ_FIRST((head)); \ - (var); \ - (var) = STAILQ_NEXT((var), field)) - -#define STAILQ_INIT(head) do { \ - STAILQ_FIRST((head)) = NULL; \ - (head)->stqh_last = &STAILQ_FIRST((head)); \ -} while (0) - -#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ - if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\ - (head)->stqh_last = &STAILQ_NEXT((elm), field); \ - STAILQ_NEXT((tqelm), field) = (elm); \ -} while (0) - -#define STAILQ_INSERT_HEAD(head, elm, field) do { \ - if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ - (head)->stqh_last = &STAILQ_NEXT((elm), field); \ - STAILQ_FIRST((head)) = (elm); \ -} while (0) - -#define STAILQ_INSERT_TAIL(head, elm, field) do { \ - STAILQ_NEXT((elm), field) = NULL; \ - *(head)->stqh_last = (elm); \ - (head)->stqh_last = &STAILQ_NEXT((elm), field); \ -} while (0) - -#define STAILQ_LAST(head, type, field) \ - (STAILQ_EMPTY((head)) ? \ - NULL : \ - ((struct type *) \ - ((char *)((head)->stqh_last) - __offsetof(struct type, field)))) - -#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) - -#define STAILQ_REMOVE(head, elm, type, field) do { \ - if (STAILQ_FIRST((head)) == (elm)) { \ - STAILQ_REMOVE_HEAD((head), field); \ - } \ - else { \ - struct type *curelm = STAILQ_FIRST((head)); \ - while (STAILQ_NEXT(curelm, field) != (elm)) \ - curelm = STAILQ_NEXT(curelm, field); \ - if ((STAILQ_NEXT(curelm, field) = \ - STAILQ_NEXT(STAILQ_NEXT(curelm, field), field)) == NULL)\ - (head)->stqh_last = &STAILQ_NEXT((curelm), field);\ - } \ -} while (0) - -#define STAILQ_REMOVE_HEAD(head, field) do { \ - if ((STAILQ_FIRST((head)) = \ - STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ - (head)->stqh_last = &STAILQ_FIRST((head)); \ -} while (0) - -#define STAILQ_REMOVE_HEAD_UNTIL(head, elm, field) do { \ - if ((STAILQ_FIRST((head)) = STAILQ_NEXT((elm), field)) == NULL) \ - (head)->stqh_last = &STAILQ_FIRST((head)); \ -} while (0) +#define STAILQ_CONCAT(head1, head2) \ + do \ + { \ + if (!STAILQ_EMPTY((head2))) \ + { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ + } while (0) + +#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) + +#define STAILQ_FIRST(head) ((head)->stqh_first) + +#define STAILQ_FOREACH(var, head, field) \ + for ((var) = STAILQ_FIRST((head)); (var); (var) = STAILQ_NEXT((var), field)) + +#define STAILQ_INIT(head) \ + do \ + { \ + STAILQ_FIRST((head)) = NULL; \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ + } while (0) + +#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) \ + do \ + { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_NEXT((tqelm), field) = (elm); \ + } while (0) + +#define STAILQ_INSERT_HEAD(head, elm, field) \ + do \ + { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_FIRST((head)) = (elm); \ + } while (0) + +#define STAILQ_INSERT_TAIL(head, elm, field) \ + do \ + { \ + STAILQ_NEXT((elm), field) = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + } while (0) + +#define STAILQ_LAST(head, type, field) \ + (STAILQ_EMPTY((head)) \ + ? NULL \ + : ((struct type*)((char*)((head)->stqh_last) - __offsetof(struct type, field)))) + +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + +#define STAILQ_REMOVE(head, elm, type, field) \ + do \ + { \ + if (STAILQ_FIRST((head)) == (elm)) \ + { \ + STAILQ_REMOVE_HEAD((head), field); \ + } \ + else \ + { \ + struct type* curelm = STAILQ_FIRST((head)); \ + while (STAILQ_NEXT(curelm, field) != (elm)) \ + curelm = STAILQ_NEXT(curelm, field); \ + if ((STAILQ_NEXT(curelm, field) = STAILQ_NEXT(STAILQ_NEXT(curelm, field), field)) \ + == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((curelm), field); \ + } \ + } while (0) + +#define STAILQ_REMOVE_HEAD(head, field) \ + do \ + { \ + if ((STAILQ_FIRST((head)) = STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ + } while (0) + +#define STAILQ_REMOVE_HEAD_UNTIL(head, elm, field) \ + do \ + { \ + if ((STAILQ_FIRST((head)) = STAILQ_NEXT((elm), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ + } while (0) /* * List declarations. */ -#define LIST_HEAD(name, type) \ -struct name { \ - struct type *lh_first; /* first element */ \ -} - -#define LIST_HEAD_INITIALIZER(head) \ - { NULL } - -#define LIST_ENTRY(type) \ -struct { \ - struct type *le_next; /* next element */ \ - struct type **le_prev; /* address of previous next element */ \ -} +#define LIST_HEAD(name, type) \ + struct name \ + { \ + struct type* lh_first; /* first element */ \ + } + +#define LIST_HEAD_INITIALIZER(head) \ + { \ + NULL \ + } + +#define LIST_ENTRY(type) \ + struct \ + { \ + struct type* le_next; /* next element */ \ + struct type** le_prev; /* address of previous next element */ \ + } /* * List functions. */ -#define LIST_EMPTY(head) ((head)->lh_first == NULL) - -#define LIST_FIRST(head) ((head)->lh_first) - -#define LIST_FOREACH(var, head, field) \ - for ((var) = LIST_FIRST((head)); \ - (var); \ - (var) = LIST_NEXT((var), field)) - -#define LIST_INIT(head) do { \ - LIST_FIRST((head)) = NULL; \ -} while (0) - -#define LIST_INSERT_AFTER(listelm, elm, field) do { \ - if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\ - LIST_NEXT((listelm), field)->field.le_prev = \ - &LIST_NEXT((elm), field); \ - LIST_NEXT((listelm), field) = (elm); \ - (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ -} while (0) - -#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ - (elm)->field.le_prev = (listelm)->field.le_prev; \ - LIST_NEXT((elm), field) = (listelm); \ - *(listelm)->field.le_prev = (elm); \ - (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ -} while (0) - -#define LIST_INSERT_HEAD(head, elm, field) do { \ - if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ - LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\ - LIST_FIRST((head)) = (elm); \ - (elm)->field.le_prev = &LIST_FIRST((head)); \ -} while (0) - -#define LIST_NEXT(elm, field) ((elm)->field.le_next) - -#define LIST_REMOVE(elm, field) do { \ - if (LIST_NEXT((elm), field) != NULL) \ - LIST_NEXT((elm), field)->field.le_prev = \ - (elm)->field.le_prev; \ - *(elm)->field.le_prev = LIST_NEXT((elm), field); \ -} while (0) +#define LIST_EMPTY(head) ((head)->lh_first == NULL) + +#define LIST_FIRST(head) ((head)->lh_first) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = LIST_FIRST((head)); (var); (var) = LIST_NEXT((var), field)) + +#define LIST_INIT(head) \ + do \ + { \ + LIST_FIRST((head)) = NULL; \ + } while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) \ + do \ + { \ + if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL) \ + LIST_NEXT((listelm), field)->field.le_prev = &LIST_NEXT((elm), field); \ + LIST_NEXT((listelm), field) = (elm); \ + (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ + } while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) \ + do \ + { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + LIST_NEXT((elm), field) = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ + } while (0) + +#define LIST_INSERT_HEAD(head, elm, field) \ + do \ + { \ + if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ + LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field); \ + LIST_FIRST((head)) = (elm); \ + (elm)->field.le_prev = &LIST_FIRST((head)); \ + } while (0) + +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_REMOVE(elm, field) \ + do \ + { \ + if (LIST_NEXT((elm), field) != NULL) \ + LIST_NEXT((elm), field)->field.le_prev = (elm)->field.le_prev; \ + *(elm)->field.le_prev = LIST_NEXT((elm), field); \ + } while (0) /* * Tail queue declarations. */ -#define TAILQ_HEAD(name, type) \ -struct name { \ - struct type *tqh_first; /* first element */ \ - struct type **tqh_last; /* addr of last next element */ \ -} - -#define TAILQ_HEAD_INITIALIZER(head) \ - { NULL, &(head).tqh_first } - -#define TAILQ_ENTRY(type) \ -struct { \ - struct type *tqe_next; /* next element */ \ - struct type **tqe_prev; /* address of previous next element */ \ -} +#define TAILQ_HEAD(name, type) \ + struct name \ + { \ + struct type* tqh_first; /* first element */ \ + struct type** tqh_last; /* addr of last next element */ \ + } + +#define TAILQ_HEAD_INITIALIZER(head) \ + { \ + NULL, &(head).tqh_first \ + } + +#define TAILQ_ENTRY(type) \ + struct \ + { \ + struct type* tqe_next; /* next element */ \ + struct type** tqe_prev; /* address of previous next element */ \ + } /* * Tail queue functions. */ -#define TAILQ_CONCAT(head1, head2, field) do { \ - if (!TAILQ_EMPTY(head2)) { \ - *(head1)->tqh_last = (head2)->tqh_first; \ - (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ - (head1)->tqh_last = (head2)->tqh_last; \ - TAILQ_INIT((head2)); \ - } \ -} while (0) - -#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) - -#define TAILQ_FIRST(head) ((head)->tqh_first) - -#define TAILQ_FOREACH(var, head, field) \ - for ((var) = TAILQ_FIRST((head)); \ - (var); \ - (var) = TAILQ_NEXT((var), field)) - -#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ - for ((var) = TAILQ_LAST((head), headname); \ - (var); \ - (var) = TAILQ_PREV((var), headname, field)) - -#define TAILQ_INIT(head) do { \ - TAILQ_FIRST((head)) = NULL; \ - (head)->tqh_last = &TAILQ_FIRST((head)); \ -} while (0) - -#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ - if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\ - TAILQ_NEXT((elm), field)->field.tqe_prev = \ - &TAILQ_NEXT((elm), field); \ - else \ - (head)->tqh_last = &TAILQ_NEXT((elm), field); \ - TAILQ_NEXT((listelm), field) = (elm); \ - (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \ -} while (0) - -#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ - (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ - TAILQ_NEXT((elm), field) = (listelm); \ - *(listelm)->field.tqe_prev = (elm); \ - (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \ -} while (0) - -#define TAILQ_INSERT_HEAD(head, elm, field) do { \ - if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ - TAILQ_FIRST((head))->field.tqe_prev = \ - &TAILQ_NEXT((elm), field); \ - else \ - (head)->tqh_last = &TAILQ_NEXT((elm), field); \ - TAILQ_FIRST((head)) = (elm); \ - (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \ -} while (0) - -#define TAILQ_INSERT_TAIL(head, elm, field) do { \ - TAILQ_NEXT((elm), field) = NULL; \ - (elm)->field.tqe_prev = (head)->tqh_last; \ - *(head)->tqh_last = (elm); \ - (head)->tqh_last = &TAILQ_NEXT((elm), field); \ -} while (0) - -#define TAILQ_LAST(head, headname) \ - (*(((struct headname *)((head)->tqh_last))->tqh_last)) - -#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) - -#define TAILQ_PREV(elm, headname, field) \ - (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) - -#define TAILQ_REMOVE(head, elm, field) do { \ - if ((TAILQ_NEXT((elm), field)) != NULL) \ - TAILQ_NEXT((elm), field)->field.tqe_prev = \ - (elm)->field.tqe_prev; \ - else \ - (head)->tqh_last = (elm)->field.tqe_prev; \ - *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ -} while (0) - +#define TAILQ_CONCAT(head1, head2, field) \ + do \ + { \ + if (!TAILQ_EMPTY(head2)) \ + { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + } \ + } while (0) + +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) + +#define TAILQ_FIRST(head) ((head)->tqh_first) + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = TAILQ_FIRST((head)); (var); (var) = TAILQ_NEXT((var), field)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = TAILQ_LAST((head), headname); (var); (var) = TAILQ_PREV((var), headname, field)) + +#define TAILQ_INIT(head) \ + do \ + { \ + TAILQ_FIRST((head)) = NULL; \ + (head)->tqh_last = &TAILQ_FIRST((head)); \ + } while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) \ + do \ + { \ + if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL) \ + TAILQ_NEXT((elm), field)->field.tqe_prev = &TAILQ_NEXT((elm), field); \ + else \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + TAILQ_NEXT((listelm), field) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \ + } while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) \ + do \ + { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + TAILQ_NEXT((elm), field) = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \ + } while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) \ + do \ + { \ + if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ + TAILQ_FIRST((head))->field.tqe_prev = &TAILQ_NEXT((elm), field); \ + else \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + TAILQ_FIRST((head)) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \ + } while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) \ + do \ + { \ + TAILQ_NEXT((elm), field) = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + } while (0) + +#define TAILQ_LAST(head, headname) (*(((struct headname*)((head)->tqh_last))->tqh_last)) + +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_PREV(elm, headname, field) (*(((struct headname*)((elm)->field.tqe_prev))->tqh_last)) + +#define TAILQ_REMOVE(head, elm, field) \ + do \ + { \ + if ((TAILQ_NEXT((elm), field)) != NULL) \ + TAILQ_NEXT((elm), field)->field.tqe_prev = (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ + } while (0) #ifdef _KERNEL @@ -430,39 +481,37 @@ struct { \ * They bogusly assumes that all queue heads look alike. */ -struct quehead { - struct quehead *qh_link; - struct quehead *qh_rlink; +struct quehead +{ + struct quehead* qh_link; + struct quehead* qh_rlink; }; -#ifdef __GNUC__ +#ifdef __GNUC__ -static __inline void -insque(void *a, void *b) +static __inline void insque(void* a, void* b) { - struct quehead *element = (struct quehead *)a, - *head = (struct quehead *)b; + struct quehead *element = (struct quehead*)a, *head = (struct quehead*)b; - element->qh_link = head->qh_link; - element->qh_rlink = head; - head->qh_link = element; - element->qh_link->qh_rlink = element; + element->qh_link = head->qh_link; + element->qh_rlink = head; + head->qh_link = element; + element->qh_link->qh_rlink = element; } -static __inline void -remque(void *a) +static __inline void remque(void* a) { - struct quehead *element = (struct quehead *)a; + struct quehead* element = (struct quehead*)a; - element->qh_link->qh_rlink = element->qh_rlink; - element->qh_rlink->qh_link = element->qh_link; - element->qh_rlink = 0; + element->qh_link->qh_rlink = element->qh_rlink; + element->qh_rlink->qh_link = element->qh_link; + element->qh_rlink = 0; } #else /* !__GNUC__ */ -void insque(void *a, void *b); -void remque(void *a); +void insque(void* a, void* b); +void remque(void* a); #endif /* __GNUC__ */ diff --git a/tests/host/common/sdfs_mock.cpp b/tests/host/common/sdfs_mock.cpp index ef2ae44152..3a7c948b6a 100644 --- a/tests/host/common/sdfs_mock.cpp +++ b/tests/host/common/sdfs_mock.cpp @@ -18,4 +18,4 @@ #define SDSIZE 16LL uint64_t _sdCardSizeB = 0; -uint8_t *_sdCard = nullptr; +uint8_t* _sdCard = nullptr; diff --git a/tests/host/common/sdfs_mock.h b/tests/host/common/sdfs_mock.h index 611474701a..fd33b2f9c3 100644 --- a/tests/host/common/sdfs_mock.h +++ b/tests/host/common/sdfs_mock.h @@ -21,22 +21,32 @@ #include #include -class SDFSMock { +class SDFSMock +{ public: - SDFSMock(ssize_t fs_size, size_t fs_block, size_t fs_page, const String& storage = emptyString) { (void)fs_size; (void)fs_block; (void)fs_page; (void)storage; } + SDFSMock(ssize_t fs_size, size_t fs_block, size_t fs_page, const String& storage = emptyString) + { + (void)fs_size; + (void)fs_block; + (void)fs_page; + (void)storage; + } void reset() { } ~SDFSMock() { } }; extern uint64_t _sdCardSizeB; -extern uint8_t *_sdCard; - -#define SDFS_MOCK_DECLARE(size_kb, block_kb, page_b, storage) \ - SDFS.end(); \ - SDFSMock sdfs_mock(size_kb * 1024, block_kb * 1024, page_b, storage); free(_sdCard); \ - _sdCardSizeB = size_kb ? 16 * 1024 * 1024 : 0; \ - if (_sdCardSizeB) _sdCard = (uint8_t*)calloc(_sdCardSizeB, 1); \ - else _sdCard = nullptr; \ +extern uint8_t* _sdCard; + +#define SDFS_MOCK_DECLARE(size_kb, block_kb, page_b, storage) \ + SDFS.end(); \ + SDFSMock sdfs_mock(size_kb * 1024, block_kb * 1024, page_b, storage); \ + free(_sdCard); \ + _sdCardSizeB = size_kb ? 16 * 1024 * 1024 : 0; \ + if (_sdCardSizeB) \ + _sdCard = (uint8_t*)calloc(_sdCardSizeB, 1); \ + else \ + _sdCard = nullptr; \ SDFS.setConfig(SDFSConfig().setAutoFormat(true)); #define SDFS_MOCK_RESET() sdfs_mock.reset() diff --git a/tests/host/common/spiffs_mock.cpp b/tests/host/common/spiffs_mock.cpp index c7f9f53aba..e2d5fe341c 100644 --- a/tests/host/common/spiffs_mock.cpp +++ b/tests/host/common/spiffs_mock.cpp @@ -13,7 +13,6 @@ all copies or substantial portions of the Software. */ - #include "spiffs_mock.h" #include "spiffs/spiffs.h" #include "debug.h" @@ -28,8 +27,6 @@ #include #include -#include "flash_hal_mock.h" - #define SPIFFS_FILE_NAME "spiffs.bin" #pragma GCC diagnostic push @@ -56,7 +53,8 @@ SpiffsMock::SpiffsMock(ssize_t fs_size, size_t fs_block, size_t fs_page, const S void SpiffsMock::reset() { - SPIFFS = FS(FSImplPtr(new spiffs_impl::SPIFFSImpl(0, s_phys_size, s_phys_page, s_phys_block, 5))); + SPIFFS + = FS(FSImplPtr(new spiffs_impl::SPIFFSImpl(0, s_phys_size, s_phys_page, s_phys_block, 5))); load(); } @@ -72,18 +70,18 @@ SpiffsMock::~SpiffsMock() SPIFFS = FS(FSImplPtr(nullptr)); } -void SpiffsMock::load () +void SpiffsMock::load() { if (!m_fs.size() || !m_storage.length()) return; - + int fs = ::open(m_storage.c_str(), O_RDONLY); if (fs == -1) { fprintf(stderr, "SPIFFS: loading '%s': %s\n", m_storage.c_str(), strerror(errno)); return; } - + off_t flen = lseek(fs, 0, SEEK_END); if (flen == (off_t)-1) { @@ -91,28 +89,32 @@ void SpiffsMock::load () return; } lseek(fs, 0, SEEK_SET); - + if (flen != (off_t)m_fs.size()) { - fprintf(stderr, "SPIFFS: size of '%s': %d does not match requested size %zd\n", m_storage.c_str(), (int)flen, m_fs.size()); + fprintf(stderr, "SPIFFS: size of '%s': %d does not match requested size %zd\n", + m_storage.c_str(), (int)flen, m_fs.size()); if (!m_overwrite && flen > 0) { fprintf(stderr, "SPIFFS: aborting at user request\n"); exit(1); } - fprintf(stderr, "SPIFFS: continuing without loading at user request, '%s' will be overwritten\n", m_storage.c_str()); + fprintf(stderr, + "SPIFFS: continuing without loading at user request, '%s' will be overwritten\n", + m_storage.c_str()); } else { fprintf(stderr, "SPIFFS: loading %zi bytes from '%s'\n", m_fs.size(), m_storage.c_str()); ssize_t r = ::read(fs, m_fs.data(), m_fs.size()); if (r != (ssize_t)m_fs.size()) - fprintf(stderr, "SPIFFS: reading %zi bytes: returned %zd: %s\n", m_fs.size(), r, strerror(errno)); + fprintf(stderr, "SPIFFS: reading %zi bytes: returned %zd: %s\n", m_fs.size(), r, + strerror(errno)); } ::close(fs); } -void SpiffsMock::save () +void SpiffsMock::save() { if (!m_fs.size() || !m_storage.length()) return; @@ -132,4 +134,3 @@ void SpiffsMock::save () } #pragma GCC diagnostic pop - diff --git a/tests/host/common/spiffs_mock.h b/tests/host/common/spiffs_mock.h index 4c265964f5..7c7bb4e034 100644 --- a/tests/host/common/spiffs_mock.h +++ b/tests/host/common/spiffs_mock.h @@ -1,14 +1,14 @@ /* spiffs_mock.h - SPIFFS HAL mock for host side testing 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. */ @@ -20,26 +20,29 @@ #include #include #include -#include "flash_hal_mock.h" +#include "flash_hal.h" #define DEFAULT_SPIFFS_FILE_NAME "spiffs.bin" -class SpiffsMock { +class SpiffsMock +{ public: - SpiffsMock(ssize_t fs_size, size_t fs_block, size_t fs_page, const String& storage = emptyString); + SpiffsMock(ssize_t fs_size, size_t fs_block, size_t fs_page, + const String& storage = emptyString); void reset(); ~SpiffsMock(); - + protected: - void load (); - void save (); + void load(); + void save(); std::vector m_fs; - String m_storage; - bool m_overwrite; + String m_storage; + bool m_overwrite; }; -#define SPIFFS_MOCK_DECLARE(size_kb, block_kb, page_b, storage) SpiffsMock spiffs_mock(size_kb * 1024, block_kb * 1024, page_b, storage) +#define SPIFFS_MOCK_DECLARE(size_kb, block_kb, page_b, storage) \ + SpiffsMock spiffs_mock(size_kb * 1024, block_kb * 1024, page_b, storage) #define SPIFFS_MOCK_RESET() spiffs_mock.reset() #endif /* spiffs_mock_hpp */ diff --git a/tests/host/common/strl.cpp b/tests/host/common/strl.cpp index 529b9a1edb..b01f6652a8 100644 --- a/tests/host/common/strl.cpp +++ b/tests/host/common/strl.cpp @@ -1,86 +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. + 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 */ - /* - Figure out how much room is left... - */ + // Figure out how much room is left... - dstlen = strlen(dst); - size -= dstlen + 1; + dstlen = strlen(dst); + size -= dstlen + 1; - if (!size) - { - return (dstlen); /* No room, return immediately... */ - } + if (!size) + { + return (dstlen); /* No room, return immediately... */ + } - /* - Figure out how much room is needed... - */ + // Figure out how much room is needed... - srclen = strlen(src); + srclen = strlen(src); - /* - Copy the appropriate amount... - */ + // Copy the appropriate amount... - if (srclen > size) - { - srclen = size; - } + if (srclen > size) + { + srclen = size; + } - memcpy(dst + dstlen, src, srclen); - dst[dstlen + srclen] = '\0'; + memcpy(dst + dstlen, src, srclen); + dst[dstlen + srclen] = '\0'; - return (dstlen + srclen); -} -#endif /* !HAVE_STRLCAT */ + return (dstlen + srclen); + } +#endif /* STRLCAT_MISSING */ -#ifndef HAVE_STRLCPY -/* - '_cups_strlcpy()' - Safely copy two strings. -*/ +#ifdef STRLCPY_MISSING + // '_cups_strlcpy()' - Safely copy two strings. -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_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 */ + // Figure out how much room is needed... - /* - Figure out how much room is needed... - */ + size--; - size --; + srclen = strlen(src); - srclen = strlen(src); + // Copy the appropriate amount... - /* - Copy the appropriate amount... - */ + if (srclen > size) + { + srclen = size; + } - if (srclen > size) - { - srclen = size; - } + memcpy(dst, src, srclen); + dst[srclen] = '\0'; - memcpy(dst, src, srclen); - dst[srclen] = '\0'; + return (srclen); + } +#endif /* STRLCPY_MISSING */ - return (srclen); -} -#endif /* !HAVE_STRLCPY */ +} // extern "C" diff --git a/tests/host/common/user_interface.cpp b/tests/host/common/user_interface.cpp index b3a302df18..26144aec1f 100644 --- a/tests/host/common/user_interface.cpp +++ b/tests/host/common/user_interface.cpp @@ -41,52 +41,8 @@ #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 #include @@ -120,24 +76,26 @@ extern "C" return 1; } - bool wifi_station_get_config(struct station_config *config) + bool wifi_station_get_config(struct station_config* config) { strcpy((char*)config->ssid, "emulated-ssid"); strcpy((char*)config->password, "emulated-ssid-password"); config->bssid_set = 0; for (int i = 0; i < 6; i++) config->bssid[i] = i; - config->threshold.rssi = 1; + 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; } - void wifi_fpm_close(void) - { - } + void wifi_fpm_close(void) { } sint8 wifi_fpm_do_sleep(uint32 sleep_time_in_us) { @@ -145,34 +103,30 @@ extern "C" return 1; } - void wifi_fpm_do_wakeup(void) - { - } + void wifi_fpm_do_wakeup(void) { } - void wifi_fpm_open(void) - { - } + void wifi_fpm_open(void) { } void wifi_fpm_set_sleep_type(sleep_type_t type) { (void)type; } - uint32_t global_ipv4_netfmt = 0; // global binding + uint32_t global_ipv4_netfmt = 0; // global binding - netif netif0; + netif netif0; uint32_t global_source_address = INADDR_ANY; - bool wifi_get_ip_info(uint8 if_index, struct ip_info *info) + bool wifi_get_ip_info(uint8 if_index, struct ip_info* info) { // emulate wifi_get_ip_info() // ignore if_index // use global option -i (host_interface) to select bound interface/address - struct ifaddrs * ifAddrStruct = NULL, * ifa = NULL; - uint32_t ipv4 = lwip_htonl(0x7f000001); - uint32_t mask = lwip_htonl(0xff000000); - global_source_address = INADDR_ANY; // =0 + struct ifaddrs *ifAddrStruct = NULL, *ifa = NULL; + uint32_t ipv4 = lwip_htonl(0x7f000001); + uint32_t mask = lwip_htonl(0xff000000); + global_source_address = INADDR_ANY; // =0 if (getifaddrs(&ifAddrStruct) != 0) { @@ -186,22 +140,23 @@ 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); + auto test_ipv4 + = lwip_ntohl(*(uint32_t*)&((struct sockaddr_in*)ifa->ifa_addr)->sin_addr); + mockverbose(" IPV4 (0x%08x)", test_ipv4); if ((test_ipv4 & 0xff000000) == 0x7f000000) // 127./8 mockverbose(" (local, ignored)"); else { - if (!host_interface || (host_interface && strcmp(ifa->ifa_name, host_interface) == 0)) + if (!host_interface + || (host_interface && strcmp(ifa->ifa_name, host_interface) == 0)) { - // use the first non-local interface, or, if specified, the one selected by user on cmdline - ipv4 = *(uint32_t*) & ((struct sockaddr_in*)ifa->ifa_addr)->sin_addr; - mask = *(uint32_t*) & ((struct sockaddr_in*)ifa->ifa_netmask)->sin_addr; + // use the first non-local interface, or, if specified, the one selected by + // user on cmdline + ipv4 = *(uint32_t*)&((struct sockaddr_in*)ifa->ifa_addr)->sin_addr; + mask = *(uint32_t*)&((struct sockaddr_in*)ifa->ifa_netmask)->sin_addr; mockverbose(" (selected)\n"); if (host_interface) global_source_address = ntohl(ipv4); @@ -216,7 +171,7 @@ extern "C" freeifaddrs(ifAddrStruct); (void)if_index; - //if (if_index != STATION_IF) + // if (if_index != STATION_IF) // fprintf(stderr, "we are not AP"); if (global_ipv4_netfmt == NO_GLOBAL_BINDING) @@ -224,17 +179,17 @@ extern "C" if (info) { - info->ip.addr = ipv4; + 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; + 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; + return true; } @@ -243,7 +198,7 @@ extern "C" return 1; } - bool wifi_get_macaddr(uint8 if_index, uint8 *macaddr) + bool wifi_get_macaddr(uint8 if_index, uint8* macaddr) { (void)if_index; macaddr[0] = 0xde; @@ -260,14 +215,14 @@ extern "C" return STATION_MODE; } -#ifdef NONOSDK3V0 +#if (NONOSDK >= (0x30000)) sleep_level_t wifi_get_sleep_level(void) { return MIN_SLEEP_T; } -#endif // nonos-sdk-pre-3 +#endif // nonos-sdk-pre-3 sleep_type_t wifi_get_sleep_type(void) { @@ -281,13 +236,13 @@ extern "C" } wifi_event_handler_cb_t wifi_event_handler_cb_emu = nullptr; - void wifi_set_event_handler_cb(wifi_event_handler_cb_t cb) + void wifi_set_event_handler_cb(wifi_event_handler_cb_t cb) { wifi_event_handler_cb_emu = cb; mockverbose("TODO: wifi_set_event_handler_cb set\n"); } - bool wifi_set_ip_info(uint8 if_index, struct ip_info *info) + bool wifi_set_ip_info(uint8 if_index, struct ip_info* info) { (void)if_index; (void)info; @@ -316,7 +271,7 @@ extern "C" return true; } -#ifdef NONOSDK3V0 +#if (NONOSDK >= (0x30000)) bool wifi_set_sleep_level(sleep_level_t level) { @@ -352,15 +307,17 @@ extern "C" return true; } - bool wifi_station_get_config_default(struct station_config *config) + bool wifi_station_get_config_default(struct station_config* config) { 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() @@ -378,19 +335,19 @@ extern "C" return set != 0; } - bool wifi_station_set_config(struct station_config *config) + bool wifi_station_set_config(struct station_config* config) { (void)config; return true; } - bool wifi_station_set_config_current(struct station_config *config) + bool wifi_station_set_config_current(struct station_config* config) { (void)config; return true; } - bool wifi_station_set_hostname(const char *name) + bool wifi_station_set_hostname(const char* name) { (void)name; return true; @@ -407,35 +364,20 @@ 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) + bool wifi_softap_get_config(struct softap_config* config) { strcpy((char*)config->ssid, "apssid"); strcpy((char*)config->password, "appasswd"); - config->ssid_len = strlen("appasswd"); - config->channel = 1; - config->authmode = AUTH_WPA2_PSK; - config->ssid_hidden = 0; - config->max_connection = 4; + config->ssid_len = strlen("appasswd"); + config->channel = 1; + config->authmode = AUTH_WPA2_PSK; + config->ssid_hidden = 0; + config->max_connection = 4; config->beacon_interval = 100; return true; } - bool wifi_softap_get_config_default(struct softap_config *config) + bool wifi_softap_get_config_default(struct softap_config* config) { return wifi_softap_get_config(config); } @@ -445,38 +387,19 @@ extern "C" return 2; } - bool wifi_softap_set_config(struct softap_config *config) + bool wifi_softap_set_config(struct softap_config* config) { (void)config; return true; } - bool wifi_softap_set_config_current(struct softap_config *config) + bool wifi_softap_set_config_current(struct softap_config* config) { (void)config; 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) + bool wifi_station_scan(struct scan_config* config, scan_done_cb_t cb) { (void)config; cb(nullptr, FAIL); @@ -498,28 +421,10 @@ extern "C" (void)intr; } - void esp_schedule(void) - { - } - - 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, ...) { - //XXXFIXME ... -> ptr + // XXXFIXME ... -> ptr cb(SC_STATUS_LINK, NULL); return true; } @@ -534,4 +439,4 @@ extern "C" return NONE_SLEEP_T; } -} // extern "C" +} // extern "C" diff --git a/tests/host/core/test_PolledTimeout.cpp b/tests/host/core/test_PolledTimeout.cpp index 37b31d8544..180a53d68e 100644 --- a/tests/host/core/test_PolledTimeout.cpp +++ b/tests/host/core/test_PolledTimeout.cpp @@ -2,245 +2,239 @@ #include "PolledTimeout.h" #define mockverbose printf -#include "common/MockEsp.cpp" // getCycleCount +#include "common/MockEsp.cpp" // getCycleCount -//This won't work for +// This won't work for template -inline bool -fuzzycomp(argT a, argT b) +inline bool fuzzycomp(argT a, argT b) { - const argT epsilon = 10; - return (std::max(a,b) - std::min(a,b) <= epsilon); + const argT epsilon = 10; + return (std::max(a, b) - std::min(a, b) <= epsilon); } TEST_CASE("OneShot Timeout 500000000ns (0.5s)", "[polledTimeout]") { - using esp8266::polledTimeout::oneShotFastNs; - using timeType = oneShotFastNs::timeType; - timeType before, after, delta; + using esp8266::polledTimeout::oneShotFastNs; + using timeType = oneShotFastNs::timeType; + timeType before, after, delta; - Serial.println("OneShot Timeout 500000000ns (0.5s)"); + Serial.println("OneShot Timeout 500000000ns (0.5s)"); - oneShotFastNs timeout(500000000); - before = micros(); - while(!timeout.expired()) - yield(); - after = micros(); + oneShotFastNs timeout(500000000); + before = micros(); + while (!timeout.expired()) + yield(); + after = micros(); - delta = after - before; - Serial.printf("delta = %u\n", delta); + delta = after - before; + Serial.printf("delta = %u\n", delta); - REQUIRE(fuzzycomp(delta/1000, (timeType)500)); + REQUIRE(fuzzycomp(delta / 1000, (timeType)500)); + Serial.print("reset\n"); - Serial.print("reset\n"); + timeout.reset(); + before = micros(); + while (!timeout) + yield(); + after = micros(); - timeout.reset(); - before = micros(); - while(!timeout) - yield(); - after = micros(); + delta = after - before; + Serial.printf("delta = %u\n", delta); - delta = after - before; - Serial.printf("delta = %u\n", delta); - - REQUIRE(fuzzycomp(delta/1000, (timeType)500)); + REQUIRE(fuzzycomp(delta / 1000, (timeType)500)); } TEST_CASE("OneShot Timeout 3000000us", "[polledTimeout]") { - using esp8266::polledTimeout::oneShotFastUs; - using timeType = oneShotFastUs::timeType; - timeType before, after, delta; - - Serial.println("OneShot Timeout 3000000us"); + using esp8266::polledTimeout::oneShotFastUs; + using timeType = oneShotFastUs::timeType; + timeType before, after, delta; - oneShotFastUs timeout(3000000); - before = micros(); - while(!timeout.expired()) - yield(); - after = micros(); + Serial.println("OneShot Timeout 3000000us"); - delta = after - before; - Serial.printf("delta = %u\n", delta); + oneShotFastUs timeout(3000000); + before = micros(); + while (!timeout.expired()) + yield(); + after = micros(); - REQUIRE(fuzzycomp(delta/1000, (timeType)3000)); + delta = after - before; + Serial.printf("delta = %u\n", delta); + REQUIRE(fuzzycomp(delta / 1000, (timeType)3000)); - Serial.print("reset\n"); + Serial.print("reset\n"); - timeout.reset(); - before = micros(); - while(!timeout) - yield(); - after = micros(); + timeout.reset(); + before = micros(); + while (!timeout) + yield(); + after = micros(); - delta = after - before; - Serial.printf("delta = %u\n", delta); + delta = after - before; + Serial.printf("delta = %u\n", delta); - REQUIRE(fuzzycomp(delta/1000, (timeType)3000)); + REQUIRE(fuzzycomp(delta / 1000, (timeType)3000)); } TEST_CASE("OneShot Timeout 3000ms", "[polledTimeout]") { - using esp8266::polledTimeout::oneShotMs; - using timeType = oneShotMs::timeType; - timeType before, after, delta; + using esp8266::polledTimeout::oneShotMs; + using timeType = oneShotMs::timeType; + timeType before, after, delta; - Serial.println("OneShot Timeout 3000ms"); + Serial.println("OneShot Timeout 3000ms"); - oneShotMs timeout(3000); - before = millis(); - while(!timeout.expired()) - yield(); - after = millis(); + oneShotMs timeout(3000); + before = millis(); + while (!timeout.expired()) + yield(); + after = millis(); - delta = after - before; - Serial.printf("delta = %lu\n", delta); + delta = after - before; + Serial.printf("delta = %lu\n", delta); - REQUIRE(fuzzycomp(delta, (timeType)3000)); + REQUIRE(fuzzycomp(delta, (timeType)3000)); + Serial.print("reset\n"); - Serial.print("reset\n"); + timeout.reset(); + before = millis(); + while (!timeout) + yield(); + after = millis(); - timeout.reset(); - before = millis(); - while(!timeout) - yield(); - after = millis(); + delta = after - before; + Serial.printf("delta = %lu\n", delta); - delta = after - before; - Serial.printf("delta = %lu\n", delta); - - REQUIRE(fuzzycomp(delta, (timeType)3000)); + REQUIRE(fuzzycomp(delta, (timeType)3000)); } TEST_CASE("OneShot Timeout 3000ms reset to 1000ms", "[polledTimeout]") { - using esp8266::polledTimeout::oneShotMs; - using timeType = oneShotMs::timeType; - timeType before, after, delta; - - Serial.println("OneShot Timeout 3000ms"); + using esp8266::polledTimeout::oneShotMs; + using timeType = oneShotMs::timeType; + timeType before, after, delta; - oneShotMs timeout(3000); - before = millis(); - while(!timeout.expired()) - yield(); - after = millis(); + Serial.println("OneShot Timeout 3000ms"); - delta = after - before; - Serial.printf("delta = %lu\n", delta); + oneShotMs timeout(3000); + before = millis(); + while (!timeout.expired()) + yield(); + after = millis(); - REQUIRE(fuzzycomp(delta, (timeType)3000)); + delta = after - before; + Serial.printf("delta = %lu\n", delta); + REQUIRE(fuzzycomp(delta, (timeType)3000)); - Serial.print("reset\n"); + Serial.print("reset\n"); - timeout.reset(1000); - before = millis(); - while(!timeout) - yield(); - after = millis(); + timeout.reset(1000); + before = millis(); + while (!timeout) + yield(); + after = millis(); - delta = after - before; - Serial.printf("delta = %lu\n", delta); + delta = after - before; + Serial.printf("delta = %lu\n", delta); - REQUIRE(fuzzycomp(delta, (timeType)1000)); + REQUIRE(fuzzycomp(delta, (timeType)1000)); } TEST_CASE("Periodic Timeout 1T 3000ms", "[polledTimeout]") { - using esp8266::polledTimeout::periodicMs; - using timeType = periodicMs::timeType; - timeType before, after, delta; + using esp8266::polledTimeout::periodicMs; + using timeType = periodicMs::timeType; + timeType before, after, delta; - Serial.println("Periodic Timeout 1T 3000ms"); + Serial.println("Periodic Timeout 1T 3000ms"); - periodicMs timeout(3000); - before = millis(); - while(!timeout) - yield(); - after = millis(); + periodicMs timeout(3000); + before = millis(); + while (!timeout) + yield(); + after = millis(); - delta = after - before; - Serial.printf("delta = %lu\n", delta); + delta = after - before; + Serial.printf("delta = %lu\n", delta); - REQUIRE(fuzzycomp(delta, (timeType)3000)); + REQUIRE(fuzzycomp(delta, (timeType)3000)); - Serial.print("no reset needed\n"); + Serial.print("no reset needed\n"); - before = millis(); - while(!timeout) - yield(); - after = millis(); + before = millis(); + while (!timeout) + yield(); + after = millis(); - delta = after - before; - Serial.printf("delta = %lu\n", delta); + delta = after - before; + Serial.printf("delta = %lu\n", delta); - REQUIRE(fuzzycomp(delta, (timeType)3000)); + REQUIRE(fuzzycomp(delta, (timeType)3000)); } TEST_CASE("Periodic Timeout 10T 1000ms", "[polledTimeout]") { - using esp8266::polledTimeout::periodicMs; - using timeType = periodicMs::timeType; - timeType before, after, delta; + using esp8266::polledTimeout::periodicMs; + using timeType = periodicMs::timeType; + timeType before, after, delta; - Serial.println("Periodic 10T Timeout 1000ms"); + Serial.println("Periodic 10T Timeout 1000ms"); - int counter = 10; + int counter = 10; - periodicMs timeout(1000); - before = millis(); - while(1) - { - if(timeout) + periodicMs timeout(1000); + before = millis(); + while (1) { - Serial.print("*"); - if(!--counter) - break; - yield(); + if (timeout) + { + Serial.print("*"); + if (!--counter) + break; + yield(); + } } - } - after = millis(); + after = millis(); - delta = after - before; - Serial.printf("\ndelta = %lu\n", delta); - REQUIRE(fuzzycomp(delta, (timeType)10000)); + delta = after - before; + Serial.printf("\ndelta = %lu\n", delta); + REQUIRE(fuzzycomp(delta, (timeType)10000)); } TEST_CASE("OneShot Timeout 3000ms reset to 1000ms custom yield", "[polledTimeout]") { - using YieldOrSkipPolicy = esp8266::polledTimeout::YieldPolicy::YieldOrSkip; - using oneShotMsYield = esp8266::polledTimeout::timeoutTemplate; - using timeType = oneShotMsYield::timeType; - timeType before, after, delta; + using YieldOrSkipPolicy = esp8266::polledTimeout::YieldPolicy::YieldOrSkip; + using oneShotMsYield = esp8266::polledTimeout::timeoutTemplate; + using timeType = oneShotMsYield::timeType; + timeType before, after, delta; - Serial.println("OneShot Timeout 3000ms"); + Serial.println("OneShot Timeout 3000ms"); + oneShotMsYield timeout(3000); + before = millis(); + while (!timeout.expired()) + ; + after = millis(); - oneShotMsYield timeout(3000); - before = millis(); - while(!timeout.expired()); - after = millis(); + delta = after - before; + Serial.printf("delta = %lu\n", delta); - delta = after - before; - Serial.printf("delta = %lu\n", delta); + REQUIRE(fuzzycomp(delta, (timeType)3000)); - REQUIRE(fuzzycomp(delta, (timeType)3000)); + Serial.print("reset\n"); + timeout.reset(1000); + before = millis(); + while (!timeout) + ; + after = millis(); - Serial.print("reset\n"); + delta = after - before; + Serial.printf("delta = %lu\n", delta); - timeout.reset(1000); - before = millis(); - while(!timeout); - after = millis(); - - delta = after - before; - Serial.printf("delta = %lu\n", delta); - - REQUIRE(fuzzycomp(delta, (timeType)1000)); + REQUIRE(fuzzycomp(delta, (timeType)1000)); } - diff --git a/tests/host/core/test_Print.cpp b/tests/host/core/test_Print.cpp index e58539c59e..9926756a24 100644 --- a/tests/host/core/test_Print.cpp +++ b/tests/host/core/test_Print.cpp @@ -27,19 +27,19 @@ TEST_CASE("Print::write overrides all compile properly", "[core][Print]") REQUIRE(LittleFS.begin()); auto p = LittleFS.open("test.bin", "w"); REQUIRE(p); - uint8_t uint8 = 1; - uint16_t uint16 = 2; - uint32_t uint32 = 3; - size_t size = 4; - int8_t int8 = 1; - int16_t int16 = 2; - int32_t int32 = 3; - char c = 'h'; - int i = 10; - long l = 11; - unsigned char uc = 20; - unsigned int ui = 21; - unsigned long ul = 22; + uint8_t uint8 = 1; + uint16_t uint16 = 2; + uint32_t uint32 = 3; + size_t size = 4; + int8_t int8 = 1; + int16_t int16 = 2; + int32_t int32 = 3; + char c = 'h'; + int i = 10; + long l = 11; + unsigned char uc = 20; + unsigned int ui = 21; + unsigned long ul = 22; p.write(uint8); p.write(uint16); p.write(uint32); @@ -60,7 +60,7 @@ TEST_CASE("Print::write overrides all compile properly", "[core][Print]") p = LittleFS.open("test.bin", "r"); REQUIRE(p); uint8_t buff[16]; - int len = p.read(buff, 16); + int len = p.read(buff, 16); p.close(); REQUIRE(len == 15); REQUIRE(buff[0] == 1); diff --git a/tests/host/core/test_Updater.cpp b/tests/host/core/test_Updater.cpp index f3b850e130..8a1ec92bd8 100644 --- a/tests/host/core/test_Updater.cpp +++ b/tests/host/core/test_Updater.cpp @@ -16,12 +16,11 @@ #include #include - // Use a SPIFFS file because we can't instantiate a virtual class like Print TEST_CASE("Updater fails when writes overflow requested size", "[core][Updater]") { - UpdaterClass *u; - uint8_t buff[6000]; + UpdaterClass* u; + uint8_t buff[6000]; memset(buff, 0, sizeof(buff)); u = new UpdaterClass(); REQUIRE(u->begin(6000)); diff --git a/tests/host/core/test_md5builder.cpp b/tests/host/core/test_md5builder.cpp index 0041c2eb72..b0a521cb8e 100644 --- a/tests/host/core/test_md5builder.cpp +++ b/tests/host/core/test_md5builder.cpp @@ -32,30 +32,35 @@ TEST_CASE("MD5Builder::add works as expected", "[core][MD5Builder]") REQUIRE(builder.toString() == "9edb67f2b22c604fab13e2fd1d6056d7"); } - TEST_CASE("MD5Builder::addHexString works as expected", "[core][MD5Builder]") { - WHEN("A char array is parsed"){ - MD5Builder builder; - builder.begin(); - const char * myPayload = "1234567890abcdeffedcba98765432106469676974616c7369676e61747572656170706c69636174696F6e73"; - builder.addHexString(myPayload); - builder.calculate(); - REQUIRE(builder.toString() == "47b937a6f9f12a4c389fa5854e023efb"); + WHEN("A char array is parsed") + { + MD5Builder builder; + builder.begin(); + const char* myPayload = "1234567890abcdeffedcba98765432106469676974616c7369676e617475726561" + "70706c69636174696F6e73"; + builder.addHexString(myPayload); + builder.calculate(); + REQUIRE(builder.toString() == "47b937a6f9f12a4c389fa5854e023efb"); } - WHEN("A Arduino String is parsed"){ - MD5Builder builder; - builder.begin(); - builder.addHexString(String("1234567890abcdeffedcba98765432106469676974616c7369676e61747572656170706c69636174696f6e73")); - builder.calculate(); - REQUIRE(builder.toString() == "47b937a6f9f12a4c389fa5854e023efb"); + WHEN("A Arduino String is parsed") + { + MD5Builder builder; + builder.begin(); + builder.addHexString(String("1234567890abcdeffedcba98765432106469676974616c7369676e61747572" + "656170706c69636174696f6e73")); + builder.calculate(); + REQUIRE(builder.toString() == "47b937a6f9f12a4c389fa5854e023efb"); } } -TEST_CASE("MD5Builder::addStream works", "[core][MD5Builder]"){ - MD5Builder builder; - const char* str = "MD5Builder::addStream_works_longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong"; +TEST_CASE("MD5Builder::addStream works", "[core][MD5Builder]") +{ + MD5Builder builder; + const char* str = "MD5Builder::addStream_works_" + "longlonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglonglong"; { StreamString stream; stream.print(str); diff --git a/tests/host/core/test_pgmspace.cpp b/tests/host/core/test_pgmspace.cpp index f44221c2b4..fd89dbe6db 100644 --- a/tests/host/core/test_pgmspace.cpp +++ b/tests/host/core/test_pgmspace.cpp @@ -19,15 +19,16 @@ TEST_CASE("strstr_P works as strstr", "[core][pgmspace]") { - auto t = [](const char* h, const char* n) { + auto t = [](const char* h, const char* n) + { const char* strstr_P_result = strstr_P(h, n); - const char* strstr_result = strstr(h, n); + const char* strstr_result = strstr(h, n); REQUIRE(strstr_P_result == strstr_result); }; // Test case data is from avr-libc, original copyright (c) 2007 Dmitry Xmelkov // See avr-libc/tests/simulate/pmstring/strstr_P.c - t ("", ""); + t("", ""); t("12345", ""); t("ababac", "abac"); t("", "a"); diff --git a/tests/host/core/test_string.cpp b/tests/host/core/test_string.cpp index 1150ba5913..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"; @@ -43,8 +45,8 @@ TEST_CASE("String::trim", "[core][String]") TEST_CASE("String::replace", "[core][String]") { String str; - str = "The quick brown fox jumped over the lazy dog."; - String find = "fox"; + str = "The quick brown fox jumped over the lazy dog."; + String find = "fox"; String replace = "vulpes vulpes"; str.replace(find, replace); REQUIRE(str == "The quick brown vulpes vulpes jumped over the lazy dog."); @@ -52,10 +54,10 @@ TEST_CASE("String::replace", "[core][String]") TEST_CASE("String(value, base)", "[core][String]") { - String strbase2(9999,2); - String strbase8(9999,8); - String strbase10(9999,10); - String strbase16(9999,16); + String strbase2(9999, 2); + String strbase8(9999, 8); + String strbase10(9999, 10); + String strbase16(9999, 16); REQUIRE(strbase2 == "10011100001111"); REQUIRE(strbase8 == "23417"); REQUIRE(strbase10 == "9999"); @@ -64,7 +66,7 @@ TEST_CASE("String(value, base)", "[core][String]") String strnegf(-2.123, 3); REQUIRE(strnegi == "-9999"); REQUIRE(strnegf == "-2.123"); - String strbase16l((long)999999,16); + String strbase16l((long)999999, 16); REQUIRE(strbase16l == "f423f"); } @@ -83,8 +85,8 @@ TEST_CASE("String constructors", "[core][String]") String s1("abcd"); String s2(s1); REQUIRE(s1 == s2); - String *s3 = new String("manos"); - s2 = *s3; + String* s3 = new String("manos"); + s2 = *s3; delete s3; REQUIRE(s2 == "manos"); s3 = new String("thisismuchlongerthantheother"); @@ -97,8 +99,8 @@ TEST_CASE("String constructors", "[core][String]") REQUIRE(ssh == "3.14159_abcd"); String flash = (F("hello from flash")); REQUIRE(flash == "hello from flash"); - const char textarray[6] = {'h', 'e', 'l', 'l', 'o', 0}; - String hello(textarray); + const char textarray[6] = { 'h', 'e', 'l', 'l', 'o', 0 }; + String hello(textarray); REQUIRE(hello == "hello"); String hello2; hello2 = textarray; @@ -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"); @@ -132,6 +136,24 @@ TEST_CASE("String concantenation", "[core][String]") REQUIRE(str == "abcdeabcde9872147483647-2147483648691969-123321-1.01"); str += (double)1.01; REQUIRE(str == "abcdeabcde9872147483647-2147483648691969-123321-1.011.01"); + str += LLONG_MIN; + REQUIRE(str == "abcdeabcde9872147483647-2147483648691969-123321-1.011.01-9223372036854775808"); + str += String(LLONG_MIN, 10); + REQUIRE(str + == "abcdeabcde9872147483647-2147483648691969-123321-1.011.01-9223372036854775808-" + "9223372036854775808"); + str += LLONG_MAX; + REQUIRE(str + == "abcdeabcde9872147483647-2147483648691969-123321-1.011.01-9223372036854775808-" + "92233720368547758089223372036854775807"); + str += ULLONG_MAX; + REQUIRE(str + == "abcdeabcde9872147483647-2147483648691969-123321-1.011.01-9223372036854775808-" + "9223372036854775808922337203685477580718446744073709551615"); + str += String(ULLONG_MAX, 16); + REQUIRE(str + == "abcdeabcde9872147483647-2147483648691969-123321-1.011.01-9223372036854775808-" + "9223372036854775808922337203685477580718446744073709551615ffffffffffffffff"); str = "clean"; REQUIRE(str.concat(str) == true); REQUIRE(str == "cleanclean"); @@ -146,19 +168,19 @@ TEST_CASE("String concantenation", "[core][String]") REQUIRE(str == "-100"); // Non-zero-terminated array concatenation const char buff[] = "abcdefg"; - String n; - n = "1234567890"; // Make it a SSO string, fill with non-0 data - n = "1"; // Overwrite [1] with 0, but leave old junk in SSO space still + String n; + n = "1234567890"; // Make it a SSO string, fill with non-0 data + n = "1"; // Overwrite [1] with 0, but leave old junk in SSO space still n.concat(buff, 3); - REQUIRE(n == "1abc"); // Ensure the trailing 0 is always present even w/this funky concat - for (int i=0; i<20; i++) - n.concat(buff, 1); // Add 20 'a's to go from SSO to normal string + REQUIRE(n == "1abc"); // Ensure the trailing 0 is always present even w/this funky concat + for (int i = 0; i < 20; i++) + n.concat(buff, 1); // Add 20 'a's to go from SSO to normal string REQUIRE(n == "1abcaaaaaaaaaaaaaaaaaaaa"); n = ""; - for (int i=0; i<=5; i++) + for (int i = 0; i <= 5; i++) n.concat(buff, i); REQUIRE(n == "aababcabcdabcde"); - n.concat(buff, 0); // And check no add'n + n.concat(buff, 0); // And check no add'n REQUIRE(n == "aababcabcdabcde"); } @@ -201,7 +223,7 @@ TEST_CASE("String byte access", "[core][String]") TEST_CASE("String conversion", "[core][String]") { String s = "12345"; - long l = s.toInt(); + long l = s.toInt(); REQUIRE(l == 12345); s = "2147483647"; l = s.toInt(); @@ -212,9 +234,9 @@ TEST_CASE("String conversion", "[core][String]") s = "-2147483648"; l = s.toInt(); REQUIRE(l == INT_MIN); - s = "3.14159"; + s = "3.14159"; float f = s.toFloat(); - REQUIRE( fabs(f - 3.14159) < 0.0001 ); + REQUIRE(fabs(f - 3.14159) < 0.0001); } TEST_CASE("String case", "[core][String]") @@ -236,7 +258,7 @@ TEST_CASE("String nulls", "[core][String]") s.trim(); s.toUpperCase(); s.toLowerCase(); - s.remove(1,1); + s.remove(1, 1); s.remove(10); s.replace("taco", "burrito"); s.replace('a', 'b'); @@ -258,7 +280,7 @@ TEST_CASE("String nulls", "[core][String]") REQUIRE(s == ""); REQUIRE(s.length() == 0); s.setCharAt(1, 't'); - REQUIRE(s.startsWith("abc",0) == false); + REQUIRE(s.startsWith("abc", 0) == false); REQUIRE(s.startsWith("def") == false); REQUIRE(s.equalsConstantTime("def") == false); REQUIRE(s.equalsConstantTime("") == true); @@ -289,125 +311,128 @@ TEST_CASE("String sizes near 8b", "[core][String]") String s15("12345678901234"); String s16("123456789012345"); String s17("1234567890123456"); - REQUIRE(!strcmp(s7.c_str(),"123456")); - REQUIRE(!strcmp(s8.c_str(),"1234567")); - REQUIRE(!strcmp(s9.c_str(),"12345678")); - REQUIRE(!strcmp(s15.c_str(),"12345678901234")); - REQUIRE(!strcmp(s16.c_str(),"123456789012345")); - REQUIRE(!strcmp(s17.c_str(),"1234567890123456")); + REQUIRE(!strcmp(s7.c_str(), "123456")); + REQUIRE(!strcmp(s8.c_str(), "1234567")); + REQUIRE(!strcmp(s9.c_str(), "12345678")); + REQUIRE(!strcmp(s15.c_str(), "12345678901234")); + REQUIRE(!strcmp(s16.c_str(), "123456789012345")); + REQUIRE(!strcmp(s17.c_str(), "1234567890123456")); s7 += '_'; s8 += '_'; s9 += '_'; s15 += '_'; s16 += '_'; s17 += '_'; - REQUIRE(!strcmp(s7.c_str(),"123456_")); - REQUIRE(!strcmp(s8.c_str(),"1234567_")); - REQUIRE(!strcmp(s9.c_str(),"12345678_")); - REQUIRE(!strcmp(s15.c_str(),"12345678901234_")); - REQUIRE(!strcmp(s16.c_str(),"123456789012345_")); - REQUIRE(!strcmp(s17.c_str(),"1234567890123456_")); + REQUIRE(!strcmp(s7.c_str(), "123456_")); + REQUIRE(!strcmp(s8.c_str(), "1234567_")); + REQUIRE(!strcmp(s9.c_str(), "12345678_")); + REQUIRE(!strcmp(s15.c_str(), "12345678901234_")); + REQUIRE(!strcmp(s16.c_str(), "123456789012345_")); + REQUIRE(!strcmp(s17.c_str(), "1234567890123456_")); } TEST_CASE("String SSO works", "[core][String]") { - // This test assumes that SSO_SIZE==8, if that changes the test must as well - String s; - s += "0"; - REQUIRE(s == "0"); - REQUIRE(s.length() == 1); - const char *savesso = s.c_str(); - s += 1; - REQUIRE(s.c_str() == savesso); - REQUIRE(s == "01"); - REQUIRE(s.length() == 2); - s += "2"; - REQUIRE(s.c_str() == savesso); - REQUIRE(s == "012"); - REQUIRE(s.length() == 3); - s += 3; - REQUIRE(s.c_str() == savesso); - REQUIRE(s == "0123"); - REQUIRE(s.length() == 4); - s += "4"; - REQUIRE(s.c_str() == savesso); - REQUIRE(s == "01234"); - REQUIRE(s.length() == 5); - s += "5"; - REQUIRE(s.c_str() == savesso); - REQUIRE(s == "012345"); - REQUIRE(s.length() == 6); - s += "6"; - REQUIRE(s.c_str() == savesso); - REQUIRE(s == "0123456"); - REQUIRE(s.length() == 7); - s += "7"; - REQUIRE(s.c_str() == savesso); - REQUIRE(s == "01234567"); - REQUIRE(s.length() == 8); - s += "8"; - REQUIRE(s.c_str() == savesso); - REQUIRE(s == "012345678"); - REQUIRE(s.length() == 9); - s += "9"; - REQUIRE(s.c_str() == savesso); - REQUIRE(s == "0123456789"); - REQUIRE(s.length() == 10); - if (sizeof(savesso) == 4) { - s += "a"; - REQUIRE(s.c_str() != savesso); - REQUIRE(s == "0123456789a"); - REQUIRE(s.length() == 11); - s += "b"; - REQUIRE(s.c_str() != savesso); - REQUIRE(s == "0123456789ab"); - REQUIRE(s.length() == 12); - s += "c"; - REQUIRE(s.c_str() != savesso); - REQUIRE(s == "0123456789abc"); - REQUIRE(s.length() == 13); - } else { - s += "a"; + // This test assumes that SSO_SIZE==8, if that changes the test must as well + String s; + s += "0"; + REQUIRE(s == "0"); + REQUIRE(s.length() == 1); + const char* savesso = s.c_str(); + s += 1; + REQUIRE(s.c_str() == savesso); + REQUIRE(s == "01"); + REQUIRE(s.length() == 2); + s += "2"; + REQUIRE(s.c_str() == savesso); + REQUIRE(s == "012"); + REQUIRE(s.length() == 3); + s += 3; + REQUIRE(s.c_str() == savesso); + REQUIRE(s == "0123"); + REQUIRE(s.length() == 4); + s += "4"; + REQUIRE(s.c_str() == savesso); + REQUIRE(s == "01234"); + REQUIRE(s.length() == 5); + s += "5"; REQUIRE(s.c_str() == savesso); - REQUIRE(s == "0123456789a"); - REQUIRE(s.length() == 11); - s += "bcde"; + REQUIRE(s == "012345"); + REQUIRE(s.length() == 6); + s += "6"; REQUIRE(s.c_str() == savesso); - REQUIRE(s == "0123456789abcde"); - REQUIRE(s.length() == 15); - s += "fghi"; - REQUIRE(s.c_str() != savesso); - REQUIRE(s == "0123456789abcdefghi"); - REQUIRE(s.length() == 19); - s += "j"; - REQUIRE(s.c_str() != savesso); - REQUIRE(s == "0123456789abcdefghij"); - REQUIRE(s.length() == 20); - s += "k"; - REQUIRE(s.c_str() != savesso); - REQUIRE(s == "0123456789abcdefghijk"); - REQUIRE(s.length() == 21); - s += "l"; - REQUIRE(s.c_str() != savesso); - REQUIRE(s == "0123456789abcdefghijkl"); - REQUIRE(s.length() == 22); - s += "m"; - REQUIRE(s.c_str() != savesso); - REQUIRE(s == "0123456789abcdefghijklm"); - REQUIRE(s.length() == 23); - s += "nopq"; - REQUIRE(s.c_str() != savesso); - REQUIRE(s == "0123456789abcdefghijklmnopq"); - REQUIRE(s.length() == 27); - s += "rstu"; - REQUIRE(s.c_str() != savesso); - REQUIRE(s == "0123456789abcdefghijklmnopqrstu"); - REQUIRE(s.length() == 31); - } - s = "0123456789abcde"; - s = s.substring(s.indexOf('a')); - REQUIRE(s == "abcde"); - REQUIRE(s.length() == 5); + REQUIRE(s == "0123456"); + REQUIRE(s.length() == 7); + s += "7"; + REQUIRE(s.c_str() == savesso); + REQUIRE(s == "01234567"); + REQUIRE(s.length() == 8); + s += "8"; + REQUIRE(s.c_str() == savesso); + REQUIRE(s == "012345678"); + REQUIRE(s.length() == 9); + s += "9"; + REQUIRE(s.c_str() == savesso); + REQUIRE(s == "0123456789"); + REQUIRE(s.length() == 10); + if (sizeof(savesso) == 4) + { + s += "a"; + REQUIRE(s.c_str() != savesso); + REQUIRE(s == "0123456789a"); + REQUIRE(s.length() == 11); + s += "b"; + REQUIRE(s.c_str() != savesso); + REQUIRE(s == "0123456789ab"); + REQUIRE(s.length() == 12); + s += "c"; + REQUIRE(s.c_str() != savesso); + REQUIRE(s == "0123456789abc"); + REQUIRE(s.length() == 13); + } + else + { + s += "a"; + REQUIRE(s.c_str() == savesso); + REQUIRE(s == "0123456789a"); + REQUIRE(s.length() == 11); + s += "bcde"; + REQUIRE(s.c_str() == savesso); + REQUIRE(s == "0123456789abcde"); + REQUIRE(s.length() == 15); + s += "fghi"; + REQUIRE(s.c_str() != savesso); + REQUIRE(s == "0123456789abcdefghi"); + REQUIRE(s.length() == 19); + s += "j"; + REQUIRE(s.c_str() != savesso); + REQUIRE(s == "0123456789abcdefghij"); + REQUIRE(s.length() == 20); + s += "k"; + REQUIRE(s.c_str() != savesso); + REQUIRE(s == "0123456789abcdefghijk"); + REQUIRE(s.length() == 21); + s += "l"; + REQUIRE(s.c_str() != savesso); + REQUIRE(s == "0123456789abcdefghijkl"); + REQUIRE(s.length() == 22); + s += "m"; + REQUIRE(s.c_str() != savesso); + REQUIRE(s == "0123456789abcdefghijklm"); + REQUIRE(s.length() == 23); + s += "nopq"; + REQUIRE(s.c_str() != savesso); + REQUIRE(s == "0123456789abcdefghijklmnopq"); + REQUIRE(s.length() == 27); + s += "rstu"; + REQUIRE(s.c_str() != savesso); + REQUIRE(s == "0123456789abcdefghijklmnopqrstu"); + REQUIRE(s.length() == 31); + } + s = "0123456789abcde"; + s = s.substring(s.indexOf('a')); + REQUIRE(s == "abcde"); + REQUIRE(s.length() == 5); } #include @@ -416,70 +441,67 @@ void repl(const String& key, const String& val, String& s, boolean useURLencode) s.replace(key, val); } - TEST_CASE("String SSO handles junk in memory", "[core][String]") { - // We fill the SSO space with garbage then construct an object in it and check consistency - // This is NOT how you want to use Strings outside of this testing! - unsigned char space[64]; - String *s = (String*)space; - memset(space, 0xff, 64); - new(s) String; - REQUIRE(*s == ""); - s->~String(); - - // Tests from #5883 - bool useURLencode = false; - const char euro[4] = {(char)0xe2, (char)0x82, (char)0xac, 0}; // Unicode euro symbol - const char yen[3] = {(char)0xc2, (char)0xa5, 0}; // Unicode yen symbol - - memset(space, 0xff, 64); - new(s) String("%ssid%"); - repl(("%ssid%"), "MikroTik", *s, useURLencode); - REQUIRE(*s == "MikroTik"); - s->~String(); - - memset(space, 0xff, 64); - new(s) String("{E}"); - repl(("{E}"), euro, *s, useURLencode); - REQUIRE(*s == "€"); - s->~String(); - memset(space, 0xff, 64); - new(s) String("€"); - repl(("€"), euro, *s, useURLencode); - REQUIRE(*s == "€"); - s->~String(); - memset(space, 0xff, 64); - new(s) String("{Y}"); - repl(("{Y}"), yen, *s, useURLencode); - REQUIRE(*s == "¥"); - s->~String(); - memset(space, 0xff, 64); - new(s) String("¥"); - repl(("¥"), yen, *s, useURLencode); - REQUIRE(*s == "¥"); - s->~String(); - - memset(space, 0xff, 64); - new(s) String("%sysname%"); - repl(("%sysname%"), "CO2_defect", *s, useURLencode); - REQUIRE(*s == "CO2_defect"); - s->~String(); -} + // We fill the SSO space with garbage then construct an object in it and check consistency + // This is NOT how you want to use Strings outside of this testing! + unsigned char space[64]; + String* s = (String*)space; + memset(space, 0xff, 64); + new (s) String; + REQUIRE(*s == ""); + s->~String(); + // Tests from #5883 + bool useURLencode = false; + const char euro[4] = { (char)0xe2, (char)0x82, (char)0xac, 0 }; // Unicode euro symbol + const char yen[3] = { (char)0xc2, (char)0xa5, 0 }; // Unicode yen symbol + + memset(space, 0xff, 64); + new (s) String("%ssid%"); + repl(("%ssid%"), "MikroTik", *s, useURLencode); + REQUIRE(*s == "MikroTik"); + s->~String(); + + memset(space, 0xff, 64); + new (s) String("{E}"); + repl(("{E}"), euro, *s, useURLencode); + REQUIRE(*s == "€"); + s->~String(); + memset(space, 0xff, 64); + new (s) String("€"); + repl(("€"), euro, *s, useURLencode); + REQUIRE(*s == "€"); + s->~String(); + memset(space, 0xff, 64); + new (s) String("{Y}"); + repl(("{Y}"), yen, *s, useURLencode); + REQUIRE(*s == "¥"); + s->~String(); + memset(space, 0xff, 64); + new (s) String("¥"); + repl(("¥"), yen, *s, useURLencode); + REQUIRE(*s == "¥"); + s->~String(); + + memset(space, 0xff, 64); + new (s) String("%sysname%"); + repl(("%sysname%"), "CO2_defect", *s, useURLencode); + REQUIRE(*s == "CO2_defect"); + s->~String(); +} TEST_CASE("Issue #5949 - Overlapping src/dest in replace", "[core][String]") { - String blah = "blah"; - blah.replace("xx", "y"); - REQUIRE(blah == "blah"); - blah.replace("x", "yy"); - REQUIRE(blah == "blah"); - blah.replace(blah, blah); - REQUIRE(blah == "blah"); + String blah = "blah"; + blah.replace("xx", "y"); + REQUIRE(blah == "blah"); + blah.replace("x", "yy"); + REQUIRE(blah == "blah"); + blah.replace(blah, blah); + REQUIRE(blah == "blah"); } - TEST_CASE("Issue #2736 - StreamString SSO fix", "[core][StreamString]") { StreamString s; @@ -492,56 +514,193 @@ TEST_CASE("Issue #2736 - StreamString SSO fix", "[core][StreamString]") TEST_CASE("Strings with NULs", "[core][String]") { - // The following should never be done in a real app! This is only to inject 0s in the middle of a string. - // Fits in SSO... - String str("01234567"); - REQUIRE(str.length() == 8); - char *ptr = (char *)str.c_str(); - ptr[3] = 0; - String str2; - str2 = str; - REQUIRE(str2.length() == 8); - // Needs a buffer pointer - str = "0123456789012345678901234567890123456789"; - ptr = (char *)str.c_str(); - ptr[3] = 0; - str2 = str; - REQUIRE(str2.length() == 40); - String str3("a"); - ptr = (char *)str3.c_str(); - *ptr = 0; - REQUIRE(str3.length() == 1); - str3 += str3; - REQUIRE(str3.length() == 2); - str3 += str3; - REQUIRE(str3.length() == 4); - str3 += str3; - REQUIRE(str3.length() == 8); - str3 += str3; - REQUIRE(str3.length() == 16); - str3 += str3; - REQUIRE(str3.length() == 32); - str3 += str3; - REQUIRE(str3.length() == 64); - static char zeros[64] = {0}; - const char *p = str3.c_str(); - REQUIRE(!memcmp(p, zeros, 64)); + // The following should never be done in a real app! This is only to inject 0s in the middle of + // a string. Fits in SSO... + String str("01234567"); + REQUIRE(str.length() == 8); + char* ptr = (char*)str.c_str(); + ptr[3] = 0; + String str2; + str2 = str; + REQUIRE(str2.length() == 8); + // Needs a buffer pointer + str = "0123456789012345678901234567890123456789"; + ptr = (char*)str.c_str(); + ptr[3] = 0; + str2 = str; + REQUIRE(str2.length() == 40); + String str3("a"); + ptr = (char*)str3.c_str(); + *ptr = 0; + REQUIRE(str3.length() == 1); + str3 += str3; + REQUIRE(str3.length() == 2); + str3 += str3; + REQUIRE(str3.length() == 4); + str3 += str3; + REQUIRE(str3.length() == 8); + str3 += str3; + REQUIRE(str3.length() == 16); + str3 += str3; + REQUIRE(str3.length() == 32); + str3 += str3; + REQUIRE(str3.length() == 64); + static char zeros[64] = { 0 }; + const char* p = str3.c_str(); + REQUIRE(!memcmp(p, zeros, 64)); } TEST_CASE("Replace and string expansion", "[core][String]") { - String s, l; - // Make these large enough to span SSO and non SSO - String whole = "#123456789012345678901234567890"; - const char *res = "abcde123456789012345678901234567890"; - for (size_t i=1; i < whole.length(); i++) { - s = whole.substring(0, i); - l = s; - l.replace("#", "abcde"); - char buff[64]; - strcpy(buff, res); - buff[5 + i-1] = 0; - REQUIRE(!strcmp(l.c_str(), buff)); - REQUIRE(l.length() == strlen(buff)); - } + String s, l; + // Make these large enough to span SSO and non SSO + String whole = "#123456789012345678901234567890"; + const char* res = "abcde123456789012345678901234567890"; + for (size_t i = 1; i < whole.length(); i++) + { + s = whole.substring(0, i); + l = s; + l.replace("#", "abcde"); + char buff[64]; + strcpy(buff, res); + buff[5 + i - 1] = 0; + REQUIRE(!strcmp(l.c_str(), buff)); + REQUIRE(l.length() == strlen(buff)); + } +} + +TEST_CASE("String chaining", "[core][String]") +{ + const char* chunks[] { "~12345", "67890", "qwertyuiopasdfghjkl", "zxcvbnm" }; + + String all; + for (auto* chunk : chunks) + { + all += chunk; + } + + // make sure we can chain a combination of things to form a String + REQUIRE((String(chunks[0]) + String(chunks[1]) + String(chunks[2]) + String(chunks[3])) == all); + REQUIRE((chunks[0] + String(chunks[1]) + F(chunks[2]) + chunks[3]) == all); + REQUIRE((String(chunks[0]) + F(chunks[1]) + F(chunks[2]) + String(chunks[3])) == all); + REQUIRE(('~' + String(&chunks[0][0] + 1) + chunks[1] + String(chunks[2]) + F(chunks[3])) + == all); + REQUIRE((String(chunks[0]) + '6' + (&chunks[1][0] + 1) + String(chunks[2]) + F(chunks[3])) + == all); + + // these are still invalid (and also cannot compile at all): + // - `F(...)` + `F(...)` + // - `F(...)` + `const char*` + // - `const char*` + `F(...)` + // we need `String()` as either rhs or lhs + + // ensure chaining reuses the buffer + // (internal details...) + { + String tmp(chunks[3]); + tmp.reserve(2 * all.length()); + auto* ptr = tmp.c_str(); + String result("~1" + String(&chunks[0][0] + 2) + F(chunks[1]) + chunks[2] + std::move(tmp)); + REQUIRE(result == all); + REQUIRE(static_cast(result.c_str()) == static_cast(ptr)); + } +} + +TEST_CASE("String concat OOB #8198", "[core][String]") +{ + char* p = (char*)malloc(16); + memset(p, 'x', 16); + String s = "abcd"; + s.concat(p, 16); + REQUIRE(!strcmp(s.c_str(), "abcdxxxxxxxxxxxxxxxx")); + free(p); +} + +TEST_CASE("String operator =(value) #8430", "[core][String]") +{ + // just like String(char), replace the string with a single char + { + String str { "123456789" }; + str = '\n'; + REQUIRE(str.length() == 1); + REQUIRE(str[0] == '\n'); + } + + // just like String(..., 10) where ... is a numeric type + // (base10 implicitly, since we don't expect an operator call with a 2nd argument) + { + String str { "99u3pokaposdas" }; + str = static_cast(123); + REQUIRE(str.length() == 3); + REQUIRE(str == "123"); + } + + { + String str { "adaj019j310923" }; + + unsigned int a { 8712373 }; + str = a; + REQUIRE(str.length() == 7); + REQUIRE(str == "8712373"); + + unsigned long b { 4231235 }; + str = b; + REQUIRE(str.length() == 7); + REQUIRE(str == "4231235"); + } + + { + String str { "123123124" }; + + int a { 123456 }; + str = a; + REQUIRE(str.length() == 6); + REQUIRE(str == "123456"); + + long b { 7654321 }; + str = b; + REQUIRE(str.length() == 7); + REQUIRE(str == "7654321"); + } + + { + String str { "adaj019j310923" }; + + long long a { 1234567890123456 }; + str = a; + REQUIRE(str.length() == 16); + REQUIRE(str == "1234567890123456"); + } + + { + String str { "lkojqwlekmas" }; + + unsigned long long a { 851238718912 }; + str = a; + REQUIRE(str.length() == 12); + REQUIRE(str == "851238718912"); + } + + // floating-point are specifically base10 + // expected to work like String(..., 2) + // + // may not be the best idea though, due to the dtostrf implementation + // and it's rounding logic may change at any point + { + String str { "qaje09`sjdsas" }; + + float a { 5.123 }; + str = a; + REQUIRE(str.length() == 4); + REQUIRE(str == "5.12"); + } + + { + String str { "9u1omasldmas" }; + + double a { 123.45 }; + str = a; + REQUIRE(str.length() == 6); + REQUIRE(str == "123.45"); + } } diff --git a/tests/host/fs/test_fs.cpp b/tests/host/fs/test_fs.cpp index fb73a6a271..900521b47e 100644 --- a/tests/host/fs/test_fs.cpp +++ b/tests/host/fs/test_fs.cpp @@ -24,8 +24,8 @@ #include "../../../libraries/SDFS/src/SDFS.h" #include "../../../libraries/SD/src/SD.h" - -namespace spiffs_test { +namespace spiffs_test +{ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" @@ -47,9 +47,9 @@ namespace spiffs_test { TEST_CASE("SPIFFS checks the config object passed in", "[fs]") { SPIFFS_MOCK_DECLARE(64, 8, 512, ""); - FSConfig f; - SPIFFSConfig s; - SDFSConfig d; + FSConfig f; + SPIFFSConfig s; + SDFSConfig d; LittleFSConfig l; REQUIRE_FALSE(SPIFFS.setConfig(f)); @@ -59,14 +59,15 @@ TEST_CASE("SPIFFS checks the config object passed in", "[fs]") } #pragma GCC diagnostic pop -}; - +}; // namespace spiffs_test -namespace littlefs_test { +namespace littlefs_test +{ #define FSTYPE LittleFS #define TESTPRE "LittleFS - " #define TESTPAT "[lfs]" -// LittleFS routines strip leading slashes before doing anything, so up to 31 char names are allowable +// LittleFS routines strip leading slashes before doing anything, so up to 31 char names are +// allowable #define TOOLONGFILENAME "/12345678901234567890123456789012" #define FS_MOCK_DECLARE LITTLEFS_MOCK_DECLARE #define FS_MOCK_RESET LITTLEFS_MOCK_RESET @@ -82,9 +83,9 @@ namespace littlefs_test { TEST_CASE("LittleFS checks the config object passed in", "[fs]") { LITTLEFS_MOCK_DECLARE(64, 8, 512, ""); - FSConfig f; - SPIFFSConfig s; - SDFSConfig d; + FSConfig f; + SPIFFSConfig s; + SDFSConfig d; LittleFSConfig l; REQUIRE_FALSE(LittleFS.setConfig(f)); @@ -93,17 +94,21 @@ TEST_CASE("LittleFS checks the config object passed in", "[fs]") REQUIRE(LittleFS.setConfig(l)); } -}; +}; // namespace littlefs_test -namespace sdfs_test { +namespace sdfs_test +{ #define FSTYPE SDFS #define TESTPRE "SDFS - " #define TESTPAT "[sdfs]" // SDFS supports long paths (MAXPATH) -#define TOOLONGFILENAME "/" \ - "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" \ - "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" \ - "12345678901234567890123456789012345678901234567890123456" +#define TOOLONGFILENAME \ + "/" \ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012" \ + "34567890" \ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012" \ + "34567890" \ + "12345678901234567890123456789012345678901234567890123456" #define FS_MOCK_DECLARE SDFS_MOCK_DECLARE #define FS_MOCK_RESET SDFS_MOCK_RESET #define FS_HAS_DIRS @@ -118,9 +123,9 @@ namespace sdfs_test { TEST_CASE("SDFS checks the config object passed in", "[fs]") { SDFS_MOCK_DECLARE(64, 8, 512, ""); - FSConfig f; - SPIFFSConfig s; - SDFSConfig d; + FSConfig f; + SPIFFSConfig s; + SDFSConfig d; LittleFSConfig l; REQUIRE_FALSE(SDFS.setConfig(f)); @@ -142,7 +147,7 @@ TEST_CASE("SD.h FILE_WRITE macro is append", "[fs]") f.write(65); f.write("bbcc"); f.write("theend", 6); - char block[3]={'x','y','z'}; + char block[3] = { 'x', 'y', 'z' }; f.write(block, 3); uint32_t bigone = 0x40404040; f.write((const uint8_t*)&bigone, 4); @@ -155,11 +160,11 @@ TEST_CASE("SD.h FILE_WRITE macro is append", "[fs]") File g = SD.open("/file2.txt", FILE_WRITE); g.write(0); g.close(); - g = SD.open("/file2.txt", FILE_READ); + g = SD.open("/file2.txt", FILE_READ); uint8_t u = 0x66; g.read(&u, 1); g.close(); REQUIRE(u == 0); } -}; +}; // namespace sdfs_test diff --git a/tests/host/fs/test_fs.inc b/tests/host/fs/test_fs.inc index b7e7692a46..2f7f96beaf 100644 --- a/tests/host/fs/test_fs.inc +++ b/tests/host/fs/test_fs.inc @@ -188,7 +188,7 @@ TEST_CASE(TESTPRE "peek() returns -1 on EOF", TESTPAT) f.close(); } -TEST_CASE(TESTPRE "seek() pase EOF returns error (#7323)", TESTPAT) +TEST_CASE(TESTPRE "seek() past EOF returns error (#7323)", TESTPAT) { FS_MOCK_DECLARE(64, 8, 512, ""); REQUIRE(FSTYPE.begin()); diff --git a/tests/host/sys/pgmspace.h b/tests/host/sys/pgmspace.h index 69a3b4fd9b..ac60cb15be 100644 --- a/tests/host/sys/pgmspace.h +++ b/tests/host/sys/pgmspace.h @@ -15,11 +15,11 @@ #endif #ifndef PGM_P -#define PGM_P const char * +#define PGM_P const char* #endif #ifndef PGM_VOID_P -#define PGM_VOID_P const void * +#define PGM_VOID_P const void* #endif #ifndef PSTR @@ -27,46 +27,61 @@ #endif #ifdef __cplusplus - #define pgm_read_byte(addr) (*reinterpret_cast(addr)) - #define pgm_read_word(addr) (*reinterpret_cast(addr)) - #define pgm_read_dword(addr) (*reinterpret_cast(addr)) - #define pgm_read_float(addr) (*reinterpret_cast(addr)) - #define pgm_read_ptr(addr) (*reinterpret_cast(addr)) +#define pgm_read_byte(addr) (*reinterpret_cast(addr)) +#define pgm_read_word(addr) (*reinterpret_cast(addr)) +#define pgm_read_dword(addr) (*reinterpret_cast(addr)) +#define pgm_read_float(addr) (*reinterpret_cast(addr)) +#define pgm_read_ptr(addr) (*reinterpret_cast(addr)) #else - #define pgm_read_byte(addr) (*(const uint8_t*)(addr)) - #define pgm_read_word(addr) (*(const uint16_t*)(addr)) - #define pgm_read_dword(addr) (*(const uint32_t*)(addr)) - #define pgm_read_float(addr) (*(const float)(addr)) - #define pgm_read_ptr(addr) (*(const void* const *)(addr)) +#define pgm_read_byte(addr) (*(const uint8_t*)(addr)) +#define pgm_read_word(addr) (*(const uint16_t*)(addr)) +#define pgm_read_dword(addr) (*(const uint32_t*)(addr)) +#define pgm_read_float(addr) (*(const float)(addr)) +#define pgm_read_ptr(addr) (*(const void* const*)(addr)) #endif -#define pgm_read_byte_near(addr) pgm_read_byte(addr) -#define pgm_read_word_near(addr) pgm_read_word(addr) -#define pgm_read_dword_near(addr) pgm_read_dword(addr) -#define pgm_read_float_near(addr) pgm_read_float(addr) -#define pgm_read_ptr_near(addr) pgm_read_ptr(addr) -#define pgm_read_byte_far(addr) pgm_read_byte(addr) -#define pgm_read_word_far(addr) pgm_read_word(addr) -#define pgm_read_dword_far(addr) pgm_read_dword(addr) -#define pgm_read_float_far(addr) pgm_read_float(addr) -#define pgm_read_ptr_far(addr) pgm_read_ptr(addr) +#define pgm_read_byte_near(addr) pgm_read_byte(addr) +#define pgm_read_word_near(addr) pgm_read_word(addr) +#define pgm_read_dword_near(addr) pgm_read_dword(addr) +#define pgm_read_float_near(addr) pgm_read_float(addr) +#define pgm_read_ptr_near(addr) pgm_read_ptr(addr) +#define pgm_read_byte_far(addr) pgm_read_byte(addr) +#define pgm_read_word_far(addr) pgm_read_word(addr) +#define pgm_read_dword_far(addr) pgm_read_dword(addr) +#define pgm_read_float_far(addr) pgm_read_float(addr) +#define pgm_read_ptr_far(addr) pgm_read_ptr(addr) // Wrapper inlines for _P functions #include #include -inline const char *strstr_P(const char *haystack, const char *needle) { return strstr(haystack, needle); } -inline char *strcpy_P(char *dest, const char *src) { return strcpy(dest, src); } -inline size_t strlen_P(const char *s) { return strlen(s); } -inline int vsnprintf_P(char *str, size_t size, const char *format, va_list ap) { return vsnprintf(str, size, format, ap); } +inline const char* strstr_P(const char* haystack, const char* needle) +{ + return strstr(haystack, needle); +} +inline char* strcpy_P(char* dest, const char* src) +{ + return strcpy(dest, src); +} +inline size_t strlen_P(const char* s) +{ + return strlen(s); +} +inline int vsnprintf_P(char* str, size_t size, const char* format, va_list ap) +{ + return vsnprintf(str, size, format, ap); +} #define memcpy_P memcpy #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-examples-only.sh b/tests/restyle-examples-only.sh deleted file mode 100755 index 513a891900..0000000000 --- a/tests/restyle-examples-only.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh - -set -e - -org=$(cd ${0%/*}; pwd) -cd ${org}/.. -pwd -test -d cores/esp8266 -test -d libraries - -# in a near future, restyle-all.sh will be renamed to restyle.sh -# and will be checked against CI - -for d in libraries; do - find $d -name "*.ino" -exec \ - astyle \ - --suffix=none \ - --options=${org}/astyle_examples.conf {} \; -done 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="append", required=True, choices=tuple(FILES_PRESETS.keys()) + ) + + files = fmt.add_parser("files") + files.add_argument("--config", type=pathlib.Path, required=True) + files.add_argument("files", type=pathlib.Path, nargs="+") + + assert_ = cmd.add_parser("assert") + assert_.set_defaults(func=run_assert) + assert_.add_argument("--with-diff", action="store_true") + assert_.add_argument("--with-errors", action="store_true") + assert_.add_argument("--with-summary", action="store_true") + + args = parser.parse_args() + args.func(args) diff --git a/tests/restyle.sh b/tests/restyle.sh index f2961d63b4..3bc90e52f0 100755 --- a/tests/restyle.sh +++ b/tests/restyle.sh @@ -1,47 +1,20 @@ #!/bin/sh +# requires python3, git, and runnable clang-format (specified below) -set -e +set -e -x -org=$(cd ${0%/*}; pwd) -cd ${org}/.. -pwd -test -d cores/esp8266 -test -d libraries +root=$(git rev-parse --show-toplevel) +test -d ${root}/cores/esp8266 +test -d ${root}/libraries -# should be: all="cores/esp8266 libraries" +# allow `env CLANG_FORMAT=clang-format-N`, or some other version +CLANG_FORMAT=${CLANG_FORMAT:-clang-format} -all=" -libraries/ESP8266mDNS -libraries/Wire -libraries/lwIP* -cores/esp8266/Lwip* -cores/esp8266/core_esp8266_si2c.cpp -libraries/Netdump -" +cd $root +python $root/tests/restyle.py format --clang-format=$CLANG_FORMAT preset --include core --include arduino -# core - -for d in $all; do - if [ -d "$d" ]; then - echo "-------- directory $d:" - for e in c cpp h; do - find $d -name "*.$e" -exec \ - astyle \ - --suffix=none \ - --options=${org}/astyle_core.conf {} \; - done - else - echo "-------- file $d:" - astyle --suffix=none --options=${org}/astyle_core.conf "$d" - fi -done - -# examples - -for d in libraries; do - echo "-------- examples in $d:" - find $d -name "*.ino" -exec \ - astyle \ - --suffix=none \ - --options=${org}/astyle_examples.conf {} \; -done +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 6d0a97093f..24d05041d6 100755 --- a/tests/run_CI_locally.sh +++ b/tests/run_CI_locally.sh @@ -82,40 +82,48 @@ 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/install_astyle.sh + tests/ci/style_check.sh + tests/restyle.sh else echo "BUILD_TYPE not set or invalid" 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 2fa6f5c97a..0ccfa8f4d9 100755 --- a/tools/boards.txt.py +++ b/tools/boards.txt.py @@ -79,13 +79,13 @@ ], 'desc': [ 'These modules come in different form factors and pinouts. See the page at ESP8266 community wiki for more info: `ESP8266 Module Family `__.', '', - 'Usually these modules have no bootstapping resistors on board, insufficient decoupling capacitors, no voltage regulator, no reset circuit, and no USB-serial adapter. This makes using them somewhat tricky, compared to development boards which add these features.', + 'Usually these modules have no bootstrapping resistors on board, insufficient decoupling capacitors, no voltage regulator, no reset circuit, and no USB-serial adapter. This makes using them somewhat tricky, compared to development boards which add these features.', '', 'In order to use these modules, make sure to observe the following:', '', '- **Provide sufficient power to the module.** For stable use of the ESP8266 a power supply with 3.3V and >= 250mA is required. Using the power available from USB to Serial adapter is not recommended, these adapters typically do not supply enough current to run ESP8266 reliably in every situation. An external supply or regulator alongwith filtering capacitors is preferred.', '', - '- **Connect bootstapping resistors** to GPIO0, GPIO2, GPIO15 according to the schematics below.', + '- **Connect bootstrapping resistors** to GPIO0, GPIO2, GPIO15 according to the schematics below.', '', '- **Put ESP8266 into bootloader mode** before uploading code.', '', @@ -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', { @@ -295,6 +304,28 @@ 'Note that since ESP8285 has SPI flash memory internally connected in DOUT mode, pins 9 and 10 may be used as GPIO / I2C / PWM pins.', ], }), + ( 'agruminolemon', { + 'name': 'Lifely Agrumino Lemon v4', + 'opts': collections.OrderedDict([ + ( '.build.board', 'ESP8266_AGRUMINO_LEMON_V4' ), + ( '.build.variant', 'agruminolemonv4' ), + ]), + 'macro': [ + 'resetmethod_nodemcu', + 'flashmode_dio', + 'flashfreq_40', + '2M', + ], + 'desc': [ 'Procuct page https://www.lifely.cc', + '', + 'This Board "Lifely Agrumino Lemon" is based with WT8266-S1 core with WiFi 2,4Ghz and 2MB of Flash.', + 'Power', + 'Micro usb power cable, Lir2450 rechargeable battery (or not rechargeable)or with JST connector in the back board Max 6 Vin', + 'Libraries and examples', + 'Download libraries from: Official Arduino Ide, our website https://www.lifely.cc or https://github.com/lifely-cc/', + 'Full pinout and PDF for setup here https://www.lifely.cc our libraries is OpenSource', + ], + }), ( 'espduino', { 'name': 'ESPDuino (ESP-13 Module)', 'opts': collections.OrderedDict([ @@ -320,7 +351,7 @@ ( 'huzzah', { 'name': 'Adafruit Feather HUZZAH ESP8266', 'opts': { - '.build.board': 'ESP8266_ESP12', + '.build.board': 'ESP8266_ADAFRUIT_HUZZAH', '.build.variant': 'adafruit', }, 'macro': [ @@ -334,10 +365,27 @@ 'Product page: https://www.adafruit.com/product/2821' ], }), + ( 'wifi_kit_8', { + 'name': 'WiFi Kit 8', + 'opts': { + '.build.board': 'wifi_kit_8', + '.build.variant': 'wifi_kit_8', + }, + 'macro': [ + 'resetmethod_nodemcu', + 'flashmode_dio', + 'flashfreq_40', + '4M', + ], + 'desc': [ 'The Heltec WiFi Kit 8 is an Arduino-compatible Wi-Fi development board powered by Ai-Thinker\'s ESP-12S, clocked at 80 MHz at 3.3V logic. A high-quality SiLabs CP2104 USB-Serial chip is included so that you can upload code at a blistering 921600 baud for fast development time. It also has auto-reset so no noodling with pins and reset button pressings. A 3.7V Lithium polymer battery connector is included, making it ideal for portable projects. The Heltec WiFi Kit 8 will automatically recharge a connected battery when USB power is available.', + '', + 'Product page: https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series' + ], + }), ( 'inventone', { 'name': 'Invent One', 'opts': { - '.build.board': 'ESP8266_GENERIC', + '.build.board': 'ESP8266_INVENT_ONE', '.build.variant': 'inventone', }, 'macro': [ @@ -354,7 +402,7 @@ ( 'cw01', { 'name': 'XinaBox CW01', 'opts': { - '.build.board': 'ESP8266_GENERIC', + '.build.board': 'ESP8266_XINABOX_CW01', '.build.variant': 'xinabox', }, 'macro': [ @@ -397,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': { @@ -428,7 +493,7 @@ ( 'nodemcu', { 'name': 'NodeMCU 0.9 (ESP-12 Module)', 'opts': { - '.build.board': 'ESP8266_NODEMCU', + '.build.board': 'ESP8266_NODEMCU_ESP12', '.build.variant': 'nodemcu', }, 'macro': [ @@ -462,7 +527,7 @@ ( 'nodemcuv2', { 'name': 'NodeMCU 1.0 (ESP-12E Module)', 'opts': { - '.build.board': 'ESP8266_NODEMCU', + '.build.board': 'ESP8266_NODEMCU_ESP12E', '.build.variant': 'nodemcu', }, 'macro': [ @@ -492,8 +557,9 @@ '.build.variant': 'modwifi', }, 'macro': [ - 'resetmethod_ck', - 'flashmode_qio', + 'resetmethod_menu', + 'resetmethod_menu_extra', + 'flashmode_menu', 'flashfreq_40', '2M', ], @@ -581,6 +647,40 @@ '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': { + '.build.board': 'ESP8266_WEMOS_D1MINI', + '.build.variant': 'd1_mini', + }, + 'macro': [ + 'resetmethod_nodemcu', + 'flashmode_menu', + 'flashfreq_menu', + '4M', + ], + 'serial': '921', + 'desc': [ 'Clone variant of the LOLIN(WEMOS) D1 mini board,', + 'with enabled flash-mode menu, DOUT selected by default.', + '', + 'Product page of the preferred official board: https://www.wemos.cc/', + ], + }), ( 'd1_mini_pro', { 'name': 'LOLIN(WEMOS) D1 mini Pro', 'opts': { @@ -652,7 +752,7 @@ ( 'espino', { 'name': 'ESPino (ESP-12 Module)', 'opts': { - '.build.board': 'ESP8266_ESP12', + '.build.board': 'ESP8266_ESPINO_ESP12', '.build.variant': 'espino', }, 'macro': [ @@ -671,7 +771,7 @@ ( 'espinotee', { 'name': 'ThaiEasyElec\'s ESPino', 'opts': { - '.build.board': 'ESP8266_ESP13', + '.build.board': 'ESP8266_ESPINO_ESP13', '.build.variant': 'espinotee', }, 'macro': [ @@ -682,14 +782,10 @@ ], 'desc': [ 'ESPino by ThaiEasyElec using WROOM-02 module from Espressif Systems with 4 MB Flash.', '', - 'We will update an English description soon. - Product page:', - 'http://thaieasyelec.com/products/wireless-modules/wifi-modules/espino-wifi-development-board-detail.html', - '- Schematics:', - 'www.thaieasyelec.com/downloads/ETEE052/ETEE052\_ESPino\_Schematic.pdf -', - 'Dimensions:', - 'http://thaieasyelec.com/downloads/ETEE052/ETEE052\_ESPino\_Dimension.pdf', - '- Pinouts:', - 'http://thaieasyelec.com/downloads/ETEE052/ETEE052\_ESPino\_User\_Manual\_TH\_v1\_0\_20160204.pdf (Please see pg. 8)', + '* Product page (retired product): https://www.thaieasyelec.com/product/%E0%B8%A2%E0%B8%81%E0%B9%80%E0%B8%A5%E0%B8%B4%E0%B8%81%E0%B8%88%E0%B8%B3%E0%B8%AB%E0%B8%99%E0%B9%88%E0%B8%B2%E0%B8%A2-retired-espino-wifi-development-board/11000833173001086', + '* Schematics: https://downloads.thaieasyelec.com/ETEE052/ETEE052\\_ESPino\\_Schematic.pdf', + '* Dimensions: https://downloads.thaieasyelec.com/ETEE052/ETEE052\\_ESPino\\_Dimension.pdf', + '* Pinouts (Please see pg.8): https://downloads.thaieasyelec.com/ETEE052/ETEE052\\_ESPino\\_User\\_Manual\\_TH\\_v1\\_0\\_20160204.pdf', ], }), ( 'wifinfo', { @@ -733,15 +829,15 @@ ( '.menu.BoardModel.primo', 'Primo' ), ( '.menu.BoardModel.primo.build.board', 'ESP8266_ARDUINO_PRIMO' ), ( '.menu.BoardModel.primo.build.variant', 'arduino_spi' ), - ( '.menu.BoardModel.primo.build.extra_flags', '-DF_CRYSTAL=40000000 -DESP8266' ), + ( '.menu.BoardModel.primo.build.extra_flags', '-DF_CRYSTAL=40000000' ), ( '.menu.BoardModel.unowifideved', 'Uno WiFi' ), ( '.menu.BoardModel.unowifideved.build.board', 'ESP8266_ARDUINO_UNOWIFI' ), ( '.menu.BoardModel.unowifideved.build.variant', 'arduino_uart' ), - ( '.menu.BoardModel.unowifideved.build.extra_flags=-DF_CRYSTAL', '40000000 -DESP8266' ), + ( '.menu.BoardModel.unowifideved.build.extra_flags=-DF_CRYSTAL', '40000000' ), ( '.menu.BoardModel.starottodeved', 'Star OTTO' ), ( '.menu.BoardModel.starottodeved.build.variant', 'arduino_uart' ), ( '.menu.BoardModel.starottodeved.build.board', 'ESP8266_ARDUINO_STAR_OTTO' ), - ( '.menu.BoardModel.starottodeved.build.extra_flags', '-DF_CRYSTAL=40000000 -DESP8266' ), + ( '.menu.BoardModel.starottodeved.build.extra_flags', '-DF_CRYSTAL=40000000' ), ]), 'macro': [ 'resetmethod_ck', @@ -890,7 +986,6 @@ 'opts': { '.build.board': 'ESP8266_SONOFF_SV', '.build.variant': 'itead', - '.build.extra_flags': '-DESP8266', '.build.flash_size': '1M', '.menu.BoardModel.sonoffSV': 'ITEAD Sonoff SV', '.menu.BoardModel.sonoffSV.build.board': 'ESP8266_SONOFF_SV', @@ -972,6 +1067,7 @@ ( '.build.core', 'esp8266' ), ( '.build.variant', 'generic' ), ( '.build.spiffs_pagesize', '256' ), + ( '.build.debug_optim', '' ), ( '.build.debug_port', '' ), ( '.build.debug_level', '' ), ]), @@ -1013,7 +1109,7 @@ 'crystalfreq_menu': collections.OrderedDict([ ( '.menu.CrystalFreq.26', '26 MHz' ), ( '.menu.CrystalFreq.40', '40 MHz' ), - ( '.menu.CrystalFreq.40.build.extra_flags', '-DF_CRYSTAL=40000000 -DESP8266' ), + ( '.menu.CrystalFreq.40.build.extra_flags', '-DF_CRYSTAL=40000000' ), ]), 'flashfreq_menu': collections.OrderedDict([ @@ -1027,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' ), ]), @@ -1211,6 +1311,10 @@ ( '.menu.mmu.4816H.build.mmuflags', '-DMMU_IRAM_SIZE=0xC000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_IRAM_HEAP' ), ( '.menu.mmu.3216', '16KB cache + 32KB IRAM + 16KB 2nd Heap (not shared)' ), ( '.menu.mmu.3216.build.mmuflags', '-DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x4000 -DMMU_SEC_HEAP=0x40108000 -DMMU_SEC_HEAP_SIZE=0x4000' ), + ( '.menu.mmu.ext128k', '128K Heap External 23LC1024' ), + ( '.menu.mmu.ext128k.build.mmuflags', '-DMMU_EXTERNAL_HEAP=128 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000' ), + ( '.menu.mmu.ext8192k', '8M w/256K Heap External 64 MBit PSRAM' ), + ( '.menu.mmu.ext8192k.build.mmuflags', '-DMMU_EXTERNAL_HEAP=256 -DMMU_IRAM_SIZE=0x8000 -DMMU_ICACHE_SIZE=0x8000' ), ]), ######################## Non 32-bit load/store exception handler @@ -1245,20 +1349,30 @@ def combn (lst): all += comb(i + 1, lst) return all -def comb1 (lst): +def comb1 (lst, lstplusone): all = [] for i in range(0, len(lst)): all += [ [ lst[i] ] ] - all += [ lst ] + if len(lstplusone): + for i in range(0, len(lstplusone)): + all += [ [ lstplusone[i] ] ] + all += [ lst ] + for i in range(0, len(lstplusone)): + all += [ lst + [ lstplusone[i] ] ] + else: + all += [ lst ] return all def all_debug (): listcomb = [ 'SSL', 'TLS_MEM', 'HTTP_CLIENT', 'HTTP_SERVER' ] listnocomb = [ 'CORE', 'WIFI', 'HTTP_UPDATE', 'UPDATER', 'OTA', 'OOM', 'MDNS' ] + listplusone = [ 'HWDT', 'HWDT_NOEXTRA4K' ] listsingle = [ 'NoAssert-NDEBUG' ] options = combn(listcomb) - options += comb1(listnocomb) + options += comb1(listnocomb, listplusone) options += [ listcomb + listnocomb ] + for i in range(0, len(listplusone)): + options += [ listcomb + listnocomb + [ listplusone[i] ] ] options += [ listsingle ] debugmenu = collections.OrderedDict([ ( '.menu.dbg.Disabled', 'Disabled' ), @@ -1269,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: @@ -1299,7 +1419,7 @@ def all_debug (): ################################################################ # flash size -def flash_map (flashsize_kb, fs_kb = 0): +def flash_map (flashsize_kb, fs_kb = 0, conf_name = ''): # mapping: # flash | reserved | empty | spiffs | eeprom | rf-cal | sdk-wifi-settings @@ -1330,7 +1450,7 @@ def flash_map (flashsize_kb, fs_kb = 0): fs_blocksize = 8192 # Adjust FS_end to be a multiple of the block size - fs_end = fs_blocksize * (int)((fs_end - fs_start)/fs_blocksize) + fs_start; + fs_end = fs_blocksize * (int)((fs_end - fs_start)/fs_blocksize) + fs_start max_ota_size = min(max_upload_size, fs_start / 2) # =(max_upload_size+empty_size)/2 strsize = str(int(flashsize_kb / 1024)) + 'M' if (flashsize_kb >= 1024) else str(flashsize_kb) + 'K' @@ -1344,12 +1464,13 @@ def flash_map (flashsize_kb, fs_kb = 0): d = collections.OrderedDict([ ( menu, strsize + 'B (FS:' + desc + ' OTA:~%iKB)' % (max_ota_size / 1024)), ( menub + 'flash_size', strsize ), - ( menub + 'flash_size_bytes', "0x%X" % (flashsize_kb * 1024)), + #( menub + 'flash_size_bytes', "0x%X" % (flashsize_kb * 1024)), ( menub + 'flash_ld', ld ), ( menub + 'spiffs_pagesize', '256' ), - ( menu + '.upload.maximum_size', "%i" % max_upload_size ), + #( menu + '.upload.maximum_size', "%i" % max_upload_size ), ( menub + 'rfcal_addr', "0x%X" % rfcal_addr) ]) + if fs_kb > 0: d.update(collections.OrderedDict([ ( menub + 'spiffs_start', "0x%05X" % fs_start ), @@ -1357,6 +1478,10 @@ def flash_map (flashsize_kb, fs_kb = 0): ( menub + 'spiffs_blocksize', "%i" % fs_blocksize ), ])) + #d.update(collections.OrderedDict([ + # ( menub + 'eeprom_start', "0x%05X" % eeprom_start ), + # ])) + if ldshow: if ldgen: @@ -1377,6 +1502,16 @@ def flash_map (flashsize_kb, fs_kb = 0): else: page = 0x100 + if not conf_name == '': + if not conf_name in c_flash_map: + c_flash_map[conf_name] = collections.OrderedDict([]) + c_flash_map[conf_name][flashsize_kb] = \ + '.eeprom_start = ' + hex(spi + eeprom_start) + ', ' \ + + '.fs_start = ' + hex(spi + fs_start) + ', ' \ + + '.fs_end = ' + hex(spi + fs_end) + ', ' \ + + '.fs_block_size = ' + hex(fs_blocksize)+ ', ' \ + + '.fs_page_size = ' + hex(page) + ', ' \ + print("/* Flash Split for %s chips */" % strsize) print("/* sketch @0x%X (~%dKB) (%dB) */" % (spi, (max_upload_size / 1024), max_upload_size)) empty_size = fs_start - max_upload_size @@ -1396,15 +1531,15 @@ def flash_map (flashsize_kb, fs_kb = 0): print(" irom0_0_seg : org = 0x40201010, len = 0x%x" % max_upload_size) print("}") print("") - print("PROVIDE ( _FS_start = 0x%08X );" % (0x40200000 + fs_start)) - print("PROVIDE ( _FS_end = 0x%08X );" % (0x40200000 + fs_end)) + print("PROVIDE ( _FS_start = 0x%08X );" % (spi + fs_start)) + print("PROVIDE ( _FS_end = 0x%08X );" % (spi + fs_end)) print("PROVIDE ( _FS_page = 0x%X );" % page) print("PROVIDE ( _FS_block = 0x%X );" % fs_blocksize) - print("PROVIDE ( _EEPROM_start = 0x%08x );" % (0x40200000 + eeprom_start)) + print("PROVIDE ( _EEPROM_start = 0x%08x );" % (spi + eeprom_start)) # Re-add deprecated symbols pointing to the same address as the new standard ones print("/* The following symbols are DEPRECATED and will be REMOVED in a future release */") - print("PROVIDE ( _SPIFFS_start = 0x%08X );" % (0x40200000 + fs_start)) - print("PROVIDE ( _SPIFFS_end = 0x%08X );" % (0x40200000 + fs_end)) + print("PROVIDE ( _SPIFFS_start = 0x%08X );" % (spi + fs_start)) + print("PROVIDE ( _SPIFFS_end = 0x%08X );" % (spi + fs_end)) print("PROVIDE ( _SPIFFS_page = 0x%X );" % page) print("PROVIDE ( _SPIFFS_block = 0x%X );" % fs_blocksize) print("") @@ -1425,44 +1560,101 @@ def all_flash_map (): f8m = collections.OrderedDict([]) f16m = collections.OrderedDict([]) - # flash(KB) spiffs(KB) + global c_flash_map + c_flash_map = collections.OrderedDict([]) - f1m.update( flash_map( 1024, 64 )) + # flash(KB) spiffs(KB) confname(C) + + f1m.update( flash_map( 1024, 64, 'OTA_FS' )) f1m.update( flash_map( 1024, 128 )) f1m.update( flash_map( 1024, 144 )) f1m.update( flash_map( 1024, 160 )) f1m.update( flash_map( 1024, 192 )) f1m.update( flash_map( 1024, 256 )) - f1m.update( flash_map( 1024, 512 )) - f1m.update( flash_map( 1024)) + f1m.update( flash_map( 1024, 512, 'MAX_FS' )) + f1m.update( flash_map( 1024, 0, 'NO_FS' )) f2m.update( flash_map( 2*1024, 64 )) f2m.update( flash_map( 2*1024, 128 )) - f2m.update( flash_map( 2*1024, 256 )) + f2m.update( flash_map( 2*1024, 256, 'OTA_FS' )) f2m.update( flash_map( 2*1024, 512 )) - f2m.update( flash_map( 2*1024, 1024 )) - f2m.update( flash_map( 2*1024)) + f2m.update( flash_map( 2*1024, 1024, 'MAX_FS' )) + f2m.update( flash_map( 2*1024, 0, 'NO_FS' )) - f4m.update( flash_map( 4*1024, 2*1024 )) - f4m.update( flash_map( 4*1024, 3*1024 )) + f4m.update( flash_map( 4*1024, 2*1024, 'OTA_FS' )) + f4m.update( flash_map( 4*1024, 3*1024, 'MAX_FS' )) f4m.update( flash_map( 4*1024, 1024 )) - f4m.update( flash_map( 4*1024)) + f4m.update( flash_map( 4*1024, 0, 'NO_FS' )) - f8m.update( flash_map( 8*1024, 6*1024 )) - f8m.update( flash_map( 8*1024, 7*1024 )) + f8m.update( flash_map( 8*1024, 6*1024, 'OTA_FS' )) + f8m.update( flash_map( 8*1024, 7*1024, 'MAX_FS' )) + f8m.update( flash_map( 8*1024, 0, 'NO_FS' )) - f16m.update(flash_map( 16*1024, 14*1024 )) - f16m.update(flash_map( 16*1024, 15*1024 )) + f16m.update(flash_map( 16*1024, 14*1024, 'OTA_FS' )) + f16m.update(flash_map( 16*1024, 15*1024, 'MAX_FS' )) + f16m.update(flash_map( 16*1024, 0, 'NO_FS' )) - f512.update(flash_map( 512, 32 )) + f512.update(flash_map( 512, 32, 'OTA_FS' )) f512.update(flash_map( 512, 64 )) - f512.update(flash_map( 512, 128 )) - f512.update(flash_map( 512)) + f512.update(flash_map( 512, 128, 'MAX_FS' )) + f512.update(flash_map( 512, 0, 'NO_FS' )) if ldgen: print("generated: ldscripts (in %s)" % lddir) + if ldshow: + if ldgen: + realstdout = sys.stdout + sys.stdout = open('cores/esp8266/FlashMap.h', 'w') + + define = '\n' + define += '// - do not edit - autogenerated by boards.txt.py\n' + define += '\n' + define += '#ifndef __FLASH_MAP_H\n' + define += '#define __FLASH_MAP_H\n' + define += '\n' + define += '#include \n' + define += '#include \n' + define += '\n' + define += 'typedef struct\n' + define += '{\n' + define += ' uint32_t eeprom_start;\n' + define += ' uint32_t fs_start;\n' + define += ' uint32_t fs_end;\n' + define += ' uint32_t fs_block_size;\n' + define += ' uint32_t fs_page_size;\n' + define += ' uint32_t flash_size_kb;\n' + define += '} flash_map_s;\n' + define += '\n' + define += '/*\n' + define += ' Following definitions map the above structure, one per line.\n' + define += ' FLASH_MAP_* is a user choice in sketch:\n' + define += ' `FLASH_MAP_SETUP_CONFIG(FLASH_MAP_OTA_FS)`\n' + define += ' Configuration is made at boot with detected flash chip size (last argument 512..16384)\n' + define += ' Other values are defined from `tools/boards.txt.py`.\n' + define += '*/\n' + for i in c_flash_map: + define += '\n#define FLASH_MAP_' + i + ' \\\n { \\\n' + for d in c_flash_map[i]: + define += ' { ' + c_flash_map[i][d] + '.flash_size_kb = ' + str(d) + ' }, \\\n' + define += ' }\n' + define += '\n#endif // __FLASH_MAP_H\n' + + print(define) + + if ldgen: + sys.stdout.close() + sys.stdout = realstdout + print("generated: flash map config file (in cores/esp8266/FlashMap.h)") + return { + '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, @@ -1478,7 +1670,7 @@ def led (name, default, ledList): led = collections.OrderedDict([ ('.menu.led.' + str(default), str(default)), ('.menu.led.' + str(default) + '.build.led', '-DLED_BUILTIN=' + str(default)), - ]); + ]) for i in ledList: # Make range incluside of max (16), since there are really 16 GPIOS not 15 if not i == default: led.update( @@ -1488,18 +1680,6 @@ def led (name, default, ledList): ])) return { name: led } -################################################################ -# Waveform flavour - -def waveform (): - return { 'waveform': collections.OrderedDict([ - ('.menu.waveform.pwm', 'Locked PWM'), - ('.menu.waveform.pwm.build.waveform', ''), - ('.menu.waveform.phase', 'Locked Phase'), - ('.menu.waveform.phase.build.waveform', '-DWAVEFORM_LOCKED_PHASE'), - ]) - } - ################################################################ # sdk selection @@ -1513,12 +1693,23 @@ def sdk (): ('.menu.sdk.nonosdk_191105.build.sdk', 'NONOSDK22x_191105'), ('.menu.sdk.nonosdk_191024', 'nonos-sdk 2.2.1+111 (191024)'), ('.menu.sdk.nonosdk_191024.build.sdk', 'NONOSDK22x_191024'), - # ('.menu.sdk.nonosdk_190313', 'nonos-sdk 2.2.1+61 (190313 testing)'), - # ('.menu.sdk.nonosdk_190313.build.sdk', 'NONOSDK22x_190313'), + ('.menu.sdk.nonosdk_190313', 'nonos-sdk 2.2.1+61 (190313)'), + ('.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'), ]) } @@ -1551,7 +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(waveform()) + macros.update(float_in_iram()) if boardfilteropt or excludeboards: print('#') @@ -1579,6 +1770,7 @@ def all_boards (): # With Arduino IDE 1.8.7 the order of the menu items will be honored from the tools pull down list. print('menu.BoardModel=Model') print('menu.ESPModule=Module') + print('menu.UploadTool=Upload Tool') print('menu.led=Builtin Led') print('menu.baud=Upload Speed') print('menu.xtal=CPU Frequency') @@ -1589,14 +1781,15 @@ 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=Exceptions') + 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.waveform=Waveform Flavour') print('menu.mmu=MMU') print('menu.non32xfer=Non-32-Bit Access') print('') @@ -1606,7 +1799,7 @@ def all_boards (): sortedrequiredfirst = requiredboards + [ item for item in boardlistsortedbydisplayedname if item not in requiredboards ] for id in sortedrequiredfirst: if id not in boards: - missingboards += [ id ]; + missingboards += [ id ] continue print('##############################################################') @@ -1619,7 +1812,7 @@ def all_boards (): print(id + optname + '=' + board['opts'][optname]) # macros - macrolist = [ 'defaults', 'cpufreq_menu', 'vtable_menu', 'exception_menu', 'stacksmash_menu', 'ssl_cipher_menu', 'waveform', 'mmu_menu', 'non32xfer_menu' ] + macrolist = [ 'defaults', 'cpufreq_menu', 'vtable_menu', 'exception_menu', 'stacksmash_menu', 'ssl_cipher_menu', 'mmu_menu', 'non32xfer_menu' ] if 'macro' in board: macrolist += board['macro'] macrolist += [ 'lwip', 'debug_menu', 'flash_erase_menu' ] @@ -1632,6 +1825,9 @@ def all_boards (): else: macrolist += speeds[default_speed] + macrolist += [ 'autoflash' ] + macrolist += [ 'iramfloat' ] + for block in macrolist: for optname in macros[block]: if not ('opts' in board) or not (optname in board['opts']): @@ -1683,7 +1879,7 @@ def package (): newfilestr = re.sub(r'"boards":[^\]]*\],', substitution, filestr, re.MULTILINE) - # To get consistent indent/formatting read the JSON and write it out programattically + # To get consistent indent/formatting read the JSON and write it out programmatically if packagegen: with open(pkgfname, 'w') as package_file: filejson = json.loads(newfilestr, object_pairs_hook=collections.OrderedDict) 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='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='append') - parser.add_argument('-d', '--hardware_dir', help='Additional hardware path', + parser.add_argument('-d', '--hardware_path', help='Additional hardware path', + action='append') + parser.add_argument('-l', '--library_path', help='Additional library path', action='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='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 new file mode 100755 index 0000000000..f319ef0f30 --- /dev/null +++ b/tools/cert.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 + +# Script to download/update certificates and public keys +# and generate compilable source files for c++/Arduino. +# released to public domain + +import urllib.request +import re +import ssl +import sys +import socket +import argparse +import datetime + +from cryptography import x509 +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.serialization import pkcs7 +from cryptography.hazmat.primitives.serialization import Encoding +from cryptography.hazmat.primitives.serialization import PublicFormat + +def printData(data, showPub = True): + try: + xcert = x509.load_der_x509_certificate(data) + except: + try: + xcert = x509.load_pem_x509_certificate(data) + except: + try: + xcert = pkcs7.load_der_pkcs7_certificates(data) + except: + xcert = pkcs7.load_pem_pkcs7_certificates(data) + if len(xcert) > 1: + print('// Warning: TODO: pkcs7 has {} entries'.format(len(xcert))) + xcert = xcert[0] + + cn = '' + for dn in xcert.subject.rfc4514_string().split(','): + keyval = dn.split('=') + if keyval[0] == 'CN': + cn += keyval[1] + name = re.sub('[^a-zA-Z0-9_]', '_', cn) + print('// CN: {} => name: {}'.format(cn, name)) + + print('// not valid before:', xcert.not_valid_before_utc) + print('// not valid after: ', xcert.not_valid_after_utc) + + if showPub: + + fingerprint = xcert.fingerprint(hashes.SHA1()).hex(':') + print('const char fingerprint_{} [] PROGMEM = "{}";'.format(name, fingerprint)) + + pem = xcert.public_key().public_bytes(Encoding.PEM, PublicFormat.SubjectPublicKeyInfo).decode('utf-8') + print('const char pubkey_{} [] PROGMEM = R"PUBKEY('.format(name)) + print(pem + ')PUBKEY";') + + else: + + cert = xcert.public_bytes(Encoding.PEM).decode('utf-8') + print('const char cert_{} [] PROGMEM = R"CERT('.format(name)) + print(cert + ')CERT";') + + cas = [] + for ext in xcert.extensions: + if ext.oid == x509.ObjectIdentifier("1.3.6.1.5.5.7.1.1"): + for desc in ext.value: + if desc.access_method == x509.oid.AuthorityInformationAccessOID.CA_ISSUERS: + cas.append(desc.access_location.value) + for ca in cas: + with urllib.request.urlopen(ca) as crt: + print() + print('// ' + ca) + printData(crt.read(), False) + print() + +def get_certificate(hostname, port, name): + context = ssl.create_default_context() + context.check_hostname = False + context.verify_mode = ssl.CERT_NONE + with socket.create_connection((hostname, port)) as sock: + with context.wrap_socket(sock, server_hostname=hostname) as ssock: + print('////////////////////////////////////////////////////////////') + print('// certificate chain for {}:{}'.format(hostname, port)) + print() + if name: + print('const char* {}_host = "{}";'.format(name, hostname)); + print('const uint16_t {}_port = {};'.format(name, port)); + print() + printData(ssock.getpeercert(binary_form=True)) + print('// end of certificate chain for {}:{}'.format(hostname, port)) + print('////////////////////////////////////////////////////////////') + print() + return 0 + +def main(): + parser = argparse.ArgumentParser(description='download certificate chain and public keys under a C++/Arduino compilable form') + parser.add_argument('-s', '--server', action='store', required=True, help='TLS server dns name') + parser.add_argument('-p', '--port', action='store', required=False, help='TLS server port') + parser.add_argument('-n', '--name', action='store', required=False, help='variable name') + port = 443 + args = parser.parse_args() + server = args.server + port = 443 + try: + split = server.split(':') + server = split[0] + port = int(split[1]) + except: + pass + try: + port = int(args.port) + except: + pass + + 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(datetime.UTC).strftime("%Y-%m-%d %H:%M:%S"))) + print('// by {}'.format(sys.argv)) + print() + print('#pragma once') + print() + return get_certificate(server, port, args.name) + +if __name__ == '__main__': + sys.exit(main()) diff --git a/tools/certsUpdate.sh b/tools/certsUpdate.sh new file mode 100755 index 0000000000..98f42bb62c --- /dev/null +++ b/tools/certsUpdate.sh @@ -0,0 +1,6 @@ + +# find `certUpdate` scripts in libraries, and execute them + +cd ${0%/*} 2>/dev/null +find ../libraries -name certUpdate -exec bash -c "echo 'updating {}...'; {};" \; +echo done 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='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): @@ -188,7 +233,7 @@ def wrapper(**kwargs): wrapper( elf=args.eboot, - segments=[".text"], + segments=[".text", ".rodata"], to_addr=4096 ) 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/makecorever.py b/tools/makecorever.py index 144466235a..d3f44f1742 100755 --- a/tools/makecorever.py +++ b/tools/makecorever.py @@ -22,21 +22,55 @@ import subprocess -def generate(path, platform_path, git_ver="ffffffff", git_desc="unspecified"): +def generate(path, platform_path, version="unspecified", release = False): def git(*args): cmd = ["git", "-C", platform_path] cmd.extend(args) proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, universal_newlines=True, stderr=subprocess.DEVNULL) return proc.stdout.readlines()[0].strip() + text = "" + + git_ver = "00000000" try: git_ver = git("rev-parse", "--short=8", "HEAD") + except Exception: + pass + text = "#define ARDUINO_ESP8266_GIT_VER 0x{}\n".format(git_ver) + + # version is + # - using Arduino-CLI: + # - blah-5.6.7 (official release, coming from platform.txt) + # - blah-5.6.7-dev (intermediate / unofficial / testing release) + # - using git: + # - 5.6.7 (from release script, official release) + # - 5.6.7-42-g00d1e5 (from release script, test release) + git_desc = version + try: + # in any case, get a better version when git is around git_desc = git("describe", "--tags") except Exception: pass - text = "#define ARDUINO_ESP8266_GIT_VER 0x{}\n".format(git_ver) - text += "#define ARDUINO_ESP8266_GIT_DESC {}\n".format(git_desc) + text += "#define ARDUINO_ESP8266_GIT_DESC {}\n".format(git_desc) + text += "#define ARDUINO_ESP8266_VERSION {}\n".format(version) + text += "\n" + + version_split = version.split(".") + # major: if present, skip "unix-" in "unix-3" + text += "#define ARDUINO_ESP8266_MAJOR {}\n".format(version_split[0].split("-")[-1]) + text += "#define ARDUINO_ESP8266_MINOR {}\n".format(version_split[1]) + # revision can be ".n" or ".n-dev" or ".n-42-g00d1e5" + revision = version_split[2].split("-") + text += "#define ARDUINO_ESP8266_REVISION {}\n".format(revision[0]) + text += "\n" + + # release or dev ? + if release: + text += "#define ARDUINO_ESP8266_RELEASE \"{}\"\n".format(git_desc) + text += "#define ARDUINO_ESP8266_RELEASE_{}\n".format(git_desc.replace("-","_").replace(".","_")) + else: + text += "#define ARDUINO_ESP8266_DEV 1 // development version\n" try: with open(path, "r") as inp: @@ -67,6 +101,7 @@ def git(*args): "-v", "--version", action="store", required=True, help="version variable" ) parser.add_argument("-i", "--include_dir", default="core") + parser.add_argument("-r", "--release", action="store_true", default=False) args = parser.parse_args() @@ -79,5 +114,6 @@ def git(*args): generate( os.path.join(include_dir, "core_version.h"), args.platform_path, - git_desc=args.version, + version=args.version, + release=args.release ) 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='store_true', required=False, default=False) + parser.add_argument('-DDEBUG_ESP_PORT', nargs='?', action='store', const="", default="", help='Add mkbuildoptglobals.extra_flags={build.debug_port} to platform.local.txt') + parser.add_argument('--ci', action='store_true', required=False, default=False) + group = parser.add_mutually_exclusive_group(required=False) + group.add_argument('--cache_core', action='store_true', default=None, help='Assume a "compiler.cache_core" value of true') + group.add_argument('--no_cache_core', dest='cache_core', action='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='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='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/mkdir.py b/tools/mkdir.py index 13fb26326e..c4e6756f76 100755 --- a/tools/mkdir.py +++ b/tools/mkdir.py @@ -12,7 +12,10 @@ def main(): parser.add_argument('dir', action='store', nargs='+') ns = parser.parse_args() for p in ns.dir: - pathlib.Path(p).mkdir(parents=ns.parents, exist_ok=True) + try: + pathlib.Path(p).mkdir(parents=ns.parents) + except FileExistsError: + pass return 0 if __name__ == '__main__': diff --git a/tools/platformio-build.py b/tools/platformio-build.py index 679d021a7f..20fe8c77fb 100644 --- a/tools/platformio-build.py +++ b/tools/platformio-build.py @@ -22,6 +22,9 @@ https://arduino.cc/en/Reference/HomePage """ +# For SCons documentation, see: +# https://scons.org/doc/latest + # Extends: https://github.com/platformio/platform-espressif8266/blob/develop/builder/main.py from os.path import isdir, join @@ -56,8 +59,15 @@ 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=[ "-std=gnu17", "-Wpointer-arith", @@ -67,6 +77,7 @@ def scons_patched_match_splitext(path, suffixes=None): "-nostdlib" ], + # General options that are passed to the C and C++ compilers CCFLAGS=[ "-Os", # optimize for size "-mlongcalls", @@ -75,15 +86,19 @@ def scons_patched_match_splitext(path, suffixes=None): "-U__STRICT_ANSI__", "-ffunction-sections", "-fdata-sections", - "-fno-exceptions", - "-Wall" + "-Wall", + "-Werror=return-type", + "-free", + "-fipa-pta" ], + # General options that are passed to the C++ compiler CXXFLAGS=[ "-fno-rtti", "-std=gnu++17" ], + # General user options passed to the linker LINKFLAGS=[ "-Os", "-nostdlib", @@ -102,36 +117,37 @@ def scons_patched_match_splitext(path, suffixes=None): "-u", "_UserExceptionVector" ], + # A platform independent specification of C preprocessor definitions as either: + # - -DFLAG as "FLAG" + # - -DFLAG=VALUE as ("FLAG", "VALUE") CPPDEFINES=[ ("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" ], + # The list of directories that the C preprocessor will search for include directories CPPPATH=[ join(FRAMEWORK_DIR, "tools", "sdk", "include"), - join(FRAMEWORK_DIR, "tools", "sdk", "libc", - "xtensa-lx106-elf", "include"), join(FRAMEWORK_DIR, "cores", env.BoardConfig().get("build.core")), join(platform.get_package_dir("toolchain-xtensa"), "include") ], + # The list of directories that will be searched for libraries LIBPATH=[ join("$BUILD_DIR", "ld"), # eagle.app.v6.common.ld join(FRAMEWORK_DIR, "tools", "sdk", "lib"), - join(FRAMEWORK_DIR, "tools", "sdk", "ld"), - join(FRAMEWORK_DIR, "tools", "sdk", "libc", "xtensa-lx106-elf", "lib") + join(FRAMEWORK_DIR, "tools", "sdk", "ld") ], - LIBS=[ - "hal", "phy", "pp", "net80211", "wpa", "crypto", "main", - "wps", "bearssl", "espnow", "smartconfig", "airkiss", "wpa2", - "stdc++", "m", "c", "gcc" - ], + # LIBS is set at the bottom of the builder script + # where we know about all system libraries to be included LIBSOURCE_DIRS=[ join(FRAMEWORK_DIR, "libraries") @@ -157,91 +173,81 @@ 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( CPPDEFINES=[("TCP_MSS", 536), ("LWIP_FEATURES", 1), ("LWIP_IPV6", 1)], CPPPATH=[join(FRAMEWORK_DIR, "tools", "sdk", "lwip2", "include")], - LIBS=["lwip6-536-feat"] ) + lwip_lib = "lwip6-536-feat" elif "PIO_FRAMEWORK_ARDUINO_LWIP2_IPV6_HIGHER_BANDWIDTH" in flatten_cppdefines: env.Append( CPPDEFINES=[("TCP_MSS", 1460), ("LWIP_FEATURES", 1), ("LWIP_IPV6", 1)], CPPPATH=[join(FRAMEWORK_DIR, "tools", "sdk", "lwip2", "include")], - LIBS=["lwip6-1460-feat"] ) + lwip_lib = "lwip6-1460-feat" elif "PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH" in flatten_cppdefines: env.Append( CPPDEFINES=[("TCP_MSS", 1460), ("LWIP_FEATURES", 1), ("LWIP_IPV6", 0)], CPPPATH=[join(FRAMEWORK_DIR, "tools", "sdk", "lwip2", "include")], - LIBS=["lwip2-1460-feat"] ) + lwip_lib = "lwip2-1460-feat" elif "PIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY_LOW_FLASH" in flatten_cppdefines: env.Append( CPPDEFINES=[("TCP_MSS", 536), ("LWIP_FEATURES", 0), ("LWIP_IPV6", 0)], CPPPATH=[join(FRAMEWORK_DIR, "tools", "sdk", "lwip2", "include")], - LIBS=["lwip2-536"] ) + lwip_lib = "lwip2-536" elif "PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH" in flatten_cppdefines: env.Append( CPPDEFINES=[("TCP_MSS", 1460), ("LWIP_FEATURES", 0), ("LWIP_IPV6", 0)], CPPPATH=[join(FRAMEWORK_DIR, "tools", "sdk", "lwip2", "include")], - LIBS=["lwip2-1460"] ) + lwip_lib = "lwip2-1460" # PIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY (default) else: env.Append( CPPDEFINES=[("TCP_MSS", 536), ("LWIP_FEATURES", 1), ("LWIP_IPV6", 0)], CPPPATH=[join(FRAMEWORK_DIR, "tools", "sdk", "lwip2", "include")], - LIBS=["lwip2-536-feat"] ) + lwip_lib = "lwip2-536-feat" # # Waveform @@ -250,43 +256,134 @@ def scons_patched_match_splitext(path, suffixes=None): env.Append(CPPDEFINES=[("WAVEFORM_LOCKED_PHASE", 1)]) # PIO_FRAMEWORK_ARDUINO_WAVEFORM_LOCKED_PWM will be used by default +# +# Exceptions +# +stdcpp_lib = None +if "PIO_FRAMEWORK_ARDUINO_ENABLE_EXCEPTIONS" in flatten_cppdefines: + env.Append( + CXXFLAGS=["-fexceptions"], + ) + stdcpp_lib = "stdc++-exc" +else: + env.Append( + CXXFLAGS=["-fno-exceptions"], + ) + stdcpp_lib = "stdc++" # # VTables # 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 -current_mmu_iram_size = None -for flag in env["CPPDEFINES"]: - try: - d, val = flag - if str(d).startswith("MMU_IRAM_SIZE"): - current_mmu_iram_size = "{}={}".format(d, val) - except ValueError: - continue -if not current_mmu_iram_size: - current_mmu_iram_size = "MMU_IRAM_SIZE=0x8000" - env.Append(CPPDEFINES=[current_mmu_iram_size]) -assert current_mmu_iram_size +if not current_fp: + current_fp = "FP_IN_IROM" + env.Append(CPPDEFINES=[current_fp]) +assert current_fp +# +# MMU +# + +mmu_flags = [] +required_flags = ("MMU_IRAM_SIZE", "MMU_ICACHE_SIZE") +if "PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48" in flatten_cppdefines: + mmu_flags = [("MMU_IRAM_SIZE", "0xC000"), ("MMU_ICACHE_SIZE", "0x4000")] +elif "PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48_SECHEAP_SHARED" in flatten_cppdefines: + mmu_flags = [ + ("MMU_IRAM_SIZE", "0xC000"), + ("MMU_ICACHE_SIZE", "0x4000"), + "MMU_IRAM_HEAP", + ] +elif "PIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM32_SECHEAP_NOTSHARED" in flatten_cppdefines: + mmu_flags = [ + ("MMU_IRAM_SIZE", "0x8000"), + ("MMU_ICACHE_SIZE", "0x4000"), + ("MMU_SEC_HEAP_SIZE", "0x4000"), + ("MMU_SEC_HEAP", "0x40108000"), + ] +elif "PIO_FRAMEWORK_ARDUINO_MMU_EXTERNAL_128K" in flatten_cppdefines: + mmu_flags = [ + ("MMU_IRAM_SIZE", "0x8000"), + ("MMU_ICACHE_SIZE", "0x8000"), + ("MMU_EXTERNAL_HEAP", "128"), + ] +elif "PIO_FRAMEWORK_ARDUINO_MMU_EXTERNAL_1024K" in flatten_cppdefines: + mmu_flags = [ + ("MMU_IRAM_SIZE", "0x8000"), + ("MMU_ICACHE_SIZE", "0x8000"), + ("MMU_EXTERNAL_HEAP", "256"), + ] +elif "PIO_FRAMEWORK_ARDUINO_MMU_CUSTOM" in flatten_cppdefines: + if not all(d in flatten_cppdefines for d in required_flags): + print( + "Error: Missing custom MMU configuration flags (%s)!" + % ", ".join(required_flags) + ) + env.Exit(1) + + for flag in env["CPPDEFINES"]: + define = flag + if isinstance(flag, (tuple, list)): + define, *_ = flag + if define.startswith("MMU_"): + mmu_flags.append(flag) +# PIO_FRAMEWORK_ARDUINO_MMU_CACHE32_IRAM32 (default) +else: + mmu_flags = [ + ("MMU_IRAM_SIZE", board.get("build.mmu_iram_size", "0x8000")), + ("MMU_ICACHE_SIZE", board.get("build.mmu_icache_size", "0x8000"))] + if any(f in flatten_cppdefines for f in required_flags): + print( + "Warning! Detected custom MMU flags. Please use the " + "`-D PIO_FRAMEWORK_ARDUINO_MMU_CUSTOM` option to disable " + "the default configuration." + ) + +assert mmu_flags +env.Append(CPPDEFINES=mmu_flags) + +# A list of one or more libraries that will be linked with any executable programs created by this environment +# We do this at this point so that we can put the libraries in their correct order more easily +env.Append( + LIBS=[ + "hal", "phy", "pp", "net80211", lwip_lib, "wpa", "crypto", "main", + "wps", "bearssl", "espnow", "smartconfig", "airkiss", "wpa2", + stdcpp_lib, "m", "c", "gcc" + ] +) # Build the eagle.app.v6.common.ld linker file app_ld = env.Command( 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 -D%s %s $SOURCE -o $TARGET" % (current_vtables, current_mmu_iram_size, fp_in_irom), - "Generating LD script $TARGET")) + "$CC -CC -E -P -D%s -D%s %s $SOURCE -o $TARGET" + % ( + current_vtables, + current_fp, + # String representation of MMU flags + " ".join( + [ + "-D%s=%s" % f if isinstance(f, (tuple, list)) else "-D" + f + for f in mmu_flags + ] + ), + ), + "Generating LD script $TARGET", + ), +) env.Depends("$BUILD_DIR/$PROGNAME$PROGSUFFIX", app_ld) if not env.BoardConfig().get("build.ldscript", ""): @@ -296,6 +393,7 @@ def scons_patched_match_splitext(path, suffixes=None): # Dynamic core_version.h for staging builds # + def platform_txt_version(default): with open(join(FRAMEWORK_DIR, "platform.txt"), "r") as platform_txt: for line in platform_txt: @@ -309,6 +407,7 @@ def platform_txt_version(default): return default + if isdir(join(FRAMEWORK_DIR, ".git")): cmd = '"$PYTHONEXE" "{script}" -b "$BUILD_DIR" -p "{framework_dir}" -v {version}' fmt = { 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 07f529ba7f..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 28bebad +#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/c_types.h b/tools/sdk/include/c_types.h index 88dc147213..0bb7c6df06 100644 --- a/tools/sdk/include/c_types.h +++ b/tools/sdk/include/c_types.h @@ -86,15 +86,15 @@ typedef enum { #ifdef ICACHE_FLASH #define __ICACHE_STRINGIZE_NX(A) #A #define __ICACHE_STRINGIZE(A) __ICACHE_STRINGIZE_NX(A) -#define ICACHE_FLASH_ATTR __attribute__((section("\".irom0.text." __FILE__ "." __ICACHE_STRINGIZE(__LINE__) "." __ICACHE_STRINGIZE(__COUNTER__) "\""))) -#define ICACHE_RAM_ATTR __attribute__((section("\".iram.text." __FILE__ "." __ICACHE_STRINGIZE(__LINE__) "." __ICACHE_STRINGIZE(__COUNTER__) "\""))) +#define ICACHE_FLASH_ATTR __attribute__((section("\".irom0.text." __FILE__ "." __ICACHE_STRINGIZE(__LINE__) "." __ICACHE_STRINGIZE(__COUNTER__) "\""))) +#define IRAM_ATTR __attribute__((section("\".iram.text." __FILE__ "." __ICACHE_STRINGIZE(__LINE__) "." __ICACHE_STRINGIZE(__COUNTER__) "\""))) #else #define ICACHE_FLASH_ATTR -#define ICACHE_RAM_ATTR +#define IRAM_ATTR #endif /* ICACHE_FLASH */ // counterpart https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp8266-compat.h -#define IRAM_ATTR ICACHE_RAM_ATTR +#define ICACHE_RAM_ATTR IRAM_ATTR __attribute__((deprecated("Use IRAM_ATTR in place of ICACHE_RAM_ATTR to move functions into IRAM"))) #define STORE_ATTR __attribute__((aligned(4))) 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 c51de98408..e6b415c50b 100644 --- a/tools/sdk/ld/eagle.app.v6.common.ld.h +++ b/tools/sdk/ld/eagle.app.v6.common.ld.h @@ -138,23 +138,58 @@ 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) { _irom0_text_start = ABSOLUTE(.); + + /* Stuff the CRC in well known symbols at a well known location */ + __crc_len = ABSOLUTE(.); + LONG(0x00000000); + __crc_val = ABSOLUTE(.); + 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.*) @@ -213,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/ld/eagle.flash.auto.ld b/tools/sdk/ld/eagle.flash.auto.ld new file mode 100644 index 0000000000..8c6d3b1060 --- /dev/null +++ b/tools/sdk/ld/eagle.flash.auto.ld @@ -0,0 +1,11 @@ +/* Flash Split */ +/* sketch @0x40200000 (~1019KB) (1044464B) = MAX */ + +MEMORY +{ + dport0_0_seg : org = 0x3FF00000, len = 0x10 + dram0_0_seg : org = 0x3FFE8000, len = 0x14000 + irom0_0_seg : org = 0x40201010, len = 0xfeff0 +} + +INCLUDE "local.eagle.app.v6.common.ld" diff --git a/tools/sdk/ld/eagle.rom.addr.v6.ld b/tools/sdk/ld/eagle.rom.addr.v6.ld index e2cff957a9..005dc44723 100644 --- a/tools/sdk/ld/eagle.rom.addr.v6.ld +++ b/tools/sdk/ld/eagle.rom.addr.v6.ld @@ -99,6 +99,7 @@ PROVIDE ( aes_decrypt_init = 0x40008ea4 ); PROVIDE ( aes_unwrap = 0x40009410 ); PROVIDE ( base64_decode = 0x40009648 ); PROVIDE ( base64_encode = 0x400094fc ); +PROVIDE ( boot_from_flash = 0x40001308 ); PROVIDE ( bzero = 0x4000de84 ); PROVIDE ( cmd_parse = 0x40000814 ); PROVIDE ( conv_str_decimal = 0x40000b24 ); @@ -278,6 +279,7 @@ PROVIDE ( rom_stop_tx_tone = 0x4000698c ); PROVIDE ( rom_tx_mac_disable = 0x40006a98 ); PROVIDE ( rom_tx_mac_enable = 0x40006ad4 ); PROVIDE ( rom_txtone_linear_pwr = 0x40006a1c ); +PROVIDE ( rom_uart_div_modify = 0x400039d8 ); PROVIDE ( rom_write_rfpll_sdm = 0x400078dc ); PROVIDE ( roundup2 = 0x400031b4 ); PROVIDE ( rtc_enter_sleep = 0x40002870 ); @@ -353,5 +355,6 @@ PROVIDE ( Te0 = 0x3fffccf0 ); PROVIDE ( Td0 = 0x3fffd100 ); PROVIDE ( Td4s = 0x3fffd500); PROVIDE ( rcons = 0x3fffd0f0); +PROVIDE ( user_start_fptr = 0x3fffdcd0 ); PROVIDE ( UartDev = 0x3fffde10 ); PROVIDE ( flashchip = 0x3fffc714); diff --git a/tools/sdk/lib/NONOSDK221/libmain.a b/tools/sdk/lib/NONOSDK221/libmain.a index c0e70cafef..adf747690b 100644 Binary files a/tools/sdk/lib/NONOSDK221/libmain.a and b/tools/sdk/lib/NONOSDK221/libmain.a differ 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/libmain.a b/tools/sdk/lib/NONOSDK22x_190313/libmain.a index a19b330c10..6640310829 100644 Binary files a/tools/sdk/lib/NONOSDK22x_190313/libmain.a and b/tools/sdk/lib/NONOSDK22x_190313/libmain.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/libmain.a b/tools/sdk/lib/NONOSDK22x_190703/libmain.a index 1213ceb8fd..cd5fd1e845 100644 Binary files a/tools/sdk/lib/NONOSDK22x_190703/libmain.a and b/tools/sdk/lib/NONOSDK22x_190703/libmain.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/libmain.a b/tools/sdk/lib/NONOSDK22x_191024/libmain.a index 58779578ad..cd01e1a365 100644 Binary files a/tools/sdk/lib/NONOSDK22x_191024/libmain.a and b/tools/sdk/lib/NONOSDK22x_191024/libmain.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/libmain.a b/tools/sdk/lib/NONOSDK22x_191105/libmain.a index 2bc9857933..0f66ab6b02 100644 Binary files a/tools/sdk/lib/NONOSDK22x_191105/libmain.a and b/tools/sdk/lib/NONOSDK22x_191105/libmain.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/libmain.a b/tools/sdk/lib/NONOSDK22x_191122/libmain.a index 77b7de1b96..260be180be 100644 Binary files a/tools/sdk/lib/NONOSDK22x_191122/libmain.a and b/tools/sdk/lib/NONOSDK22x_191122/libmain.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 e93201c520..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 178f08bd2f..d024b46a08 100755 --- a/tools/sdk/lib/fix_sdk_libs.sh +++ b/tools/sdk/lib/fix_sdk_libs.sh @@ -1,18 +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() { + 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 -xtensa-lx106-elf-ar r libmain.a eagle_lwip_if.o user_interface.o -rm -f eagle_lwip_if.o user_interface.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" + 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 + +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 a169456a4b..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 5528ff863a..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 077150b28c..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 a0f62e0d42..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 fe232ac23b..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 eef7e02425..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 0ca9133d44..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 b2373eb227..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 68f3a096ac..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 1c7645c78a..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 7421258237..4087efd9d2 160000 --- a/tools/sdk/lwip2/builder +++ b/tools/sdk/lwip2/builder @@ -1 +1 @@ -Subproject commit 7421258237b7c8f61629226961af498a0a6e0096 +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/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 146c0d3110..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_2_RELEASE/glue:1.2-48-g7421258" +#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/debug.h b/tools/sdk/lwip2/include/lwip/debug.h index baa6a40901..579fd242b2 100644 --- a/tools/sdk/lwip2/include/lwip/debug.h +++ b/tools/sdk/lwip2/include/lwip/debug.h @@ -120,9 +120,7 @@ #endif /* LWIP_NOASSERT */ #ifndef LWIP_ERROR -#ifndef LWIP_NOASSERT -#define LWIP_PLATFORM_ERROR(message) LWIP_PLATFORM_ASSERT(message) -#elif defined LWIP_DEBUG +#ifdef LWIP_DEBUG #define LWIP_PLATFORM_ERROR(message) LWIP_PLATFORM_DIAG((message)) #else #define LWIP_PLATFORM_ERROR(message) diff --git a/tools/sdk/lwip2/include/lwip/if_api.h b/tools/sdk/lwip2/include/lwip/if_api.h index 39017abd32..b7269e2a62 100644 --- a/tools/sdk/lwip2/include/lwip/if_api.h +++ b/tools/sdk/lwip2/include/lwip/if_api.h @@ -49,7 +49,9 @@ extern "C" { #endif +#ifndef IF_NAMESIZE #define IF_NAMESIZE NETIF_NAMESIZE +#endif char * lwip_if_indextoname(unsigned int ifindex, char *ifname); unsigned int lwip_if_nametoindex(const char *ifname); diff --git a/tools/sdk/lwip2/include/lwip/init.h b/tools/sdk/lwip2/include/lwip/init.h index a149be1848..6cabfc8fd4 100644 --- a/tools/sdk/lwip2/include/lwip/init.h +++ b/tools/sdk/lwip2/include/lwip/init.h @@ -54,7 +54,7 @@ extern "C" { /** x.X.x: Minor version of the stack */ #define LWIP_VERSION_MINOR 1 /** x.x.X: Revision of the stack */ -#define LWIP_VERSION_REVISION 2 +#define LWIP_VERSION_REVISION 3 /** For release candidates, this is set to 1..254 * For official releases, this is set to 255 (LWIP_RC_RELEASE) * For development versions (Git), this is set to 0 (LWIP_RC_DEVELOPMENT) */ diff --git a/tools/sdk/lwip2/include/lwip/netif.h b/tools/sdk/lwip2/include/lwip/netif.h index c29a987610..89ab85d256 100644 --- a/tools/sdk/lwip2/include/lwip/netif.h +++ b/tools/sdk/lwip2/include/lwip/netif.h @@ -386,6 +386,10 @@ struct netif { #if LWIP_LOOPBACK_MAX_PBUFS u16_t loop_cnt_current; #endif /* LWIP_LOOPBACK_MAX_PBUFS */ +#if LWIP_NETIF_LOOPBACK_MULTITHREADING + /* Used if the original scheduling failed. */ + u8_t reschedule_poll; +#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */ #endif /* ENABLE_LOOPBACK */ #if LWIP_IPV4 && IP_NAPT u8_t napt; @@ -454,7 +458,7 @@ void netif_set_gw(struct netif *netif, const ip4_addr_t *gw); #define netif_set_flags(netif, set_flags) do { (netif)->flags = (u8_t)((netif)->flags | (set_flags)); } while(0) #define netif_clear_flags(netif, clr_flags) do { (netif)->flags = (u8_t)((netif)->flags & (u8_t)(~(clr_flags) & 0xff)); } while(0) -#define netif_is_flag_set(nefif, flag) (((netif)->flags & (flag)) != 0) +#define netif_is_flag_set(netif, flag) (((netif)->flags & (flag)) != 0) void netif_set_up(struct netif *netif); void netif_set_down(struct netif *netif); diff --git a/tools/sdk/lwip2/include/lwip/opt.h b/tools/sdk/lwip2/include/lwip/opt.h index 371286b6ab..410e3fffdd 100644 --- a/tools/sdk/lwip2/include/lwip/opt.h +++ b/tools/sdk/lwip2/include/lwip/opt.h @@ -1549,7 +1549,7 @@ * TCP_MSS, IP header, and link header. */ #if !defined PBUF_POOL_BUFSIZE || defined __DOXYGEN__ -#define PBUF_POOL_BUFSIZE LWIP_MEM_ALIGN_SIZE(TCP_MSS+40+PBUF_LINK_ENCAPSULATION_HLEN+PBUF_LINK_HLEN) +#define PBUF_POOL_BUFSIZE LWIP_MEM_ALIGN_SIZE(TCP_MSS+PBUF_IP_HLEN+PBUF_TRANSPORT_HLEN+PBUF_LINK_ENCAPSULATION_HLEN+PBUF_LINK_HLEN) #endif /** @@ -1559,6 +1559,14 @@ #if !defined LWIP_PBUF_REF_T || defined __DOXYGEN__ #define LWIP_PBUF_REF_T u8_t #endif + +/** + * LWIP_PBUF_CUSTOM_DATA: Store private data on pbufs (e.g. timestamps) + * This extends struct pbuf so user can store custom data on every pbuf. + */ +#if !defined LWIP_PBUF_CUSTOM_DATA || defined __DOXYGEN__ +#define LWIP_PBUF_CUSTOM_DATA +#endif /** * @} */ @@ -1916,11 +1924,8 @@ /** LWIP_NETCONN_FULLDUPLEX==1: Enable code that allows reading from one thread, * writing from a 2nd thread and closing from a 3rd thread at the same time. - * ATTENTION: This is currently really alpha! Some requirements: - * - LWIP_NETCONN_SEM_PER_THREAD==1 is required to use one socket/netconn from - * multiple threads at once - * - sys_mbox_free() has to unblock receive tasks waiting on recvmbox/acceptmbox - * and prevent a task pending on this during/after deletion + * LWIP_NETCONN_SEM_PER_THREAD==1 is required to use one socket/netconn from + * multiple threads at once! */ #if !defined LWIP_NETCONN_FULLDUPLEX || defined __DOXYGEN__ #define LWIP_NETCONN_FULLDUPLEX 0 @@ -2450,7 +2455,7 @@ * network startup. */ #if !defined LWIP_IPV6_SEND_ROUTER_SOLICIT || defined __DOXYGEN__ -#define LWIP_IPV6_SEND_ROUTER_SOLICIT 1 +#define LWIP_IPV6_SEND_ROUTER_SOLICIT LWIP_IPV6 #endif /** @@ -2495,10 +2500,12 @@ /** * LWIP_ICMP6_DATASIZE: bytes from original packet to send back in - * ICMPv6 error messages. + * ICMPv6 error messages (0 = default of IP6_MIN_MTU_LENGTH) + * ATTENTION: RFC4443 section 2.4 says IP6_MIN_MTU_LENGTH is a MUST, + * so override this only if you absolutely have to! */ #if !defined LWIP_ICMP6_DATASIZE || defined __DOXYGEN__ -#define LWIP_ICMP6_DATASIZE 8 +#define LWIP_ICMP6_DATASIZE 0 #endif /** diff --git a/tools/sdk/lwip2/include/lwip/pbuf.h b/tools/sdk/lwip2/include/lwip/pbuf.h index 82902a4e98..e5daf968e8 100644 --- a/tools/sdk/lwip2/include/lwip/pbuf.h +++ b/tools/sdk/lwip2/include/lwip/pbuf.h @@ -219,6 +219,9 @@ struct pbuf { /** For incoming packets, this contains the input netif's index */ u8_t if_idx; + + /** In case the user needs to store data custom data on a pbuf */ + LWIP_PBUF_CUSTOM_DATA }; @@ -293,6 +296,7 @@ void pbuf_cat(struct pbuf *head, struct pbuf *tail); void pbuf_chain(struct pbuf *head, struct pbuf *tail); struct pbuf *pbuf_dechain(struct pbuf *p); err_t pbuf_copy(struct pbuf *p_to, const struct pbuf *p_from); +err_t pbuf_copy_partial_pbuf(struct pbuf *p_to, const struct pbuf *p_from, u16_t copy_len, u16_t offset); u16_t pbuf_copy_partial(const struct pbuf *p, void *dataptr, u16_t len, u16_t offset); void *pbuf_get_contiguous(const struct pbuf *p, void *buffer, size_t bufsize, u16_t len, u16_t offset); err_t pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len); diff --git a/tools/sdk/lwip2/include/lwip/priv/altcp_priv.h b/tools/sdk/lwip2/include/lwip/priv/altcp_priv.h index 2d3b2fdbce..d1de9b111b 100644 --- a/tools/sdk/lwip2/include/lwip/priv/altcp_priv.h +++ b/tools/sdk/lwip2/include/lwip/priv/altcp_priv.h @@ -85,6 +85,11 @@ typedef err_t (*altcp_get_tcp_addrinfo_fn)(struct altcp_pcb *conn, int local, ip typedef ip_addr_t *(*altcp_get_ip_fn)(struct altcp_pcb *conn, int local); typedef u16_t (*altcp_get_port_fn)(struct altcp_pcb *conn, int local); +#if LWIP_TCP_KEEPALIVE +typedef void (*altcp_keepalive_disable_fn)(struct altcp_pcb *conn); +typedef void (*altcp_keepalive_enable_fn)(struct altcp_pcb *conn, u32_t idle, u32_t intvl, u32_t count); +#endif + #ifdef LWIP_DEBUG typedef enum tcp_state (*altcp_dbg_get_tcp_state_fn)(struct altcp_pcb *conn); #endif @@ -111,6 +116,10 @@ struct altcp_functions { altcp_get_tcp_addrinfo_fn addrinfo; altcp_get_ip_fn getip; altcp_get_port_fn getport; +#if LWIP_TCP_KEEPALIVE + altcp_keepalive_disable_fn keepalive_disable; + altcp_keepalive_enable_fn keepalive_enable; +#endif #ifdef LWIP_DEBUG altcp_dbg_get_tcp_state_fn dbg_get_tcp_state; #endif @@ -133,6 +142,10 @@ void altcp_default_dealloc(struct altcp_pcb *conn); err_t altcp_default_get_tcp_addrinfo(struct altcp_pcb *conn, int local, ip_addr_t *addr, u16_t *port); ip_addr_t *altcp_default_get_ip(struct altcp_pcb *conn, int local); u16_t altcp_default_get_port(struct altcp_pcb *conn, int local); +#if LWIP_TCP_KEEPALIVE +void altcp_default_keepalive_disable(struct altcp_pcb *conn); +void altcp_default_keepalive_enable(struct altcp_pcb *conn, u32_t idle, u32_t intvl, u32_t count); +#endif #ifdef LWIP_DEBUG enum tcp_state altcp_default_dbg_get_tcp_state(struct altcp_pcb *conn); #endif diff --git a/tools/sdk/lwip2/include/lwip/prot/icmp6.h b/tools/sdk/lwip2/include/lwip/prot/icmp6.h index 3461120421..36989f6b32 100644 --- a/tools/sdk/lwip2/include/lwip/prot/icmp6.h +++ b/tools/sdk/lwip2/include/lwip/prot/icmp6.h @@ -146,6 +146,8 @@ PACK_STRUCT_END # include "arch/epstruct.h" #endif +#define ICMP6_HLEN 8 + /** This is the ICMP6 header adapted for echo req/resp. */ #ifdef PACK_STRUCT_USE_INCLUDES # include "arch/bpstruct.h" diff --git a/tools/sdk/lwip2/include/lwip/prot/ip6.h b/tools/sdk/lwip2/include/lwip/prot/ip6.h index 0f6de45593..7df81edd69 100644 --- a/tools/sdk/lwip2/include/lwip/prot/ip6.h +++ b/tools/sdk/lwip2/include/lwip/prot/ip6.h @@ -44,6 +44,8 @@ extern "C" { #endif +#define IP6_MIN_MTU_LENGTH 1280 + /** This is the packed version of ip6_addr_t, used in network headers that are itself packed */ #ifdef PACK_STRUCT_USE_INCLUDES 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 d4dc179558..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 */ /** @@ -1557,16 +1556,24 @@ * TCP_MSS, IP header, and link header. */ #if !defined PBUF_POOL_BUFSIZE || defined __DOXYGEN__ -#define PBUF_POOL_BUFSIZE LWIP_MEM_ALIGN_SIZE(TCP_MSS+40+PBUF_LINK_ENCAPSULATION_HLEN+PBUF_LINK_HLEN) +#define PBUF_POOL_BUFSIZE LWIP_MEM_ALIGN_SIZE(TCP_MSS+PBUF_IP_HLEN+PBUF_TRANSPORT_HLEN+PBUF_LINK_ENCAPSULATION_HLEN+PBUF_LINK_HLEN) #endif /** * 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 + +/** + * LWIP_PBUF_CUSTOM_DATA: Store private data on pbufs (e.g. timestamps) + * This extends struct pbuf so user can store custom data on every pbuf. + */ +#if !defined LWIP_PBUF_CUSTOM_DATA || defined __DOXYGEN__ +#define LWIP_PBUF_CUSTOM_DATA +#endif /** * @} */ @@ -1924,11 +1931,8 @@ /** LWIP_NETCONN_FULLDUPLEX==1: Enable code that allows reading from one thread, * writing from a 2nd thread and closing from a 3rd thread at the same time. - * ATTENTION: This is currently really alpha! Some requirements: - * - LWIP_NETCONN_SEM_PER_THREAD==1 is required to use one socket/netconn from - * multiple threads at once - * - sys_mbox_free() has to unblock receive tasks waiting on recvmbox/acceptmbox - * and prevent a task pending on this during/after deletion + * LWIP_NETCONN_SEM_PER_THREAD==1 is required to use one socket/netconn from + * multiple threads at once! */ #if !defined LWIP_NETCONN_FULLDUPLEX || defined __DOXYGEN__ #define LWIP_NETCONN_FULLDUPLEX 0 @@ -2442,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 /** @@ -2464,7 +2468,7 @@ * network startup. */ #if !defined LWIP_IPV6_SEND_ROUTER_SOLICIT || defined __DOXYGEN__ -#define LWIP_IPV6_SEND_ROUTER_SOLICIT 1 +#define LWIP_IPV6_SEND_ROUTER_SOLICIT LWIP_IPV6 #endif /** @@ -2509,10 +2513,12 @@ /** * LWIP_ICMP6_DATASIZE: bytes from original packet to send back in - * ICMPv6 error messages. + * ICMPv6 error messages (0 = default of IP6_MIN_MTU_LENGTH) + * ATTENTION: RFC4443 section 2.4 says IP6_MIN_MTU_LENGTH is a MUST, + * so override this only if you absolutely have to! */ #if !defined LWIP_ICMP6_DATASIZE || defined __DOXYGEN__ -#define LWIP_ICMP6_DATASIZE 8 +#define LWIP_ICMP6_DATASIZE 0 #endif /** @@ -2676,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 /** * @} @@ -2715,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 /** @@ -3502,9 +3508,6 @@ #if !defined DHCP6_DEBUG || defined __DOXYGEN__ #define DHCP6_DEBUG LWIP_DBG_OFF #endif -/** - * @} - */ /** * NAPT_DEBUG: Enable debugging for NAPT. @@ -3513,6 +3516,10 @@ #define NAPT_DEBUG LWIP_DBG_OFF #endif +/** + * @} + */ + /** * LWIP_TESTMODE: Changes to make unit test possible */ @@ -3547,6 +3554,11 @@ -------------------------------------------------- */ +#include "lwip/debug.h" +#include "arch/cc.h" +#include "lwip-git-hash.h" +#include // settimeofday() + struct timeval + #ifndef LWIP_FEATURES #error LWIP_FEATURES must be defined #endif @@ -3554,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 @@ -3584,6 +3600,19 @@ extern void lwip_hook_dhcp_parse_option(struct netif *netif, struct dhcp *dhcp, int msg_type, int option, int option_len, struct pbuf *pbuf, int option_value_offset); +#if LWIP_FEATURES +#define LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, state, msg, msg_type, option_len_ptr) { \ + /* https://github.com/esp8266/Arduino/issues/8223 */ \ + lwip_hook_dhcp_amend_options(netif, dhcp, state, msg, msg_type, option_len_ptr); \ + /* https://github.com/esp8266/Arduino/issues/8247 */ \ + if ((msg_type) == DHCP_DISCOVER) \ + *(option_len_ptr) = dhcp_option_hostname(*(option_len_ptr), (msg)->options, netif); \ +} + +extern void lwip_hook_dhcp_amend_options(struct netif *netif, struct dhcp *dhcp, int state, struct dhcp_msg *msg, + int msg_type, u16 *option_len_ptr); +#endif + /* -------------------------------------------------- ------------------ SNTP options ------------------ @@ -3629,11 +3658,6 @@ uint32_t SNTP_STARTUP_DELAY_FUNC; -------------------------------------------------- */ -#include "lwip/debug.h" -#include "arch/cc.h" -#include "lwip-git-hash.h" -#include // settimeofday() + struct timeval - // allow to handle special packets (user redefinable) struct pbuf; struct netif; @@ -3690,4 +3714,4 @@ void tcp_kill_timewait (void); } // extern "C" #endif -#endif // MYLWIPOPTS_H +#endif /* MYLWIPOPTS_H */ diff --git a/tools/sdk/lwip2/include/netif/ppp/ppp_opts.h b/tools/sdk/lwip2/include/netif/ppp/ppp_opts.h index 6702bec618..479a006d10 100644 --- a/tools/sdk/lwip2/include/netif/ppp/ppp_opts.h +++ b/tools/sdk/lwip2/include/netif/ppp/ppp_opts.h @@ -44,6 +44,13 @@ #define PPPOE_SUPPORT 0 #endif +/** + * PPPOE_SCNAME_SUPPORT==1: Enable PPP Over Ethernet Service Name and Concentrator Name support + */ +#ifndef PPPOE_SCNAME_SUPPORT +#define PPPOE_SCNAME_SUPPORT 0 +#endif + /** * PPPOL2TP_SUPPORT==1: Enable PPP Over L2TP */ diff --git a/tools/sdk/ssl/bearssl b/tools/sdk/ssl/bearssl index 28bebad562..5166f2bb03 160000 --- a/tools/sdk/ssl/bearssl +++ b/tools/sdk/ssl/bearssl @@ -1 +1 @@ -Subproject commit 28bebad56295df6a49b01ad323a6e354cb977785 +Subproject commit 5166f2bb03fb03597b0f2c8c7fbcf01616df67c9 diff --git a/tools/sdk/uzlib b/tools/sdk/uzlib index 42398df66c..27e4f4c15b 160000 --- a/tools/sdk/uzlib +++ b/tools/sdk/uzlib @@ -1 +1 @@ -Subproject commit 42398df66c02da1cec387b634b6d2f285007a7af +Subproject commit 27e4f4c15ba30c2cfc89575159e8efb50f95037e 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 (ICACHE_RAM_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='store', required=True, help='Path to the Arduino sketch ELF') - parser.add_argument('-p', '--path', action='store', required=True, help='Path to Xtensa toolchain binaries') - parser.add_argument('-i', '--mmu', action='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="store", + required=True, + help="Path to the Arduino sketch ELF", + ) + parser.add_argument( + "-p", + "--path", + action="store", + required=True, + help="Path to Xtensa toolchain binaries", + ) + parser.add_argument( + "-i", "--mmu", action="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/tools/warnings/README.md b/tools/warnings/README.md new file mode 100644 index 0000000000..5fb33f2bd7 --- /dev/null +++ b/tools/warnings/README.md @@ -0,0 +1,12 @@ +These are the warning options for the compiler at different levels. + +Because G++ 10 produces code which crashes when a function is declared +to return a value but doesn't (this is undefined per the C++ specs, but legal +for C11 and above code as long as the [non]returned value is ignored), we +cannot warn them if we use "-w" to disable all warnings, and instead have +to delete every warning but "-Wreturn-type" + +Generate the "none-g++" file with the following command: +```` +./tools/xtensa-lx106-elf/bin/xtensa-lx106-elf-gcc --help=warnings -Q | grep '\[enabled\]' | grep -v 'return-type' | awk '{print $1}' | sed 's/-W/-Wno-/' | grep -v = | grep -v -- -f | egrep -v '(c11-c2x-compat|c90-c99-compat|c99-c11-compat|declaration-after-statement|designated-init|discarded-array-qualifiers|discarded-qualifiers|implicit-int|incompatible-pointer-types|int-conversion|old-style-definition|override-init-side-effects|pointer-to-int-cast)' > tools/warnings/none-g++ +```` 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-cppflags b/tools/warnings/default-cppflags new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/warnings/extra-cflags b/tools/warnings/extra-cflags new file mode 100644 index 0000000000..6492588382 --- /dev/null +++ b/tools/warnings/extra-cflags @@ -0,0 +1 @@ +-Wall -Wextra diff --git a/tools/warnings/extra-cppflags b/tools/warnings/extra-cppflags new file mode 100644 index 0000000000..6492588382 --- /dev/null +++ b/tools/warnings/extra-cppflags @@ -0,0 +1 @@ +-Wall -Wextra diff --git a/tools/warnings/more-cflags b/tools/warnings/more-cflags new file mode 100644 index 0000000000..bd866b6966 --- /dev/null +++ b/tools/warnings/more-cflags @@ -0,0 +1 @@ +-Wall diff --git a/tools/warnings/more-cppflags b/tools/warnings/more-cppflags new file mode 100644 index 0000000000..bd866b6966 --- /dev/null +++ b/tools/warnings/more-cppflags @@ -0,0 +1 @@ +-Wall diff --git a/tools/warnings/none-cflags b/tools/warnings/none-cflags new file mode 100644 index 0000000000..e1350473a1 --- /dev/null +++ b/tools/warnings/none-cflags @@ -0,0 +1 @@ +-w diff --git a/tools/warnings/none-cppflags b/tools/warnings/none-cppflags new file mode 100644 index 0000000000..d72f50f2a8 --- /dev/null +++ b/tools/warnings/none-cppflags @@ -0,0 +1,52 @@ +-Wno-address-of-packed-member +-Wno-aggressive-loop-optimizations +-Wno-analyzer-malloc-leak +-Wno-analyzer-null-argument +-Wno-analyzer-null-dereference +-Wno-analyzer-possible-null-argument +-Wno-analyzer-possible-null-dereference +-Wno-analyzer-stale-setjmp-buffer +-Wno-analyzer-tainted-array-index +-Wno-analyzer-unsafe-call-within-signal-handler +-Wno-attribute-warning +-Wno-attributes +-Wno-builtin-declaration-mismatch +-Wno-builtin-macro-redefined +-Wno-cannot-profile +-Wno-coverage-mismatch +-Wno-cpp +-Wno-deprecated +-Wno-deprecated-declarations +-Wno-div-by-zero +-Wno-endif-labels +-Wno-enum-compare +-Wno-hsa +-Wno-if-not-aligned +-Wno-ignored-attributes +-Wno-int-to-pointer-cast +-Wno-invalid-memory-model +-Wno-long-long +-Wno-lto-type-mismatch +-Wno-main +-Wno-missing-profile +-Wno-narrowing +-Wno-odr +-Wno-overflow +-Wno-packed-bitfield-compat +-Wno-pointer-compare +-Wno-pragmas +-Wno-prio-ctor-dtor +-Wno-psabi +-Wno-return-local-addr +-Wno-shift-count-negative +-Wno-shift-count-overflow +-Wno-shift-negative-value +-Wno-sizeof-array-argument +-Wno-switch-bool +-Wno-switch-outside-range +-Wno-switch-unreachable +-Wno-sync-nand +-Wno-trigraphs +-Wno-unused-result +-Wno-varargs +-Wno-vla diff --git a/variants/agruminolemonv4/pins_arduino.h b/variants/agruminolemonv4/pins_arduino.h new file mode 100644 index 0000000000..02be0b1898 --- /dev/null +++ b/variants/agruminolemonv4/pins_arduino.h @@ -0,0 +1,53 @@ +/* + 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 + +#define LED_BUILTIN 2 + +// PINOUT Agrumino Implemented +#define PIN_SDA 2 // [X] BOOT: Must be HIGH at boot +#define PIN_SCL 14 // [X] +#define PIN_PUMP 12 // [X] +#define PIN_BTN_S1 4 // [X] Same as Internal WT8266 LED +#define PIN_USB_DETECT 5 // [X] +#define PIN_MOSFET 15 // [X] BOOT: Must be LOW at boot +#define PIN_BATT_STAT 13 // [X] +#define PIN_LEVEL 0 // [ ] BOOT: HIGH for Running and LOW for Program + +static constexpr uint8_t D0 = 16; +static constexpr uint8_t RX = 3; +static constexpr uint8_t TX = 1; + +#define PIN_WIRE_SDA PIN_SDA +#define PIN_WIRE_SCL PIN_SCL + +static constexpr uint8_t SDA = PIN_WIRE_SDA; +static constexpr uint8_t SCL = PIN_WIRE_SCL; + +#include "../generic/common.h" + +#endif /* Pins_Arduino_h */ 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 */ diff --git a/variants/wifi_kit_8/pins_arduino.h b/variants/wifi_kit_8/pins_arduino.h new file mode 100644 index 0000000000..9acdd8c857 --- /dev/null +++ b/variants/wifi_kit_8/pins_arduino.h @@ -0,0 +1,54 @@ +/* + 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. + Modified for Wifi Kit 8 by G,Neiß, 2021-07-002 + + 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 + +#define WIFI_Kit_8 true + +#define PIN_WIRE_SDA (4) +#define PIN_WIRE_SCL (5) + +static const uint8_t SDA = PIN_WIRE_SDA; +static const uint8_t SCL = PIN_WIRE_SCL; +static const uint8_t OLED_RST = 16; + +// Board doesen't have a builtin LED +// #define LED_BUILTIN 0 + +static const uint8_t D0 = 16; +static const uint8_t D1 = 5; +static const uint8_t D2 = 4; +static const uint8_t D3 = 0; +static const uint8_t D6 = 12; +static const uint8_t D7 = 13; +static const uint8_t D8 = 15; +static const uint8_t RX = 3; +static const uint8_t TX = 1; + +#include "../generic/common.h" + +#endif /* Pins_Arduino_h */ diff --git a/variants/wifi_slot/analogRead.cpp b/variants/wifi_slot/analogRead.cpp index f2c1f3c7a2..3550709f82 100644 --- a/variants/wifi_slot/analogRead.cpp +++ b/variants/wifi_slot/analogRead.cpp @@ -39,7 +39,7 @@ extern "C" int analogRead(uint8_t pin) { void initVariant() { // we need to reset analog mux. When ANALOG_INPUT_SELECTOR_PIN is high // on ~0.4 ms, mux channel is becoming 0. - // Mux channel is swithing on back \_ front. But there is no switching + // Mux channel is switching on back \_ front. But there is no switching // - rc reset is still high when ANALOG_INPUT_SELECTOR_PIN became low uint16_t resetDelay = 777; pinMode(ANALOG_INPUT_SELECTOR_PIN, OUTPUT);