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 74f1b43063..0000000000 --- a/.github/workflows/pull-request.yml +++ /dev/null @@ -1,316 +0,0 @@ -# Run whenever a PR is generated or updated. - -# Most jobs check out the code, ensure Python3 is installed, and for build -# tests the ESP8266 toolchain is cached when possible to speed up execution. - -name: ESP8266 Arduino CI - -on: - pull_request: - - -jobs: - -# Run 8 parallel jobs for the default build of all examples. - build-linux: - name: Build ${{ matrix.chunk }} - runs-on: ubuntu-latest - defaults: - run: - shell: bash - strategy: - matrix: - chunk: [0, 1, 2, 3, 4, 5, 6, 7] - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Cache Linux toolchain - id: cache-linux - uses: actions/cache@v2 - with: - path: ./tools/dist - key: ${{ runner.os }}-${{ hashFiles('package/package_esp8266com_index.template.json', 'tests/common.sh') }} - - name: Build Sketches - env: - TRAVIS_BUILD_DIR: ${{ github.workspace }} - TRAVIS_TAG: ${{ github.ref }} - BUILD_PARITY: custom - mod: 8 - rem: ${{ matrix.chunk }} - run: | - bash ./tests/build.sh - - -# Cover the debug and IPv6 cases by enabling both and running 8 parallel jobs -# over all example code. - build-debug-ipv6: - name: Debug IPv6 ${{ matrix.chunk }} - runs-on: ubuntu-latest - defaults: - run: - shell: bash - strategy: - matrix: - chunk: [0, 1, 2, 3, 4, 5, 6, 7] - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Cache Linux toolchain - id: cache-linux - uses: actions/cache@v2 - with: - path: ./tools/dist - key: ${{ runner.os }}-${{ hashFiles('package/package_esp8266com_index.template.json', 'tests/common.sh') }} - - name: Build Sketches - env: - TRAVIS_BUILD_DIR: ${{ github.workspace }} - TRAVIS_TAG: ${{ github.ref }} - BUILD_PARITY: custom - mod: 8 - rem: ${{ matrix.chunk }} - run: | - bash ./tests/debug6.sh - - -# Single build under Windows to ensure the Win toolchain is good. - build-windows: - name: Windows - runs-on: windows-latest - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Cache Windows toolchain - id: cache-windows - uses: actions/cache@v2 - with: - path: ./tools/dist - key: ${{ runner.os }}-${{ hashFiles('package/package_esp8266com_index.template.json', 'tests/common.sh') }} - - name: Build Sketch - env: - TRAVIS_BUILD_DIR: ${{ github.workspace }} - TRAVIS_TAG: ${{ github.ref }} - WINDOWS: 1 - BUILD_PARITY: custom - mod: 500 - rem: 1 - run: | - # Windows has python3 already installed, but it's called "python". - # Copy python.exe to the proper name so scripts "just work". - try { Get-Command python3 } catch { copy (get-command python).source (get-command python).source.Replace("python.exe", "python3.exe") } - bash ./tests/build.sh - - -# Single build under macOS to ensure the Mac toolchain is good. - build-mac: - name: Mac - runs-on: macOS-latest - defaults: - run: - shell: bash - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Cache Mac toolchain - id: cache-mac - uses: actions/cache@v2 - with: - path: ./tools/dist - key: ${{ runner.os }}-${{ hashFiles('package/package_esp8266com_index.template.json', 'tests/common.sh') }} - - name: Build Sketch - env: - TRAVIS_BUILD_DIR: ${{ github.workspace }} - TRAVIS_TAG: ${{ github.ref }} - MACOSX: 1 - BUILD_PARITY: custom - mod: 500 - rem: 1 - run: | - bash ./tests/build.sh - - -# Run a few Platform.IO jobs (not full suite) to check PIO integration. - build-pio: - name: Build Platform.IO - runs-on: ubuntu-latest - defaults: - run: - shell: bash - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Build subset on Platform.IO - env: - TRAVIS_BUILD_DIR: ${{ github.workspace }} - TRAVIS_TAG: ${{ github.ref }} - BUILD_PARITY: custom - mod: 42 # Picked at random to give 4-5 builds and exit. - rem: 13 - run: | - sudo apt update - sudo apt install python3-pip python3-setuptools - PATH=/home/runner/.local/bin:$PATH bash ./tests/platformio.sh - - -# Run host test suite under valgrind for runtime checking of code. - host-tests: - name: Host tests - runs-on: ubuntu-latest - defaults: - run: - shell: bash - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Run host tests - env: - TRAVIS_BUILD_DIR: ${{ github.workspace }} - TRAVIS_TAG: ${{ github.ref }} - run: | - sudo apt update - sudo apt install valgrind lcov - bash ./tests/ci/host_test.sh - - -# Ensure Sphinx can build the documentation properly. - documentation: - name: Documentation - runs-on: ubuntu-latest - defaults: - run: - shell: bash - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Build documentation - env: - TRAVIS_BUILD_DIR: ${{ github.workspace }} - TRAVIS_TAG: ${{ github.ref }} - run: | - sudo apt update - sudo apt install python3-pip python3-setuptools - # GitHub CI installs pip3 and setuptools outside the path. - # Update the path to include them and run. - PATH=/home/runner/.local/bin:$PATH pip3 install --user -r doc/requirements.txt - PATH=/home/runner/.local/bin:$PATH bash ./tests/ci/build_docs.sh - - -# Standard Arduino formatting in all the examples - style-check: - name: Style and formatting - runs-on: ubuntu-latest - defaults: - run: - shell: bash - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Style check - env: - 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 - defaults: - run: - shell: bash - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Mock build - env: - TRAVIS_BUILD_DIR: ${{ github.workspace }} - TRAVIS_TAG: ${{ github.ref }} - run: | - bash ./tests/buildm.sh - - -# Ensure no manual edits to boards.txt - boards-check: - name: Boards.txt check - runs-on: ubuntu-latest - defaults: - run: - shell: bash - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Cache Linux toolchain - id: cache-linux - uses: actions/cache@v2 - with: - path: ./tools/dist - key: ${{ runner.os }}-${{ hashFiles('package/package_esp8266com_index.template.json', 'tests/common.sh') }} - - name: Boards.txt diff - env: - TRAVIS_BUILD_DIR: ${{ github.workspace }} - TRAVIS_TAG: ${{ github.ref }} - run: | - bash ./tests/ci/build_boards.sh - bash ./tests/ci/eboot_test.sh - bash ./tests/ci/pkgrefs_test.sh - - -# Validate orthography - code-spell: - name: Check spelling - runs-on: ubuntu-latest - defaults: - run: - shell: bash - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - name: Run codespell - uses: codespell-project/actions-codespell@master - with: - skip: ./libraries/ESP8266SdFat,./libraries/LittleFS/lib,./tools/pyserial,./tools/sdk,./tools/esptool,./libraries/SoftwareSerial,./libraries/Ethernet,./github/workflows,./libraries/ESP8266HTTPUpdateServer/examples/SecureBearSSLUpdater/SecureBearSSLUpdater.ino,./libraries/esp8266/examples/RTCUserMemory/RTCUserMemory.ino,./libraries/esp8266/examples/StreamString/StreamString.ino,./libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino,./libraries/ESP8266WiFi/examples/BearSSL_Sessions/BearSSL_Sessions.ino,./libraries/ESP8266WebServer/examples/HelloServerBearSSL/HelloServerBearSSL.ino,./libraries/ESP8266WebServer/examples/HttpHashCredAuth/HttpHashCredAuth.ino,./cores/esp8266/spiffs,./tests/device/test_libc/libm_string.c, ./libraries/Netdump/examples/Netdump/Netdump.ino,./libraries/ESP8266WiFi/examples/BearSSL_Server,./cores/esp8266/LwipIntfDev.h - ignore_words_list: ESP8266,esp8266,esp,dout,DOUT,ser,ans diff --git a/.github/workflows/release-to-publish.yml b/.github/workflows/release-to-publish.yml index 8c815e4fd3..3a80412551 100644 --- a/.github/workflows/release-to-publish.yml +++ b/.github/workflows/release-to-publish.yml @@ -28,21 +28,20 @@ on: release: types: [published] +permissions: + contents: read + jobs: package: name: Update master JSON file runs-on: ubuntu-latest - defaults: - run: - shell: bash steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: false fetch-depth: 0 - name: Deploy updated JSON env: - TRAVIS_BUILD_DIR: ${{ github.workspace }} BUILD_TYPE: package CI_GITHUB_API_KEY: ${{ secrets.GITHUB_TOKEN }} GHCI_DEPLOY_KEY: ${{ secrets.GHCI_DEPLOY_KEY }} diff --git a/.github/workflows/style-check.yml b/.github/workflows/style-check.yml new file mode 100644 index 0000000000..c371c2f252 --- /dev/null +++ b/.github/workflows/style-check.yml @@ -0,0 +1,45 @@ +name: Style and syntax checks + +on: + pull_request: + +permissions: + contents: read + +jobs: + + # Generic formatting for Core and examples + + clang-format: + name: clang-format + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - uses: actions/setup-python@v5 + with: + python-version: '3.x' + - name: Style check + run: | + sudo apt update + python ./tests/test_restyle.py --quiet + env CLANG_FORMAT="clang-format-18" bash ./tests/ci/style_check.sh + + # Validate orthography + + code-spell: + name: codespell + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: false + - name: Run codespell + uses: codespell-project/actions-codespell@master + with: + skip: ./libraries/ESP8266SdFat,./libraries/LittleFS/lib,./tools/pyserial,./tools/sdk,./tools/esptool,./libraries/SoftwareSerial,./libraries/Ethernet,./github/workflows,./libraries/ESP8266HTTPUpdateServer/examples/SecureBearSSLUpdater/SecureBearSSLUpdater.ino,./libraries/esp8266/examples/RTCUserMemory/RTCUserMemory.ino,./libraries/esp8266/examples/StreamString/StreamString.ino,./libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino,./libraries/ESP8266WiFi/examples/BearSSL_Sessions/BearSSL_Sessions.ino,./libraries/ESP8266WebServer/examples/HelloServerBearSSL/HelloServerBearSSL.ino,./libraries/ESP8266WebServer/examples/HttpHashCredAuth/HttpHashCredAuth.ino,./cores/esp8266/spiffs,./tests/device/test_libc/libm_string.c, ./libraries/Netdump/examples/Netdump/Netdump.ino,./libraries/ESP8266WiFi/examples/BearSSL_Server,./cores/esp8266/LwipIntfDev.h + ignore_words_list: ESP8266,esp8266,esp,dout,DOUT,ser,ans diff --git a/.github/workflows/tag-to-draft-release.yml b/.github/workflows/tag-to-draft-release.yml index 51240262d0..e9311f9fce 100644 --- a/.github/workflows/tag-to-draft-release.yml +++ b/.github/workflows/tag-to-draft-release.yml @@ -18,28 +18,34 @@ jobs: run: shell: bash steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: true fetch-depth: 0 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v5 with: python-version: '3.x' - name: Set GIT tag name run: | # Sets an environment variable used in the next steps - TRAVIS_TAG="$(git describe --exact-match --tags)" - echo "TRAVIS_TAG=${TRAVIS_TAG}" >> $GITHUB_ENV + ESP8266_ARDUINO_RELEASE_TAG="$(git describe --exact-match --tags)" + echo "ESP8266_ARDUINO_RELEASE_TAG=${ESP8266_ARDUINO_RELEASE_TAG}" >> $GITHUB_ENV - name: Build package JSON env: - TRAVIS_BUILD_DIR: ${{ github.workspace }} - BUILD_TYPE: package CI_GITHUB_API_KEY: ${{ secrets.GITHUB_TOKEN }} + BUILD_TYPE: package run: | - bash ./tests/ci/build_package.sh - pip3 install PyGithub - # Create a draft release and upload the ZIP and JSON files. - # This draft is not visible to normal users and needs to be - # updated manually with release notes and published from the - # GitHub web interface. - python3 ./package/upload_release.py --user "$GITHUB_ACTOR" --repo "$GITHUB_REPOSITORY" --token "$CI_GITHUB_API_KEY" --tag "$TRAVIS_TAG" --name "Release $TRAVIS_TAG" --msg "Update the draft with release notes before publishing." package/versions/*/*.zip package/versions/*/package_esp8266com_index.json + bash ./tests/ci/build_package.sh + # Create a draft release and upload the ZIP and JSON files. + # This draft is not visible to normal users and needs to be + # updated manually with release notes and published from the + # GitHub web interface. + pip3 install PyGithub + python3 ./package/upload_release.py \ + --user "$GITHUB_ACTOR" \ + --repo "$GITHUB_REPOSITORY" \ + --token "$CI_GITHUB_API_KEY" \ + --tag "$ESP8266_ARDUINO_RELEASE_TAG" \ + --name "Release ${ESP8266_ARDUINO_RELEASE_TAG}" \ + --msg "Update the draft with release notes before publishing." \ + package/versions/*/*.zip package/versions/*/package_esp8266com_index.json diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000000..867f3d40de --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,21 @@ +# Read the Docs configuration file for Sphinx projects +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the OS, Python version and other tools you might need +build: + os: ubuntu-24.04 + tools: + python: "3.12" + +# Build documentation in the "doc/" directory with Sphinx +sphinx: + configuration: doc/conf.py + +# Install same versions as our local tools +# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html +python: + install: + - requirements: doc/requirements.txt diff --git a/README.md b/README.md index eaaabb6a8b..21ec6ef397 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Arduino core for ESP8266 WiFi chip # Quick links -- [Latest release documentation](https://arduino-esp8266.readthedocs.io/en/3.0.2/) +- [Latest release documentation](https://arduino-esp8266.readthedocs.io/en/3.1.2/) - [Current "git version" documentation](https://arduino-esp8266.readthedocs.io/en/latest/) - [Install git version](https://arduino-esp8266.readthedocs.io/en/latest/installing.html#using-git-version) ([sources](doc/installing.rst#using-git-version)) @@ -28,23 +28,22 @@ ESP8266 Arduino core comes with libraries to communicate over WiFi using TCP and Starting with 1.6.4, Arduino allows installation of third-party platform packages using Boards Manager. We have packages available for Windows, Mac OS, and Linux (32 and 64 bit). -- Install the current upstream Arduino IDE at the 1.8.9 level or later. The current version is on the [Arduino website](https://www.arduino.cc/en/main/software). -- Start Arduino and open the Preferences window. -- Enter ```https://arduino.esp8266.com/stable/package_esp8266com_index.json``` into the *File>Preferences>Additional Boards Manager URLs* field of the Arduino IDE. You can add multiple URLs, separating them with commas. +- [Download and install Arduino IDE 1.x or 2.x](https://www.arduino.cc/en/software) +- Start Arduino and open the Preferences window +- Enter `https://arduino.esp8266.com/stable/package_esp8266com_index.json` into the *File>Preferences>Additional Boards Manager URLs* field of the Arduino IDE. You can add multiple URLs, separating them with commas. - Open Boards Manager from Tools > Board menu and install *esp8266* platform (and don't forget to select your ESP8266 board from Tools > Board menu after installation). #### Latest release [![Latest release](https://img.shields.io/github/release/esp8266/Arduino.svg)](https://github.com/esp8266/Arduino/releases/latest/) Boards manager link: `https://arduino.esp8266.com/stable/package_esp8266com_index.json` -Documentation: [https://arduino-esp8266.readthedocs.io/en/3.0.2/](https://arduino-esp8266.readthedocs.io/en/3.0.2/) +Documentation: [https://arduino-esp8266.readthedocs.io/en/3.1.2/](https://arduino-esp8266.readthedocs.io/en/3.1.2/) ### Using git version -[![Linux build status](https://travis-ci.org/esp8266/Arduino.svg)](https://travis-ci.org/esp8266/Arduino) Also known as latest git or master branch. -- Install the current upstream Arduino IDE at the 1.8 level or later. The current version is on the [Arduino website](https://www.arduino.cc/en/main/software). -- Follow the [instructions in the documentation](https://arduino-esp8266.readthedocs.io/en/latest/installing.html#using-git-version). +- When using [Arduino IDE](https://www.arduino.cc/en/software), follow [our instructions here](https://arduino-esp8266.readthedocs.io/en/latest/installing.html#using-git-version). +- When using [PlatformIO](https://platformio.org/install), refer to [platformio/espressif8266 platform documentation](https://docs.platformio.org/en/stable/platforms/espressif8266.html#using-arduino-framework-with-staging-version). ### Using PlatformIO diff --git a/boards.txt b/boards.txt index fd05749760..1b44fe3310 100644 --- a/boards.txt +++ b/boards.txt @@ -17,12 +17,14 @@ menu.FlashFreq=Flash Frequency menu.ResetMethod=Reset Method menu.dbg=Debug port menu.lvl=Debug Level +menu.optim=Debug Optimization menu.ip=lwIP Variant menu.vt=VTables menu.exception=C++ Exceptions menu.stacksmash=Stack Protection menu.wipe=Erase Flash -menu.sdk=Espressif FW +menu.sdk=NONOS SDK Version +menu.iramfloat=Floating Point operations menu.ssl=SSL Support menu.mmu=MMU menu.non32xfer=Non-32-Bit Access @@ -40,6 +42,7 @@ generic.build.mcu=esp8266 generic.build.core=esp8266 generic.build.variant=generic generic.build.spiffs_pagesize=256 +generic.build.debug_optim= generic.build.debug_port= generic.build.debug_level= generic.menu.xtal.80=80 MHz @@ -74,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -generic.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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 @@ -523,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 @@ -537,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 @@ -571,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -esp8285.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -587,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 @@ -767,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 @@ -803,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 @@ -889,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 @@ -904,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 @@ -938,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -gen4iod.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -962,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 @@ -1086,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 @@ -1172,6 +1127,15 @@ 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 @@ -1186,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 @@ -1220,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -huzzah.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -1234,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 @@ -1301,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 @@ -1387,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 @@ -1401,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 @@ -1435,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -wifi_slot.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -1466,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 @@ -1630,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 @@ -1716,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 @@ -1742,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 @@ -1776,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -arduino-esp8266.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -1790,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 @@ -1857,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 @@ -1943,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) @@ -1958,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 @@ -1992,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -espmxdevkit.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -2006,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 @@ -2113,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 @@ -2199,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 @@ -2214,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 @@ -2248,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -oak.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -2262,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 @@ -2329,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 @@ -2415,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) @@ -2438,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 @@ -2472,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -espduino.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -2485,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 @@ -2552,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 @@ -2638,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 @@ -2652,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 @@ -2686,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -espectro.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -2700,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 @@ -2767,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 @@ -2853,6 +2836,15 @@ 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) @@ -2867,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 @@ -2901,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -espino.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -2918,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 @@ -2985,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 @@ -3071,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 @@ -3085,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 @@ -3119,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -espresso_lite_v1.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -3132,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 @@ -3203,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 @@ -3289,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 @@ -3303,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 @@ -3337,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -espresso_lite_v2.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -3350,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 @@ -3421,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 @@ -3507,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 @@ -3531,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 @@ -3565,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -sonoff.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -3579,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 @@ -3686,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 @@ -3772,6 +3778,15 @@ 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 @@ -3786,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 @@ -3820,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -inventone.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -3834,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 @@ -3901,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 @@ -3987,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 @@ -4001,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 @@ -4035,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -d1_mini.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -4049,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 @@ -4116,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 @@ -4202,6 +4463,15 @@ 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) @@ -4216,6 +4486,7 @@ d1_mini_clone.serial.disableRTS=true d1_mini_clone.build.mcu=esp8266 d1_mini_clone.build.core=esp8266 d1_mini_clone.build.spiffs_pagesize=256 +d1_mini_clone.build.debug_optim= d1_mini_clone.build.debug_port= d1_mini_clone.build.debug_level= d1_mini_clone.menu.xtal.80=80 MHz @@ -4250,10 +4521,10 @@ 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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -d1_mini_clone.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -4281,40 +4552,32 @@ 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_size_bytes=0x400000 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.upload.maximum_size=1044464 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_size_bytes=0x400000 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.upload.maximum_size=1044464 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_size_bytes=0x400000 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.upload.maximum_size=1044464 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_size_bytes=0x400000 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.upload.maximum_size=1044464 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 @@ -4348,6 +4611,12 @@ d1_mini_clone.menu.dbg.Serial1=Serial1 d1_mini_clone.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 d1_mini_clone.menu.lvl.None____=None d1_mini_clone.menu.lvl.None____.build.debug_level= +d1_mini_clone.menu.optim.Smallest=None +d1_mini_clone.menu.optim.Smallest.build.debug_optim=-Os +d1_mini_clone.menu.optim.Lite=Lite +d1_mini_clone.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +d1_mini_clone.menu.optim.Full=Optimum +d1_mini_clone.menu.optim.Full.build.debug_optim=-Og d1_mini_clone.menu.lvl.SSL=SSL d1_mini_clone.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL d1_mini_clone.menu.lvl.TLS_MEM=TLS_MEM @@ -4434,6 +4703,15 @@ 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 @@ -4448,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 @@ -4482,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -d1_mini_lite.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -4496,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 @@ -4603,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 @@ -4689,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 @@ -4703,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 @@ -4737,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -d1_mini_pro.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -4751,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 @@ -4801,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 @@ -4887,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 @@ -4901,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 @@ -4935,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -d1.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -4949,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 @@ -5016,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 @@ -5102,6 +5396,15 @@ 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 @@ -5116,6 +5419,7 @@ agruminolemon.serial.disableRTS=true agruminolemon.build.mcu=esp8266 agruminolemon.build.core=esp8266 agruminolemon.build.spiffs_pagesize=256 +agruminolemon.build.debug_optim= agruminolemon.build.debug_port= agruminolemon.build.debug_level= agruminolemon.menu.xtal.80=80 MHz @@ -5150,10 +5454,10 @@ 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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -agruminolemon.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -5164,60 +5468,48 @@ 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_size_bytes=0x200000 agruminolemon.menu.eesz.2M64.build.flash_ld=eagle.flash.2m64.ld agruminolemon.menu.eesz.2M64.build.spiffs_pagesize=256 -agruminolemon.menu.eesz.2M64.upload.maximum_size=1044464 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_size_bytes=0x200000 agruminolemon.menu.eesz.2M128.build.flash_ld=eagle.flash.2m128.ld agruminolemon.menu.eesz.2M128.build.spiffs_pagesize=256 -agruminolemon.menu.eesz.2M128.upload.maximum_size=1044464 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_size_bytes=0x200000 agruminolemon.menu.eesz.2M256.build.flash_ld=eagle.flash.2m256.ld agruminolemon.menu.eesz.2M256.build.spiffs_pagesize=256 -agruminolemon.menu.eesz.2M256.upload.maximum_size=1044464 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_size_bytes=0x200000 agruminolemon.menu.eesz.2M512.build.flash_ld=eagle.flash.2m512.ld agruminolemon.menu.eesz.2M512.build.spiffs_pagesize=256 -agruminolemon.menu.eesz.2M512.upload.maximum_size=1044464 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_size_bytes=0x200000 agruminolemon.menu.eesz.2M1M.build.flash_ld=eagle.flash.2m1m.ld agruminolemon.menu.eesz.2M1M.build.spiffs_pagesize=256 -agruminolemon.menu.eesz.2M1M.upload.maximum_size=1044464 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_size_bytes=0x200000 agruminolemon.menu.eesz.2M.build.flash_ld=eagle.flash.2m.ld agruminolemon.menu.eesz.2M.build.spiffs_pagesize=256 -agruminolemon.menu.eesz.2M.upload.maximum_size=1044464 agruminolemon.menu.eesz.2M.build.rfcal_addr=0x1FC000 agruminolemon.menu.ip.lm2f=v2 Lower Memory agruminolemon.menu.ip.lm2f.build.lwip_include=lwip2/include @@ -5251,6 +5543,12 @@ agruminolemon.menu.dbg.Serial1=Serial1 agruminolemon.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 agruminolemon.menu.lvl.None____=None agruminolemon.menu.lvl.None____.build.debug_level= +agruminolemon.menu.optim.Smallest=None +agruminolemon.menu.optim.Smallest.build.debug_optim=-Os +agruminolemon.menu.optim.Lite=Lite +agruminolemon.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +agruminolemon.menu.optim.Full=Optimum +agruminolemon.menu.optim.Full.build.debug_optim=-Og agruminolemon.menu.lvl.SSL=SSL agruminolemon.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL agruminolemon.menu.lvl.TLS_MEM=TLS_MEM @@ -5337,6 +5635,238 @@ 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) @@ -5351,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 @@ -5385,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -nodemcu.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -5399,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 @@ -5466,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 @@ -5552,6 +6081,15 @@ 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) @@ -5566,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 @@ -5600,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -nodemcuv2.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -5614,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 @@ -5685,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 @@ -5771,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) @@ -5785,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 @@ -5819,74 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -modwifi.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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 @@ -5920,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 @@ -6006,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 @@ -6020,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 @@ -6054,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -phoenix_v1.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -6067,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 @@ -6138,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 @@ -6224,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 @@ -6238,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 @@ -6272,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -phoenix_v2.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -6285,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 @@ -6356,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 @@ -6442,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 @@ -6456,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 @@ -6490,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -eduinowifi.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -6504,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 @@ -6571,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 @@ -6657,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 @@ -6671,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 @@ -6705,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -wiolink.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -6719,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 @@ -6786,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 @@ -6872,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 @@ -6886,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 @@ -6920,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -blynk.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -6934,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 @@ -7001,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 @@ -7087,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 @@ -7101,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 @@ -7135,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -thing.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -7149,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 @@ -7216,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 @@ -7302,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 @@ -7316,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 @@ -7350,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -thingdev.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -7364,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 @@ -7431,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 @@ -7517,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 @@ -7531,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 @@ -7565,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -esp210.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -7579,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 @@ -7646,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 @@ -7732,6 +8352,15 @@ 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 @@ -7746,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 @@ -7780,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -espinotee.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -7794,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 @@ -7861,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 @@ -7947,6 +8575,15 @@ 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 @@ -7961,6 +8598,7 @@ wifi_kit_8.serial.disableRTS=true wifi_kit_8.build.mcu=esp8266 wifi_kit_8.build.core=esp8266 wifi_kit_8.build.spiffs_pagesize=256 +wifi_kit_8.build.debug_optim= wifi_kit_8.build.debug_port= wifi_kit_8.build.debug_level= wifi_kit_8.menu.xtal.80=80 MHz @@ -7995,10 +8633,10 @@ 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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -wifi_kit_8.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -8009,40 +8647,32 @@ 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_size_bytes=0x400000 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.upload.maximum_size=1044464 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_size_bytes=0x400000 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.upload.maximum_size=1044464 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_size_bytes=0x400000 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.upload.maximum_size=1044464 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_size_bytes=0x400000 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.upload.maximum_size=1044464 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 @@ -8076,6 +8706,12 @@ wifi_kit_8.menu.dbg.Serial1=Serial1 wifi_kit_8.menu.dbg.Serial1.build.debug_port=-DDEBUG_ESP_PORT=Serial1 wifi_kit_8.menu.lvl.None____=None wifi_kit_8.menu.lvl.None____.build.debug_level= +wifi_kit_8.menu.optim.Smallest=None +wifi_kit_8.menu.optim.Smallest.build.debug_optim=-Os +wifi_kit_8.menu.optim.Lite=Lite +wifi_kit_8.menu.optim.Lite.build.debug_optim=-Os -fno-optimize-sibling-calls +wifi_kit_8.menu.optim.Full=Optimum +wifi_kit_8.menu.optim.Full.build.debug_optim=-Og wifi_kit_8.menu.lvl.SSL=SSL wifi_kit_8.menu.lvl.SSL.build.debug_level= -DDEBUG_ESP_SSL wifi_kit_8.menu.lvl.TLS_MEM=TLS_MEM @@ -8162,6 +8798,15 @@ 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 @@ -8176,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 @@ -8210,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -wifiduino.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -8224,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 @@ -8291,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 @@ -8377,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 @@ -8408,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 @@ -8442,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -wifinfo.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -8463,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 @@ -8570,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 @@ -8656,6 +9300,15 @@ 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 @@ -8670,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 @@ -8704,10 +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 External 23LC1024 +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.ext1024k=1M External 64 MBit PSRAM -cw01.menu.mmu.ext1024k.build.mmuflags=-DMMU_EXTERNAL_HEAP=256 -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) @@ -8715,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 @@ -8788,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 @@ -8874,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/eboot.c b/bootloaders/eboot/eboot.c index 6e15d137b2..c3d0c278f7 100644 --- a/bootloaders/eboot/eboot.c +++ b/bootloaders/eboot/eboot.c @@ -17,8 +17,39 @@ #define SWRST do { (*((volatile uint32_t*) 0x60000700)) |= 0x80000000; } while(0); -extern void ets_wdt_enable(void); -extern void ets_wdt_disable(void); +/* + After Power Enable Pin, EXT_RST, or HWDT event, at "main()" in eboot, WDT is + disabled. Key WDT hardware registers are zero. + + After "ESP.restart()" and other soft restarts, at "main()" in eboot, WDT is enabled. + + References for the under-documented ets_wdt_* API + https://mongoose-os.com/blog/esp8266-watchdog-timer/ + http://cholla.mmto.org/esp8266/bootrom/boot.txt + + After looking at esp8266-watchdog-timer some more, `ets_wdt_enable(4, 12, 12)` + is good for eboot's needs. From a ".map" the NON-OS SDK does not use the + ets_wdt_* APIs, so our choices are not too critical. + The SDK will set up the WDT as it wants it. + + A rationale for keeping the "ets_wdt_enable()" line, if the system is not + stable during a "soft restart," the HWDT would provide a recovery reboot. +*/ +extern void ets_wdt_enable(uint32_t mode, uint32_t arg1, uint32_t arg2); +/* + "ets_wdt_disable" + + Diables WDT, then feeds the dog. + For current modes other than 1 or 2, returns the current mode. + For current mode 1, calls ets_timer_disarm, then return the current mode. + For current mode 2, calls ets_isr_mask, then return the current mode. + + I always see a return value of 0xFFFFFFFF. + + The return value would normally be used with ets_wdt_restore; however, that is + not an option since a valid prior call to ets_wdt_enable() may not have been done. +*/ +extern uint32_t ets_wdt_disable(void); int print_version(const uint32_t flash_addr) { @@ -241,12 +272,12 @@ int main() ets_wdt_disable(); res = copy_raw(cmd.args[0], cmd.args[1], cmd.args[2], false); - ets_wdt_enable(); + ets_wdt_enable(4, 12, 12); // WDT about 13 secs. ets_printf("%d\n", res); #if 0 - //devyte: this verify step below (cmp:) only works when the end of copy operation above does not overwrite the - //beginning of the image in the empty area, see #7458. Disabling for now. + //devyte: this verify step below (cmp:) only works when the end of copy operation above does not overwrite the + //beginning of the image in the empty area, see #7458. Disabling for now. //TODO: replace the below verify with hash type, crc, or similar. // Verify the copy ets_printf("cmp:"); @@ -257,7 +288,7 @@ int main() } ets_printf("%d\n", res); -#endif +#endif if (res == 0) { cmd.action = ACTION_LOAD_APP; cmd.args[0] = cmd.args[1]; diff --git a/bootloaders/eboot/eboot.elf b/bootloaders/eboot/eboot.elf index 83384d8109..0e679ff46c 100755 Binary files a/bootloaders/eboot/eboot.elf and b/bootloaders/eboot/eboot.elf differ diff --git a/cores/esp8266/Arduino.h b/cores/esp8266/Arduino.h index 2aab5770c9..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" @@ -186,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); @@ -266,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 @@ -279,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" @@ -297,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/Esp-frag.cpp b/cores/esp8266/Esp-frag.cpp index 4a27ac2e8b..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 / 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 41d0592f31..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 #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,7 @@ void EspClass::reset(void) void EspClass::restart(void) { system_restart(); - esp_yield(); + esp_suspend(); } [[noreturn]] void EspClass::rebootIntoUartDownloadMode() @@ -221,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() { @@ -293,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) @@ -300,6 +304,7 @@ uint32_t EspClass::getFlashChipSize(void) return magicFlashChipSize((bytes[3] & 0xf0) >> 4); } return 0; +#endif } uint32_t EspClass::getFlashChipSpeed(void) @@ -325,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) @@ -345,6 +351,7 @@ uint32_t EspClass::magicFlashChipSize(uint8_t byte) { return 0; } } +#endif uint32_t EspClass::magicFlashChipSpeed(uint8_t byte) { switch(byte & 0x0F) { @@ -465,7 +472,7 @@ bool EspClass::checkFlashCRC() { uint32_t firstPart = (uintptr_t)&__crc_len - 0x40200000; // How many bytes to check before the 1st CRC val // Start the checksum - uint32_t crc = crc32((const void*)0x40200000, firstPart, 0xffffffff); + uint32_t crc = crc32((const void*)0x40200000, firstPart); // Pretend the 2 words of crc/len are zero to be idempotent crc = crc32(z, 8, crc); // Finish the CRC calculation over the rest of flash @@ -513,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) { @@ -525,6 +532,17 @@ bool EspClass::eraseConfig(void) { return true; } +bool EspClass::eraseConfigAndReset(void) { + // Before calling, ensure the WiFi state is equivalent to + // "WiFi.mode(WIFI_OFF)." This will reduce the likelihood of the SDK + // performing WiFi data writes to Flash between erasing and resetting. + bool reset = eraseConfig(); + if (reset) { + hardware_reset(); + } + return reset; +} + uint8_t *EspClass::random(uint8_t *resultArray, const size_t outputSizeBytes) { /** @@ -614,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); @@ -671,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; @@ -718,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; + }; - if (pageBreak) { - size_t byteCount = 4 - (address % 4); + 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; + }; - if (!flashReplaceBlock(address, data, byteCount)) { + constexpr size_t BufferSize { FLASH_PAGE_SIZE }; + alignas(alignof(uint32_t)) uint8_t buf[BufferSize]; + + size_t written = 0; + + if (!isAlignedAddress(address)) { + auto before_address = alignBefore(address); + auto offset = address - before_address; + auto wlen = std::min(Alignment - offset, size); + + if (!flash_read(before_address, &buf[0], Alignment)) { return 0; } - // We will now have aligned address, so we can cross page boundaries - currentOffset += byteCount; - // Realign size to 4 - sizeLeft = (size - byteCount) & ~3; - } - while (sizeLeft) { - size_t willCopy = std::min(sizeLeft, sizeof(alignedData)); - memcpy(alignedData, data + currentOffset, willCopy); - // We now have address, data and size aligned to 4 bytes, so we can use aligned write - if (!flashWrite(address + currentOffset, alignedData, willCopy)) - { +#if PUYA_SUPPORT + if (getFlashChipVendorId() == SPI_FLASH_VENDOR_PUYA) { + for (size_t i = 0; i < wlen ; ++i) { + buf[offset + i] &= data[i]; + } + } else { +#endif + memcpy(&buf[offset], data, wlen); +#if PUYA_SUPPORT + } +#endif + + if (!flash_write(before_address, &buf[0], Alignment)) { return 0; } - sizeLeft -= willCopy; - currentOffset += willCopy; + + address += wlen; + data += wlen; + written += wlen; + size -= wlen; } - return currentOffset; -} + while (size > 0) { + auto len = std::min(size, BufferSize); + auto wlen = aligned(len); -bool EspClass::flashWritePageBreak(uint32_t address, const uint8_t *data, size_t size) { - if (size > 4) { - return false; - } - size_t pageLeft = FLASH_PAGE_SIZE - (address % FLASH_PAGE_SIZE); - size_t offset = 0; - size_t sizeLeft = size; - if (pageLeft > 3) { - return false; - } + if (wlen != len) { + auto partial = wlen - Alignment; + if (!flash_read(address + partial, &buf[partial], Alignment)) { + return written; + } + } - if (!flashReplaceBlock(address, data, pageLeft)) { - return false; - } - offset += pageLeft; - sizeLeft -= pageLeft; - // We replaced last 4-byte block of the page, now we write the remainder in next page - if (!flashReplaceBlock(address + offset, data + offset, sizeLeft)) { - return false; + memcpy(&buf[0], data, len); + if (!flashWrite(address, reinterpret_cast(&buf[0]), wlen)) { + return written; + } + + address += len; + data += len; + written += len; + size -= len; } - return true; + + return written; } bool EspClass::flashWrite(uint32_t address, const uint32_t *data, size_t size) { - SpiFlashOpResult rc = SPI_FLASH_RESULT_OK; - bool pageBreak = ((address % 4) != 0 && (address / FLASH_PAGE_SIZE) != ((address + size - 1) / FLASH_PAGE_SIZE)); - - if ((uintptr_t)data % 4 != 0 || size % 4 != 0 || pageBreak) { - return false; - } + SpiFlashOpResult result; #if PUYA_SUPPORT if (getFlashChipVendorId() == SPI_FLASH_VENDOR_PUYA) { - rc = spi_flash_write_puya(address, const_cast(data), size); + result = spi_flash_write_puya(address, const_cast(data), size); } else -#endif // PUYA_SUPPORT +#endif { - rc = spi_flash_write(address, const_cast(data), size); + result = spi_flash_write_page_break(address, const_cast(data), size); } - return rc == SPI_FLASH_RESULT_OK; + return result == SPI_FLASH_RESULT_OK; } bool EspClass::flashWrite(uint32_t address, const uint8_t *data, size_t size) { - if (size == 0) { - return true; - } - - size_t sizeLeft = size & ~3; - size_t currentOffset = 0; - - if (sizeLeft) { - if ((uintptr_t)data % 4 != 0) { - size_t written = flashWriteUnalignedMemory(address, data, size); - if (!written) { - return false; - } - currentOffset += written; - sizeLeft -= written; - } else { - bool pageBreak = ((address % 4) != 0 && (address / FLASH_PAGE_SIZE) != ((address + sizeLeft - 1) / FLASH_PAGE_SIZE)); - - if (pageBreak) { - while (sizeLeft) { - // We cannot cross page boundary, but the write must be 4 byte aligned, - // so this is the maximum amount we can write - size_t pageBoundary = (FLASH_PAGE_SIZE - ((address + currentOffset) % FLASH_PAGE_SIZE)) & ~3; - - if (sizeLeft > pageBoundary) { - // Aligned write up to page boundary - if (!flashWrite(address + currentOffset, (uint32_t *)(data + currentOffset), pageBoundary)) { - return false; - } - currentOffset += pageBoundary; - sizeLeft -= pageBoundary; - // Cross the page boundary - if (!flashWritePageBreak(address + currentOffset, data + currentOffset, 4)) { - return false; - } - currentOffset += 4; - sizeLeft -= 4; - } else { - // We do not cross page boundary - if (!flashWrite(address + currentOffset, (uint32_t *)(data + currentOffset), sizeLeft)) { - return false; - } - currentOffset += sizeLeft; - sizeLeft = 0; - } - } - } else { - // Pointer is properly aligned and write does not cross page boundary, - // so use aligned write - if (!flashWrite(address, (uint32_t *)data, sizeLeft)) { - return false; - } - currentOffset = sizeLeft; - sizeLeft = 0; - } - } - } - sizeLeft = size - currentOffset; - if (sizeLeft > 0) { - // Size was not aligned, so we have some bytes left to write, we also need to recheck for - // page boundary crossing - bool pageBreak = ((address % 4) != 0 && (address / FLASH_PAGE_SIZE) != ((address + sizeLeft - 1) / FLASH_PAGE_SIZE)); - - if (pageBreak) { - // Cross the page boundary - if (!flashWritePageBreak(address + currentOffset, data + currentOffset, sizeLeft)) { - return false; - } - } else { - // Just write partial block - flashReplaceBlock(address + currentOffset, data + currentOffset, sizeLeft); + if (data && size) { + if (!isAlignedAddress(address) + || !isAlignedPointer(data) + || !isAlignedSize(size)) + { + return flashWriteUnalignedMemory(address, data, size) == size; } + + return flashWrite(address, reinterpret_cast(data), size); } - return true; + return false; } bool EspClass::flashRead(uint32_t address, uint8_t *data, size_t size) { @@ -914,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; @@ -938,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; @@ -971,7 +961,7 @@ String EspClass::getSketchMD5() md5.begin(); while( lengthLeft > 0) { size_t readBytes = (lengthLeft < bufSize) ? lengthLeft : bufSize; - if (!flashRead(offset, reinterpret_cast(buf.get()), (readBytes + 3) & ~3)) { + if (!flashRead(offset, reinterpret_cast(buf.get()), (readBytes + 3) & ~3)) { return emptyString; } md5.add(buf.get(), readBytes); diff --git a/cores/esp8266/Esp.h b/cores/esp8266/Esp.h index 727ca7bb3b..9cb4141292 100644 --- a/cores/esp8266/Esp.h +++ b/cores/esp8266/Esp.h @@ -103,21 +103,23 @@ class EspClass { static void reset(); static void restart(); - /** - * @brief When calling this method the ESP8266 reboots into the UART download mode without - * the need of any external wiring. This is the same mode which can also be entered by - * pulling GPIO0=low, GPIO2=high, GPIO15=low and resetting the ESP8266. - */ + /** + * @brief When calling this method the ESP8266 reboots into the UART download mode without + * the need of any external wiring. This is the same mode which can also be entered by + * pulling GPIO0=low, GPIO2=high, GPIO15=low and resetting the ESP8266. + */ [[noreturn]] static void rebootIntoUartDownloadMode(); static uint16_t getVcc(); static uint32_t getChipId(); static uint32_t getFreeHeap(); - static uint16_t getMaxFreeBlockSize(); +#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); - + 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(); @@ -210,6 +212,22 @@ class EspClass { static bool eraseConfig(); + /** + * @brief Erases 4 sectors at the end of flash, 1 - RF_CAL and 3 - SYSTEMPARM. + * These are the same additional sectors that are erase when you select + * Erase Flash: "Sketch + WiFi Settings" from the Arduino IDE Tools menu. + * + * This operation erases the running SDK's flash configuration space. + * As a precaution before calling, first call "WiFi.mode(WIFI_OFF)." + * + * If you need to erase "WiFi Settings" and reboot consider using + * "ArduinoOTA.eraseConfigAndReset()" it handles shutting down WiFi + * before the erase. + * @return bool result of operation. Always False on return. + * Function does not return on success. + */ + static bool eraseConfigAndReset(); + static uint8_t *random(uint8_t *resultArray, const size_t outputSizeBytes); static uint32_t random(); @@ -251,39 +269,18 @@ class EspClass { */ static void resetHeap(); private: - /** - * @brief Replaces @a byteCount bytes of a 4 byte block on flash - * - * @param address flash address - * @param value buffer with data - * @param byteCount number of bytes to replace - * @return bool result of operation - * @retval true success - * @retval false failed to read/write or invalid args - */ - static bool flashReplaceBlock(uint32_t address, const uint8_t *value, uint32_t byteCount); /** * @brief Write up to @a size bytes from @a data to flash at @a address - * This function takes case of unaligned memory access by copying @a data to a temporary buffer, - * it also takes care of page boundary crossing see @a flashWritePageBreak as to why it's done. - * Less than @a size bytes may be written, due to 4 byte alignment requirement of spi_flash_write + * This function handles all cases of unaligned memory acccess; when either + * address is not aligned, data pointer is not aligned or size is not a multiple of 4. + * User of this function should note that @a data will be copied into a buffer allocated on stack. + * * @param address address on flash where write should start * @param data input buffer * @param size amount of data * @return size_t amount of data written, 0 on failure */ static size_t flashWriteUnalignedMemory(uint32_t address, const uint8_t *data, size_t size); - /** - * @brief Splits up to 4 bytes into 4 byte blocks and writes them to flash - * We need this since spi_flash_write cannot handle writing over a page boundary with unaligned offset - * i.e. spi_flash_write(254, data, 4) will fail, also we cannot write less bytes as in - * spi_flash_write(254, data, 2) since it will be extended internally to 4 bytes and fail - * @param address start of write - * @param data data to be written - * @param size amount of data, must be < 4 - * @return bool result of operation - */ - static bool flashWritePageBreak(uint32_t address, const uint8_t *data, size_t size); }; extern EspClass ESP; diff --git a/cores/esp8266/FS.cpp b/cores/esp8266/FS.cpp index d9e4209c0e..edfc1f8b49 100644 --- a/cores/esp8266/FS.cpp +++ b/cores/esp8266/FS.cpp @@ -173,18 +173,15 @@ File File::openNextFile() { return _fakeDir->openFile("r"); } -String File::readString() -{ +String File::readString() { String ret; ret.reserve(size() - position()); - char temp[256+1]; - int countRead = readBytes(temp, sizeof(temp)-1); - while (countRead > 0) - { - temp[countRead] = 0; - ret += temp; - countRead = readBytes(temp, sizeof(temp)-1); - } + uint8_t temp[256]; + int countRead; + do { + countRead = read(temp, sizeof(temp)); + ret.concat((const char*)temp, countRead); + } while (countRead > 0); return ret; } diff --git a/cores/esp8266/FS.h b/cores/esp8266/FS.h index f8d68d56ba..55305e9688 100644 --- a/cores/esp8266/FS.h +++ b/cores/esp8266/FS.h @@ -90,9 +90,8 @@ class File : public Stream uint8_t obuf[256]; size_t doneLen = 0; size_t sentLen; - int i; - while (src.available() > sizeof(obuf)){ + while (src.available() > (int)sizeof(obuf)){ src.read(obuf, sizeof(obuf)); sentLen = write(obuf, sizeof(obuf)); doneLen = doneLen + sentLen; @@ -118,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; 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/HardwareSerial.cpp b/cores/esp8266/HardwareSerial.cpp index 88b47b33f3..4896e27758 100644 --- a/cores/esp8266/HardwareSerial.cpp +++ b/cores/esp8266/HardwareSerial.cpp @@ -143,7 +143,7 @@ unsigned long HardwareSerial::detectBaudrate(time_t timeoutMillis) if ((detectedBaudrate = testBaudrate())) { break; } - delay(100); + esp_delay(100); } return detectedBaudrate; } diff --git a/cores/esp8266/HardwareSerial.h b/cores/esp8266/HardwareSerial.h index ea17f60efa..3ef45892e6 100644 --- a/cores/esp8266/HardwareSerial.h +++ b/cores/esp8266/HardwareSerial.h @@ -183,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); 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 0bd04ebcfe..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 initialized 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 86f8849a91..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 initial boot sequence + return *this; } -}; -// wifi_softap_set_station_info is missing in user_interface.h: -extern "C" void wifi_softap_set_station_info(uint8_t* mac, struct ipv4_addr*); - -void DhcpServer::dhcps_set_dns(int num, const ipv4_addr_t* dns) -{ - (void)num; - if (!ip4_addr_isany(dns)) + if ((size_t)(_end - _it) < (size + 2)) { - ip4_addr_copy(dns_address, *dns); + return *this; } + + *_it++ = code; + *_it++ = size; + + memcpy_P(_it, data, size); + _it += size; + + return *this; } +//////////////////////////////////////////////////////////////////////////////////// + +DhcpServer::DhcpServer(netif* netif) : _netif(netif) { } + +// wifi_softap_set_station_info is missing in user_interface.h: +extern "C" void wifi_softap_set_station_info(uint8_t* mac, struct ipv4_addr*); + /****************************************************************************** FunctionName : node_insert_to_list Description : insert the node to the list 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 + Set DHCP msg offer options for the given server - @param optptr -- DHCP msg - @param type -- option - - @return uint8_t* DHCP msg + @param buffer -- DHCP options buffer + @param server -- DHCP server instance */ /////////////////////////////////////////////////////////////////////////////////// -uint8_t* DhcpServer::add_msg_type(uint8_t *optptr, uint8_t type) +void DhcpServer::add_offer_options(OptionsBuffer& options) { + 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 uninitialized (??) - *optptr++ = 43; // vendor specific - *optptr++ = 6; - // uninitialized ? -#endif + { + const auto* addr = ip_2_ip4(&_netif->ip_addr); + const auto* mask = ip_2_ip4(&_netif->netmask); + const auto broadcast = ip4_addr_t { .addr = (addr->addr & mask->addr) | ~mask->addr }; -#if 0 // already set (DHCP_OPTION_SUBNET_MASK==1) (??) - *optptr++ = 0x01; - *optptr++ = 4; - *optptr++ = 0; - *optptr++ = 0; - *optptr++ = 0; - *optptr++ = 2; -#endif + options.add(DHCP_OPTION_BROADCAST_ADDRESS, &broadcast); + } - return optptr; + // TODO: _netif->mtu ? + static constexpr uint16_t Mtu { 1500 }; + options.add(DHCP_OPTION_INTERFACE_MTU, Mtu); -#undef ipadd -#undef if_ip + static constexpr uint8_t RouterDiscovery { 0 }; + options.add(DHCP_OPTION_PERFORM_ROUTER_DISCOVERY, RouterDiscovery); } -/////////////////////////////////////////////////////////////////////////////////// -/* - DHCP msg - @param optptr -- DHCP msg - - @return uint8_t* DHCP msg -*/ /////////////////////////////////////////////////////////////////////////////////// -uint8_t* DhcpServer::add_end(uint8_t *optptr) -{ - - *optptr++ = DHCP_OPTION_END; - return optptr; -} /////////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////////// -void DhcpServer::create_msg(struct dhcps_msg *m) +DhcpServer::OptionsBuffer DhcpServer::create_msg(struct dhcps_msg* m) { struct ipv4_addr client; 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; + + auto options = create_msg(m); + options.add(DHCP_OPTION_MSG_TYPE, DHCPOFFER); + + add_offer_options(options); + if (custom_offer_options) + { + custom_offer_options(*this, options); + } - end = add_msg_type(&m->options[4], DHCPOFFER); - end = add_offer_options(end); - end = add_end(end); + options.add(DHCP_OPTION_END); p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcps_msg), PBUF_RAM); #if DHCPS_DEBUG @@ -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; + + auto options = create_msg(m); + options.add(DHCP_OPTION_MSG_TYPE, DHCPACK); - u8_t *end; - struct pbuf *p, *q; - u8_t *data; - u16_t cnt = 0; - u16_t i; - create_msg(m); + add_offer_options(options); + if (custom_offer_options) + { + custom_offer_options(*this, options); + } - end = add_msg_type(&m->options[4], DHCPACK); - end = add_offer_options(end); - end = add_end(end); + options.add(DHCP_OPTION_END); p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcps_msg), PBUF_RAM); #if DHCPS_DEBUG @@ -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) { @@ -1141,8 +982,8 @@ bool DhcpServer::set_dhcps_lease(struct dhcps_lease *please) // - is wrong // - limited to /24 address plans softap_ip = htonl(ip_2_ip4(&_netif->ip_addr)->addr); - start_ip = htonl(please->start_ip.addr); - end_ip = htonl(please->end_ip.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)) { @@ -1151,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; } @@ -1162,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; } @@ -1180,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) { @@ -1197,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()) { @@ -1206,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); @@ -1253,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; @@ -1277,8 +1118,8 @@ void DhcpServer::dhcps_coarse_tmr(void) } else { - pnode = pnode ->pnext; - num_dhcps_pool ++; + pnode = pnode->pnext; + num_dhcps_pool++; } } @@ -1288,85 +1129,10 @@ void DhcpServer::dhcps_coarse_tmr(void) } } -bool DhcpServer::set_dhcps_offer_option(uint8 level, void* optarg) -{ - bool offer_flag = true; - //uint8 option = 0; - if (optarg == nullptr && !isRunning()) - { - return false; - } - - if (level <= OFFER_START || level >= OFFER_END) - { - return false; - } - - switch (level) - { - case OFFER_ROUTER: - offer = (*(uint8 *)optarg) & 0x01; - offer_flag = true; - break; - default : - offer_flag = false; - break; - } - return offer_flag; -} - -bool DhcpServer::set_dhcps_lease_time(uint32 minute) -{ - if (_netif->num == SOFTAP_IF) - { - uint8 opmode = wifi_get_opmode(); - if (opmode == STATION_MODE || opmode == NULL_MODE) - { - return false; - } - } - - if (isRunning()) - { - return false; - } - - if (minute == 0) - { - return false; - } - dhcps_lease_time = minute; - return true; -} - -bool DhcpServer::reset_dhcps_lease_time(void) +void DhcpServer::dhcps_client_leave(u8* bssid, struct ipv4_addr* ip, bool force) { - 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)) { @@ -1412,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; @@ -1447,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; @@ -1496,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) { @@ -1525,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 { @@ -1544,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) @@ -1566,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; @@ -1581,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; @@ -1592,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 b4a4807b9a..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,7 @@ bool LwipIntf::ipAddressReorder(const IPAddress& local_ip, const IPAddress& arg1 */ String LwipIntf::hostname(void) { - return wifi_station_get_hostname(); + return wifi_station_hostname; } /** @@ -70,7 +108,7 @@ String LwipIntf::hostname(void) */ const char* LwipIntf::getHostname(void) { - return wifi_station_get_hostname(); + return wifi_station_hostname; } /** @@ -131,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) { @@ -162,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 d73c2d825a..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,40 +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); + // ESP32 API compatibility bool setHostname(const char* aHostName) { return hostname(aHostName); } + + // ESP32 API compatibility const char* getHostname(); -protected: + // 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 3354a32563..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 @@ -14,7 +35,7 @@ #include #include -#include // wifi_get_macaddr() +#include // wifi_get_macaddr() #include "SPI.h" #include "Schedule.h" @@ -25,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 = IPADDR_NONE, const IPAddress& dns2 = IPADDR_NONE); + //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 @@ -89,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) { @@ -131,7 +205,25 @@ boolean LwipIntfDev::config(const IPAddress& localIP, const IPAddress& g 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) @@ -161,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)) @@ -182,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; @@ -211,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; @@ -254,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 @@ -300,17 +427,28 @@ err_t LwipIntfDev::netif_init() return ERR_OK; } -template +template void LwipIntfDev::netif_status_callback() +{ + check_route(); + if (connected()) + { + sntp_stop(); + sntp_init(); + } +} + +template +void LwipIntfDev::check_route() { if (connected()) { - if (_default) + 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) { @@ -318,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; } @@ -371,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 @@ -381,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.h b/cores/esp8266/Print.h index e5d8d1b1ef..67f3ba5502 100644 --- a/cores/esp8266/Print.h +++ b/cores/esp8266/Print.h @@ -110,7 +110,10 @@ 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)) 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 5e7e8636c1..b9b5b95f65 100644 --- a/cores/esp8266/Stream.cpp +++ b/cores/esp8266/Stream.cpp @@ -58,16 +58,16 @@ int Stream::timedPeek() { // returns peek of the next digit in the stream or -1 if timeout // discards non-numeric characters -int Stream::peekNextDigit() { +int Stream::peekNextDigit(bool detectDecimal) { int c; while(1) { c = timedPeek(); - if(c < 0) - return c; // timeout - if(c == '-') - return c; - if(c >= '0' && c <= '9') + if( c < 0 || // timeout + c == '-' || + ( c >= '0' && c <= '9' ) || + ( detectDecimal && c == '.' ) ) { return c; + } read(); // discard non-numeric } } @@ -141,7 +141,7 @@ long Stream::parseInt(char skipChar) { long value = 0; int c; - c = peekNextDigit(); + c = peekNextDigit(false); // ignore non numeric leading characters if(c < 0) return 0; // zero returned if timeout @@ -176,7 +176,7 @@ float Stream::parseFloat(char skipChar) { int c; float fraction = 1.0f; - c = peekNextDigit(); + c = peekNextDigit(true); // ignore non numeric leading characters if(c < 0) return 0; // zero returned if timeout @@ -262,6 +262,32 @@ String Stream::readStringUntil(char terminator) { return ret; } +String Stream::readStringUntil(const char* terminator, uint32_t untilTotalNumberOfOccurrences) { + String ret; + int c; + uint32_t occurrences = 0; + size_t termLen = strlen(terminator); + size_t termIndex = 0; + size_t index = 0; + + while ((c = timedRead()) > 0) { + ret += (char) c; + index++; + + if (terminator[termIndex] == c) { + if (++termIndex == termLen && ++occurrences == untilTotalNumberOfOccurrences) { + // don't include terminator in returned string + ret.remove(index - termIndex, termLen); + break; + } + } else { + termIndex = 0; + } + } + + return ret; +} + // read what can be read, immediate exit on unavailable data // prototype similar to Arduino's `int Client::read(buf, len)` int Stream::read (uint8_t* buffer, size_t maxLen) @@ -274,7 +300,7 @@ int Stream::read (uint8_t* buffer, size_t maxLen) int c = read(); if (c == -1) break; - buffer[nbread++] = read(); + buffer[nbread++] = c; } return nbread; } diff --git a/cores/esp8266/Stream.h b/cores/esp8266/Stream.h index 6408befe02..0706bec001 100644 --- a/cores/esp8266/Stream.h +++ b/cores/esp8266/Stream.h @@ -53,7 +53,7 @@ class Stream: public Print { unsigned long _startMillis; // used for timeout measurement int timedRead(); // private method to read stream with timeout int timedPeek(); // private method to peek stream with timeout - int peekNextDigit(); // returns the next numeric digit in the stream or -1 if timeout + int peekNextDigit(bool detectDecimal = false); // returns the next numeric digit in the stream or -1 if timeout public: virtual int available() = 0; @@ -61,6 +61,7 @@ class Stream: public Print { virtual int peek() = 0; Stream() {} + virtual ~Stream() {} // parsing methods @@ -114,6 +115,7 @@ class Stream: public Print { // Arduino String functions to be added here virtual String readString(); String readStringUntil(char terminator); + String readStringUntil(const char* terminator, uint32_t untilTotalNumberOfOccurrences = 1); virtual int read (uint8_t* buffer, size_t len); int read (char* buffer, size_t len) { return read((uint8_t*)buffer, len); } @@ -166,25 +168,53 @@ class Stream: public Print { // When result is 0 or less than requested maxLen, Print::getLastSend() // contains an error reason. +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + + // transfers already buffered / immediately available data (no timeout) + // returns number of transferred bytes + [[deprecated]] size_t sendAvailable (Print* to) { return sendGeneric(to, -1, -1, oneShotMs::alwaysExpired); } + [[deprecated]] size_t sendAvailable (Print& to) { return sendAvailable(&to); } + + // transfers data until timeout + // returns number of transferred bytes + [[deprecated]] size_t sendAll (Print* to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, -1, -1, timeoutMs); } + [[deprecated]] size_t sendAll (Print& to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendAll(&to, timeoutMs); } + + // transfers data until a char is encountered (the char is swallowed but not transferred) with timeout + // returns number of transferred bytes + [[deprecated]] size_t sendUntil (Print* to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, -1, readUntilChar, timeoutMs); } + [[deprecated]] size_t sendUntil (Print& to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendUntil(&to, readUntilChar, timeoutMs); } + + // transfers data until requested size or timeout + // returns number of transferred bytes + [[deprecated]] size_t sendSize (Print* to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, maxLen, -1, timeoutMs); } + [[deprecated]] size_t sendSize (Print& to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendSize(&to, maxLen, timeoutMs); } + +#pragma GCC diagnostic pop + // transfers already buffered / immediately available data (no timeout) // returns number of transferred bytes - size_t sendAvailable (Print* to) { return sendGeneric(to, -1, -1, oneShotMs::alwaysExpired); } - size_t sendAvailable (Print& to) { return sendAvailable(&to); } + size_t sendAvailable (Stream* to) { return sendGeneric(to, -1, -1, oneShotMs::alwaysExpired); } + size_t sendAvailable (Stream& to) { return sendAvailable(&to); } + size_t sendAvailable (Stream&& to) { return sendAvailable(&to); } // transfers data until timeout // returns number of transferred bytes - size_t sendAll (Print* to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, -1, -1, timeoutMs); } - size_t sendAll (Print& to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendAll(&to, timeoutMs); } + size_t sendAll (Stream* to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, -1, -1, timeoutMs); } + size_t sendAll (Stream& to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendAll(&to, timeoutMs); } + size_t sendAll (Stream&& to, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendAll(&to, timeoutMs); } // transfers data until a char is encountered (the char is swallowed but not transferred) with timeout // returns number of transferred bytes - size_t sendUntil (Print* to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, -1, readUntilChar, timeoutMs); } - size_t sendUntil (Print& to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendUntil(&to, readUntilChar, timeoutMs); } + size_t sendUntil (Stream* to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, -1, readUntilChar, timeoutMs); } + size_t sendUntil (Stream& to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendUntil(&to, readUntilChar, timeoutMs); } + size_t sendUntil (Stream&& to, const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendUntil(&to, readUntilChar, timeoutMs); } // transfers data until requested size or timeout // returns number of transferred bytes - size_t sendSize (Print* to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, maxLen, -1, timeoutMs); } - size_t sendSize (Print& to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendSize(&to, maxLen, timeoutMs); } + size_t sendSize (Stream* to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendGeneric(to, maxLen, -1, timeoutMs); } + size_t sendSize (Stream& to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendSize(&to, maxLen, timeoutMs); } + size_t sendSize (Stream&& to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendSize(&to, maxLen, timeoutMs); } // remaining size (-1 by default = unknown) virtual ssize_t streamRemaining () { return -1; } @@ -201,11 +231,17 @@ class Stream: public Print { Report getLastSendReport () const { return _sendReport; } protected: + [[deprecated]] size_t sendGeneric (Print* to, const ssize_t len = -1, const int readUntilChar = -1, oneShotMs::timeType timeoutMs = oneShotMs::neverExpires /* neverExpires=>getTimeout() */); + size_t sendGeneric (Stream* to, + const ssize_t len = -1, + const int readUntilChar = -1, + oneShotMs::timeType timeoutMs = oneShotMs::neverExpires /* neverExpires=>getTimeout() */); + size_t SendGenericPeekBuffer(Print* to, const ssize_t len, const int readUntilChar, const oneShotMs::timeType timeoutMs); size_t SendGenericRegularUntil(Print* to, const ssize_t len, const int readUntilChar, const oneShotMs::timeType timeoutMs); size_t SendGenericRegular(Print* to, const ssize_t len, const oneShotMs::timeType timeoutMs); diff --git a/cores/esp8266/StreamDev.h b/cores/esp8266/StreamDev.h index b61a0df55a..2aeee4c87a 100644 --- a/cores/esp8266/StreamDev.h +++ b/cores/esp8266/StreamDev.h @@ -160,6 +160,7 @@ class StreamConstPtr: public StreamNull 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)) { } diff --git a/cores/esp8266/StreamSend.cpp b/cores/esp8266/StreamSend.cpp index c351575463..b46d1c1560 100644 --- a/cores/esp8266/StreamSend.cpp +++ b/cores/esp8266/StreamSend.cpp @@ -19,20 +19,25 @@ parsing functions based on TextFinder library by Michael Margolis */ - #include #include -size_t Stream::sendGeneric(Print* to, - const ssize_t len, - const int readUntilChar, +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 + return 0; // conveniently avoids timeout for no requested data } // There are two timeouts: @@ -46,25 +51,63 @@ size_t Stream::sendGeneric(Print* to, if (hasPeekBufferAPI()) { - return SendGenericPeekBuffer(to, len, readUntilChar, timeoutMs); + return SendGenericPeekBuffer(to, len, readUntilChar, mainTimeoutMs); } if (readUntilChar >= 0) { - return SendGenericRegularUntil(to, len, readUntilChar, timeoutMs); + return SendGenericRegularUntil(to, len, readUntilChar, mainTimeoutMs); } - return SendGenericRegular(to, len, timeoutMs); + return SendGenericRegular(to, len, mainTimeoutMs); } - -size_t Stream::SendGenericPeekBuffer(Print* to, const ssize_t len, const int readUntilChar, const esp8266::polledTimeout::oneShotFastMs::timeType timeoutMs) +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 timedOut(timeoutMs >= esp8266::polledTimeout::oneShotFastMs::neverExpires ? getTimeout() : timeoutMs); + 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; + const size_t maxLen = std::max((ssize_t)0, len); + size_t written = 0; while (!maxLen || written < maxLen) { @@ -90,13 +133,13 @@ size_t Stream::SendGenericPeekBuffer(Print* to, const ssize_t len, const int rea if (w) { const char* directbuf = peekBuffer(); - bool foundChar = false; + 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); + w = std::min((size_t)(last - directbuf), w); foundChar = true; } } @@ -104,7 +147,7 @@ size_t Stream::SendGenericPeekBuffer(Print* to, const ssize_t len, const int rea { peekConsume(w); written += w; - timedOut.reset(); // something has been written + timedOut.reset(); // something has been written } if (foundChar) { @@ -145,16 +188,18 @@ size_t Stream::SendGenericPeekBuffer(Print* to, const ssize_t len, const int rea return written; } -size_t Stream::SendGenericRegularUntil(Print* to, const ssize_t len, const int readUntilChar, const esp8266::polledTimeout::oneShotFastMs::timeType timeoutMs) +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 - // "neverExpires (default, impossible)" is translated to default timeout - esp8266::polledTimeout::oneShotFastMs timedOut(timeoutMs >= esp8266::polledTimeout::oneShotFastMs::neverExpires ? getTimeout() : timeoutMs); + esp8266::polledTimeout::oneShotFastMs timedOut(timeoutMs); + // len==-1 => maxLen=0 <=> until starvation - const size_t maxLen = std::max((ssize_t)0, len); - size_t written = 0; + const size_t maxLen = std::max((ssize_t)0, len); + size_t written = 0; while (!maxLen || written < maxLen) { @@ -186,7 +231,7 @@ size_t Stream::SendGenericRegularUntil(Print* to, const ssize_t len, const int r break; } written += 1; - timedOut.reset(); // something has been written + timedOut.reset(); // something has been written } if (timedOut) @@ -221,16 +266,17 @@ size_t Stream::SendGenericRegularUntil(Print* to, const ssize_t len, const int r return written; } -size_t Stream::SendGenericRegular(Print* to, const ssize_t len, const esp8266::polledTimeout::oneShotFastMs::timeType timeoutMs) +size_t Stream::SendGenericRegular(Print* to, const ssize_t len, + const esp8266::polledTimeout::oneShotFastMs::timeType timeoutMs) { // regular Stream API // use an intermediary buffer - // "neverExpires (default, impossible)" is translated to default timeout - esp8266::polledTimeout::oneShotFastMs timedOut(timeoutMs >= esp8266::polledTimeout::oneShotFastMs::neverExpires ? getTimeout() : timeoutMs); + esp8266::polledTimeout::oneShotFastMs timedOut(timeoutMs); + // len==-1 => maxLen=0 <=> until starvation - const size_t maxLen = std::max((ssize_t)0, len); - size_t written = 0; + const size_t maxLen = std::max((ssize_t)0, len); + size_t written = 0; while (!maxLen || written < maxLen) { @@ -243,7 +289,7 @@ size_t Stream::SendGenericRegular(Print* to, const ssize_t len, const esp8266::p size_t w = to->availableForWrite(); if (w == 0 && !to->outputCanTimeout()) - // no more data can be written, ever + // no more data can be written, ever { break; } @@ -256,7 +302,7 @@ size_t Stream::SendGenericRegular(Print* to, const ssize_t len, const esp8266::p w = std::min(w, (decltype(w))temporaryStackBufferSize); if (w) { - char temp[w]; + char temp[w]; ssize_t r = read(temp, w); if (r < 0) { @@ -270,7 +316,7 @@ size_t Stream::SendGenericRegular(Print* to, const ssize_t len, const esp8266::p setReport(Report::WriteError); break; } - timedOut.reset(); // something has been written + timedOut.reset(); // something has been written } if (timedOut) @@ -305,19 +351,19 @@ size_t Stream::SendGenericRegular(Print* to, const ssize_t len, const esp8266::p return written; } -Stream& operator << (Stream& out, String& string) +Stream& operator<<(Stream& out, String& string) { StreamConstPtr(string).sendAll(out); return out; } -Stream& operator << (Stream& out, StreamString& stream) +Stream& operator<<(Stream& out, StreamString& stream) { stream.sendAll(out); return out; } -Stream& operator << (Stream& out, Stream& stream) +Stream& operator<<(Stream& out, Stream& stream) { if (stream.streamRemaining() < 0) { @@ -339,13 +385,13 @@ Stream& operator << (Stream& out, Stream& stream) return out; } -Stream& operator << (Stream& out, const char* text) +Stream& operator<<(Stream& out, const char* text) { - StreamConstPtr(text).sendAll(out); + StreamConstPtr(text, strlen_P(text)).sendAll(out); return out; } -Stream& operator << (Stream& out, const __FlashStringHelper* text) +Stream& operator<<(Stream& out, const __FlashStringHelper* text) { StreamConstPtr(text).sendAll(out); return out; diff --git a/cores/esp8266/StreamString.h b/cores/esp8266/StreamString.h index 264604f7a4..dced5aee80 100644 --- a/cores/esp8266/StreamString.h +++ b/cores/esp8266/StreamString.h @@ -24,25 +24,20 @@ #define __STREAMSTRING_H #include +#include +#include "Stream.h" #include "WString.h" /////////////////////////////////////////////////////////////// -// S2Stream points to a String and makes it a Stream +// S2Stream ("String to Stream") points to a String and makes it a Stream // (it is also the helper for StreamString) class S2Stream: public Stream { public: + S2Stream(String& string, int peekPointer = -1) : string(&string), peekPointer(peekPointer) { } - S2Stream(String& string, int peekPointer = -1): - string(&string), peekPointer(peekPointer) - { - } - - S2Stream(String* string, int peekPointer = -1): - string(string), peekPointer(peekPointer) - { - } + S2Stream(String* string, int peekPointer = -1) : string(string), peekPointer(peekPointer) { } virtual int available() override { @@ -189,34 +184,31 @@ class S2Stream: public Stream return peekPointer < 0 ? string->length() : string->length() - peekPointer; } - // calling setConsume() will consume bytes as the stream is read - // (enabled by default) + // calling setConsume() will make the string consumed as the stream is read. + // (default behaviour) void setConsume() { peekPointer = -1; } - // Reading this stream will mark the string as read without consuming - // (not enabled by default) - // Calling resetPointer() resets the read state and allows rereading. - void resetPointer(int pointer = 0) + // Calling resetPointer() resets the read cursor and allows rereading. + // (this is the opposite of default mode set by setConsume()) + void resetPointer(size_t pointer = 0) { - peekPointer = pointer; + peekPointer = std::min(pointer, (size_t)string->length()); } protected: - String* string; - int peekPointer; // -1:String is consumed / >=0:resettable pointer + 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) @@ -226,55 +218,68 @@ class StreamString: public String, public S2Stream } public: - - StreamString(StreamString&& bro): String(bro), S2Stream(this) { } - StreamString(const StreamString& bro): String(bro), S2Stream(this) { } + 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) + 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) + StreamString& operator=(const String& rhs) { String::operator=(rhs); resetpp(); return *this; } - StreamString& operator= (const char* cstr) + StreamString& operator=(const char* cstr) { String::operator=(cstr); resetpp(); return *this; } - StreamString& operator= (const __FlashStringHelper* str) + StreamString& operator=(const __FlashStringHelper* str) { String::operator=(str); resetpp(); return *this; } - StreamString& operator= (String&& rval) + StreamString& operator=(String&& rval) { String::operator=(rval); resetpp(); @@ -282,4 +287,4 @@ class StreamString: public String, public S2Stream } }; -#endif // __STREAMSTRING_H +#endif // __STREAMSTRING_H diff --git a/cores/esp8266/TZ.h b/cores/esp8266/TZ.h index ea53c363a8..478abb16c0 100644 --- a/cores/esp8266/TZ.h +++ b/cores/esp8266/TZ.h @@ -1,22 +1,16 @@ +// ! ! ! DO NOT EDIT, AUTOMATICALLY GENERATED ! ! ! +// File created 2024-02-05 00:00:00.000000+00:00 +// Based on IANA database 2023d +// Re-run /tools/tools/format_tzdata.py to update -// autogenerated from https://raw.githubusercontent.com/nayarsystems/posix_tz_db/master/zones.csv -// by script /tools/TZupdate.sh -// Mon Jul 26 20:04:37 UTC 2021 -// -// This database is autogenerated from IANA timezone database -// https://raw.githubusercontent.com/nayarsystems/posix_tz_db/master/zones.csv -// (using https://www.iana.org/time-zones) -// and can be updated on demand in this repository -// or by yourself using the above script - -#ifndef TZDB_H -#define TZDB_H +#pragma once #define TZ_Africa_Abidjan PSTR("GMT0") #define TZ_Africa_Accra PSTR("GMT0") #define TZ_Africa_Addis_Ababa PSTR("EAT-3") #define TZ_Africa_Algiers PSTR("CET-1") #define TZ_Africa_Asmara PSTR("EAT-3") +#define TZ_Africa_Asmera PSTR("EAT-3") #define TZ_Africa_Bamako PSTR("GMT0") #define TZ_Africa_Bangui PSTR("WAT-1") #define TZ_Africa_Banjul PSTR("GMT0") @@ -24,7 +18,7 @@ #define TZ_Africa_Blantyre PSTR("CAT-2") #define TZ_Africa_Brazzaville PSTR("WAT-1") #define TZ_Africa_Bujumbura PSTR("CAT-2") -#define TZ_Africa_Cairo PSTR("EET-2") +#define TZ_Africa_Cairo PSTR("EET-2EEST,M4.5.5/0,M10.5.4/24") #define TZ_Africa_Casablanca PSTR("<+01>-1") #define TZ_Africa_Ceuta PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Africa_Conakry PSTR("GMT0") @@ -37,7 +31,7 @@ #define TZ_Africa_Gaborone PSTR("CAT-2") #define TZ_Africa_Harare PSTR("CAT-2") #define TZ_Africa_Johannesburg PSTR("SAST-2") -#define TZ_Africa_Juba PSTR("EAT-3") +#define TZ_Africa_Juba PSTR("CAT-2") #define TZ_Africa_Kampala PSTR("EAT-3") #define TZ_Africa_Khartoum PSTR("CAT-2") #define TZ_Africa_Kigali PSTR("CAT-2") @@ -61,6 +55,7 @@ #define TZ_Africa_Ouagadougou PSTR("GMT0") #define TZ_Africa_PortomNovo PSTR("WAT-1") #define TZ_Africa_Sao_Tome PSTR("GMT0") +#define TZ_Africa_Timbuktu PSTR("GMT0") #define TZ_Africa_Tripoli PSTR("EET-2") #define TZ_Africa_Tunis PSTR("CET-1") #define TZ_Africa_Windhoek PSTR("CAT-2") @@ -71,6 +66,7 @@ #define TZ_America_Araguaina PSTR("<-03>3") #define TZ_America_Argentina_Buenos_Aires PSTR("<-03>3") #define TZ_America_Argentina_Catamarca PSTR("<-03>3") +#define TZ_America_Argentina_ComodRivadavia PSTR("<-03>3") #define TZ_America_Argentina_Cordoba PSTR("<-03>3") #define TZ_America_Argentina_Jujuy PSTR("<-03>3") #define TZ_America_Argentina_La_Rioja PSTR("<-03>3") @@ -84,8 +80,9 @@ #define TZ_America_Aruba PSTR("AST4") #define TZ_America_Asuncion PSTR("<-04>4<-03>,M10.1.0/0,M3.4.0/0") #define TZ_America_Atikokan PSTR("EST5") +#define TZ_America_Atka PSTR("HST10HDT,M3.2.0,M11.1.0") #define TZ_America_Bahia PSTR("<-03>3") -#define TZ_America_Bahia_Banderas PSTR("CST6CDT,M4.1.0,M10.5.0") +#define TZ_America_Bahia_Banderas PSTR("CST6") #define TZ_America_Barbados PSTR("AST4") #define TZ_America_Belem PSTR("<-03>3") #define TZ_America_Belize PSTR("CST6") @@ -93,14 +90,19 @@ #define TZ_America_Boa_Vista PSTR("<-04>4") #define TZ_America_Bogota PSTR("<-05>5") #define TZ_America_Boise PSTR("MST7MDT,M3.2.0,M11.1.0") +#define TZ_America_Buenos_Aires PSTR("<-03>3") #define TZ_America_Cambridge_Bay PSTR("MST7MDT,M3.2.0,M11.1.0") #define TZ_America_Campo_Grande PSTR("<-04>4") #define TZ_America_Cancun PSTR("EST5") #define TZ_America_Caracas PSTR("<-04>4") +#define TZ_America_Catamarca PSTR("<-03>3") #define TZ_America_Cayenne PSTR("<-03>3") #define TZ_America_Cayman PSTR("EST5") #define TZ_America_Chicago PSTR("CST6CDT,M3.2.0,M11.1.0") -#define TZ_America_Chihuahua PSTR("MST7MDT,M4.1.0,M10.5.0") +#define TZ_America_Chihuahua PSTR("CST6") +#define TZ_America_Ciudad_Juarez PSTR("MST7MDT,M3.2.0,M11.1.0") +#define TZ_America_Coral_Harbour PSTR("EST5") +#define TZ_America_Cordoba PSTR("<-03>3") #define TZ_America_Costa_Rica PSTR("CST6") #define TZ_America_Creston PSTR("MST7") #define TZ_America_Cuiaba PSTR("<-04>4") @@ -114,10 +116,12 @@ #define TZ_America_Edmonton PSTR("MST7MDT,M3.2.0,M11.1.0") #define TZ_America_Eirunepe PSTR("<-05>5") #define TZ_America_El_Salvador PSTR("CST6") -#define TZ_America_Fortaleza PSTR("<-03>3") +#define TZ_America_Ensenada PSTR("PST8PDT,M3.2.0,M11.1.0") #define TZ_America_Fort_Nelson PSTR("MST7") +#define TZ_America_Fort_Wayne PSTR("EST5EDT,M3.2.0,M11.1.0") +#define TZ_America_Fortaleza PSTR("<-03>3") #define TZ_America_Glace_Bay PSTR("AST4ADT,M3.2.0,M11.1.0") -#define TZ_America_Godthab PSTR("<-03>3<-02>,M3.5.0/-2,M10.5.0/-1") +#define TZ_America_Godthab PSTR("<-02>2<-01>,M3.5.0/-1,M10.5.0/0") #define TZ_America_Goose_Bay PSTR("AST4ADT,M3.2.0,M11.1.0") #define TZ_America_Grand_Turk PSTR("EST5EDT,M3.2.0,M11.1.0") #define TZ_America_Grenada PSTR("AST4") @@ -136,16 +140,20 @@ #define TZ_America_Indiana_Vevay PSTR("EST5EDT,M3.2.0,M11.1.0") #define TZ_America_Indiana_Vincennes PSTR("EST5EDT,M3.2.0,M11.1.0") #define TZ_America_Indiana_Winamac PSTR("EST5EDT,M3.2.0,M11.1.0") +#define TZ_America_Indianapolis PSTR("EST5EDT,M3.2.0,M11.1.0") #define TZ_America_Inuvik PSTR("MST7MDT,M3.2.0,M11.1.0") #define TZ_America_Iqaluit PSTR("EST5EDT,M3.2.0,M11.1.0") #define TZ_America_Jamaica PSTR("EST5") +#define TZ_America_Jujuy PSTR("<-03>3") #define TZ_America_Juneau PSTR("AKST9AKDT,M3.2.0,M11.1.0") #define TZ_America_Kentucky_Louisville PSTR("EST5EDT,M3.2.0,M11.1.0") #define TZ_America_Kentucky_Monticello PSTR("EST5EDT,M3.2.0,M11.1.0") +#define TZ_America_Knox_IN PSTR("CST6CDT,M3.2.0,M11.1.0") #define TZ_America_Kralendijk PSTR("AST4") #define TZ_America_La_Paz PSTR("<-04>4") #define TZ_America_Lima PSTR("<-05>5") #define TZ_America_Los_Angeles PSTR("PST8PDT,M3.2.0,M11.1.0") +#define TZ_America_Louisville PSTR("EST5EDT,M3.2.0,M11.1.0") #define TZ_America_Lower_Princes PSTR("AST4") #define TZ_America_Maceio PSTR("<-03>3") #define TZ_America_Managua PSTR("CST6") @@ -153,14 +161,15 @@ #define TZ_America_Marigot PSTR("AST4") #define TZ_America_Martinique PSTR("AST4") #define TZ_America_Matamoros PSTR("CST6CDT,M3.2.0,M11.1.0") -#define TZ_America_Mazatlan PSTR("MST7MDT,M4.1.0,M10.5.0") +#define TZ_America_Mazatlan PSTR("MST7") +#define TZ_America_Mendoza PSTR("<-03>3") #define TZ_America_Menominee PSTR("CST6CDT,M3.2.0,M11.1.0") -#define TZ_America_Merida PSTR("CST6CDT,M4.1.0,M10.5.0") +#define TZ_America_Merida PSTR("CST6") #define TZ_America_Metlakatla PSTR("AKST9AKDT,M3.2.0,M11.1.0") -#define TZ_America_Mexico_City PSTR("CST6CDT,M4.1.0,M10.5.0") +#define TZ_America_Mexico_City PSTR("CST6") #define TZ_America_Miquelon PSTR("<-03>3<-02>,M3.2.0,M11.1.0") #define TZ_America_Moncton PSTR("AST4ADT,M3.2.0,M11.1.0") -#define TZ_America_Monterrey PSTR("CST6CDT,M4.1.0,M10.5.0") +#define TZ_America_Monterrey PSTR("CST6") #define TZ_America_Montevideo PSTR("<-03>3") #define TZ_America_Montreal PSTR("EST5EDT,M3.2.0,M11.1.0") #define TZ_America_Montserrat PSTR("AST4") @@ -172,14 +181,15 @@ #define TZ_America_North_Dakota_Beulah PSTR("CST6CDT,M3.2.0,M11.1.0") #define TZ_America_North_Dakota_Center PSTR("CST6CDT,M3.2.0,M11.1.0") #define TZ_America_North_Dakota_New_Salem PSTR("CST6CDT,M3.2.0,M11.1.0") -#define TZ_America_Nuuk PSTR("<-03>3<-02>,M3.5.0/-2,M10.5.0/-1") -#define TZ_America_Ojinaga PSTR("MST7MDT,M3.2.0,M11.1.0") +#define TZ_America_Nuuk PSTR("<-02>2<-01>,M3.5.0/-1,M10.5.0/0") +#define TZ_America_Ojinaga PSTR("CST6CDT,M3.2.0,M11.1.0") #define TZ_America_Panama PSTR("EST5") #define TZ_America_Pangnirtung PSTR("EST5EDT,M3.2.0,M11.1.0") #define TZ_America_Paramaribo PSTR("<-03>3") #define TZ_America_Phoenix PSTR("MST7") #define TZ_America_PortmaumPrince PSTR("EST5EDT,M3.2.0,M11.1.0") #define TZ_America_Port_of_Spain PSTR("AST4") +#define TZ_America_Porto_Acre PSTR("<-05>5") #define TZ_America_Porto_Velho PSTR("<-04>4") #define TZ_America_Puerto_Rico PSTR("AST4") #define TZ_America_Punta_Arenas PSTR("<-03>3") @@ -189,11 +199,14 @@ #define TZ_America_Regina PSTR("CST6") #define TZ_America_Resolute PSTR("CST6CDT,M3.2.0,M11.1.0") #define TZ_America_Rio_Branco PSTR("<-05>5") +#define TZ_America_Rosario PSTR("<-03>3") +#define TZ_America_Santa_Isabel PSTR("PST8PDT,M3.2.0,M11.1.0") #define TZ_America_Santarem PSTR("<-03>3") #define TZ_America_Santiago PSTR("<-04>4<-03>,M9.1.6/24,M4.1.6/24") #define TZ_America_Santo_Domingo PSTR("AST4") #define TZ_America_Sao_Paulo PSTR("<-03>3") -#define TZ_America_Scoresbysund PSTR("<-01>1<+00>,M3.5.0/0,M10.5.0/1") +#define TZ_America_Scoresbysund PSTR("<-02>2<-01>,M3.5.0/-1,M10.5.0/0") +#define TZ_America_Shiprock PSTR("MST7MDT,M3.2.0,M11.1.0") #define TZ_America_Sitka PSTR("AKST9AKDT,M3.2.0,M11.1.0") #define TZ_America_St_Barthelemy PSTR("AST4") #define TZ_America_St_Johns PSTR("NST3:30NDT,M3.2.0,M11.1.0") @@ -209,11 +222,12 @@ #define TZ_America_Toronto PSTR("EST5EDT,M3.2.0,M11.1.0") #define TZ_America_Tortola PSTR("AST4") #define TZ_America_Vancouver PSTR("PST8PDT,M3.2.0,M11.1.0") +#define TZ_America_Virgin PSTR("AST4") #define TZ_America_Whitehorse PSTR("MST7") #define TZ_America_Winnipeg PSTR("CST6CDT,M3.2.0,M11.1.0") #define TZ_America_Yakutat PSTR("AKST9AKDT,M3.2.0,M11.1.0") #define TZ_America_Yellowknife PSTR("MST7MDT,M3.2.0,M11.1.0") -#define TZ_Antarctica_Casey PSTR("<+11>-11") +#define TZ_Antarctica_Casey PSTR("<+08>-8") #define TZ_Antarctica_Davis PSTR("<+07>-7") #define TZ_Antarctica_DumontDUrville PSTR("<+10>-10") #define TZ_Antarctica_Macquarie PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3") @@ -221,17 +235,19 @@ #define TZ_Antarctica_McMurdo PSTR("NZST-12NZDT,M9.5.0,M4.1.0/3") #define TZ_Antarctica_Palmer PSTR("<-03>3") #define TZ_Antarctica_Rothera PSTR("<-03>3") +#define TZ_Antarctica_South_Pole PSTR("NZST-12NZDT,M9.5.0,M4.1.0/3") #define TZ_Antarctica_Syowa PSTR("<+03>-3") #define TZ_Antarctica_Troll PSTR("<+00>0<+02>-2,M3.5.0/1,M10.5.0/3") -#define TZ_Antarctica_Vostok PSTR("<+06>-6") +#define TZ_Antarctica_Vostok PSTR("<+05>-5") #define TZ_Arctic_Longyearbyen PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Asia_Aden PSTR("<+03>-3") #define TZ_Asia_Almaty PSTR("<+06>-6") -#define TZ_Asia_Amman PSTR("EET-2EEST,M3.5.4/24,M10.5.5/1") +#define TZ_Asia_Amman PSTR("<+03>-3") #define TZ_Asia_Anadyr PSTR("<+12>-12") #define TZ_Asia_Aqtau PSTR("<+05>-5") #define TZ_Asia_Aqtobe PSTR("<+05>-5") #define TZ_Asia_Ashgabat PSTR("<+05>-5") +#define TZ_Asia_Ashkhabad PSTR("<+05>-5") #define TZ_Asia_Atyrau PSTR("<+05>-5") #define TZ_Asia_Baghdad PSTR("<+03>-3") #define TZ_Asia_Bahrain PSTR("<+03>-3") @@ -241,34 +257,43 @@ #define TZ_Asia_Beirut PSTR("EET-2EEST,M3.5.0/0,M10.5.0/0") #define TZ_Asia_Bishkek PSTR("<+06>-6") #define TZ_Asia_Brunei PSTR("<+08>-8") +#define TZ_Asia_Calcutta PSTR("IST-5:30") #define TZ_Asia_Chita PSTR("<+09>-9") #define TZ_Asia_Choibalsan PSTR("<+08>-8") +#define TZ_Asia_Chongqing PSTR("CST-8") +#define TZ_Asia_Chungking PSTR("CST-8") #define TZ_Asia_Colombo PSTR("<+0530>-5:30") -#define TZ_Asia_Damascus PSTR("EET-2EEST,M3.5.5/0,M10.5.5/0") +#define TZ_Asia_Dacca PSTR("<+06>-6") +#define TZ_Asia_Damascus PSTR("<+03>-3") #define TZ_Asia_Dhaka PSTR("<+06>-6") #define TZ_Asia_Dili PSTR("<+09>-9") #define TZ_Asia_Dubai PSTR("<+04>-4") #define TZ_Asia_Dushanbe PSTR("<+05>-5") #define TZ_Asia_Famagusta PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4") -#define TZ_Asia_Gaza PSTR("EET-2EEST,M3.4.4/48,M10.4.4/49") -#define TZ_Asia_Hebron PSTR("EET-2EEST,M3.4.4/48,M10.4.4/49") +#define TZ_Asia_Gaza PSTR("EET-2EEST,M3.4.4/50,M10.4.4/50") +#define TZ_Asia_Harbin PSTR("CST-8") +#define TZ_Asia_Hebron PSTR("EET-2EEST,M3.4.4/50,M10.4.4/50") #define TZ_Asia_Ho_Chi_Minh PSTR("<+07>-7") #define TZ_Asia_Hong_Kong PSTR("HKT-8") #define TZ_Asia_Hovd PSTR("<+07>-7") #define TZ_Asia_Irkutsk PSTR("<+08>-8") +#define TZ_Asia_Istanbul PSTR("<+03>-3") #define TZ_Asia_Jakarta PSTR("WIB-7") #define TZ_Asia_Jayapura PSTR("WIT-9") #define TZ_Asia_Jerusalem PSTR("IST-2IDT,M3.4.4/26,M10.5.0") #define TZ_Asia_Kabul PSTR("<+0430>-4:30") #define TZ_Asia_Kamchatka PSTR("<+12>-12") #define TZ_Asia_Karachi PSTR("PKT-5") +#define TZ_Asia_Kashgar PSTR("<+06>-6") #define TZ_Asia_Kathmandu PSTR("<+0545>-5:45") +#define TZ_Asia_Katmandu PSTR("<+0545>-5:45") #define TZ_Asia_Khandyga PSTR("<+09>-9") #define TZ_Asia_Kolkata PSTR("IST-5:30") #define TZ_Asia_Krasnoyarsk PSTR("<+07>-7") #define TZ_Asia_Kuala_Lumpur PSTR("<+08>-8") #define TZ_Asia_Kuching PSTR("<+08>-8") #define TZ_Asia_Kuwait PSTR("<+03>-3") +#define TZ_Asia_Macao PSTR("CST-8") #define TZ_Asia_Macau PSTR("CST-8") #define TZ_Asia_Magadan PSTR("<+11>-11") #define TZ_Asia_Makassar PSTR("WITA-8") @@ -283,8 +308,11 @@ #define TZ_Asia_Pontianak PSTR("WIB-7") #define TZ_Asia_Pyongyang PSTR("KST-9") #define TZ_Asia_Qatar PSTR("<+03>-3") +#define TZ_Asia_Qostanay PSTR("<+06>-6") #define TZ_Asia_Qyzylorda PSTR("<+05>-5") +#define TZ_Asia_Rangoon PSTR("<+0630>-6:30") #define TZ_Asia_Riyadh PSTR("<+03>-3") +#define TZ_Asia_Saigon PSTR("<+07>-7") #define TZ_Asia_Sakhalin PSTR("<+11>-11") #define TZ_Asia_Samarkand PSTR("<+05>-5") #define TZ_Asia_Seoul PSTR("KST-9") @@ -294,11 +322,15 @@ #define TZ_Asia_Taipei PSTR("CST-8") #define TZ_Asia_Tashkent PSTR("<+05>-5") #define TZ_Asia_Tbilisi PSTR("<+04>-4") -#define TZ_Asia_Tehran PSTR("<+0330>-3:30<+0430>,J79/24,J263/24") +#define TZ_Asia_Tehran PSTR("<+0330>-3:30") +#define TZ_Asia_Tel_Aviv PSTR("IST-2IDT,M3.4.4/26,M10.5.0") +#define TZ_Asia_Thimbu PSTR("<+06>-6") #define TZ_Asia_Thimphu PSTR("<+06>-6") #define TZ_Asia_Tokyo PSTR("JST-9") #define TZ_Asia_Tomsk PSTR("<+07>-7") +#define TZ_Asia_Ujung_Pandang PSTR("WITA-8") #define TZ_Asia_Ulaanbaatar PSTR("<+08>-8") +#define TZ_Asia_Ulan_Bator PSTR("<+08>-8") #define TZ_Asia_Urumqi PSTR("<+06>-6") #define TZ_Asia_UstmNera PSTR("<+10>-10") #define TZ_Asia_Vientiane PSTR("<+07>-7") @@ -311,28 +343,99 @@ #define TZ_Atlantic_Bermuda PSTR("AST4ADT,M3.2.0,M11.1.0") #define TZ_Atlantic_Canary PSTR("WET0WEST,M3.5.0/1,M10.5.0") #define TZ_Atlantic_Cape_Verde PSTR("<-01>1") +#define TZ_Atlantic_Faeroe PSTR("WET0WEST,M3.5.0/1,M10.5.0") #define TZ_Atlantic_Faroe PSTR("WET0WEST,M3.5.0/1,M10.5.0") +#define TZ_Atlantic_Jan_Mayen PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Atlantic_Madeira PSTR("WET0WEST,M3.5.0/1,M10.5.0") #define TZ_Atlantic_Reykjavik PSTR("GMT0") #define TZ_Atlantic_South_Georgia PSTR("<-02>2") -#define TZ_Atlantic_Stanley PSTR("<-03>3") #define TZ_Atlantic_St_Helena PSTR("GMT0") +#define TZ_Atlantic_Stanley PSTR("<-03>3") +#define TZ_Australia_ACT PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3") #define TZ_Australia_Adelaide PSTR("ACST-9:30ACDT,M10.1.0,M4.1.0/3") #define TZ_Australia_Brisbane PSTR("AEST-10") #define TZ_Australia_Broken_Hill PSTR("ACST-9:30ACDT,M10.1.0,M4.1.0/3") +#define TZ_Australia_Canberra PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3") #define TZ_Australia_Currie PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3") #define TZ_Australia_Darwin PSTR("ACST-9:30") #define TZ_Australia_Eucla PSTR("<+0845>-8:45") #define TZ_Australia_Hobart PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3") +#define TZ_Australia_LHI PSTR("<+1030>-10:30<+11>-11,M10.1.0,M4.1.0") #define TZ_Australia_Lindeman PSTR("AEST-10") #define TZ_Australia_Lord_Howe PSTR("<+1030>-10:30<+11>-11,M10.1.0,M4.1.0") #define TZ_Australia_Melbourne PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3") +#define TZ_Australia_NSW PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3") +#define TZ_Australia_North PSTR("ACST-9:30") #define TZ_Australia_Perth PSTR("AWST-8") +#define TZ_Australia_Queensland PSTR("AEST-10") +#define TZ_Australia_South PSTR("ACST-9:30ACDT,M10.1.0,M4.1.0/3") #define TZ_Australia_Sydney PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3") +#define TZ_Australia_Tasmania PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3") +#define TZ_Australia_Victoria PSTR("AEST-10AEDT,M10.1.0,M4.1.0/3") +#define TZ_Australia_West PSTR("AWST-8") +#define TZ_Australia_Yancowinna PSTR("ACST-9:30ACDT,M10.1.0,M4.1.0/3") +#define TZ_Brazil_Acre PSTR("<-05>5") +#define TZ_Brazil_DeNoronha PSTR("<-02>2") +#define TZ_Brazil_East PSTR("<-03>3") +#define TZ_Brazil_West PSTR("<-04>4") +#define TZ_CET PSTR("CET-1CEST,M3.5.0,M10.5.0/3") +#define TZ_CST6CDT PSTR("CST6CDT,M3.2.0,M11.1.0") +#define TZ_Canada_Atlantic PSTR("AST4ADT,M3.2.0,M11.1.0") +#define TZ_Canada_Central PSTR("CST6CDT,M3.2.0,M11.1.0") +#define TZ_Canada_Eastern PSTR("EST5EDT,M3.2.0,M11.1.0") +#define TZ_Canada_Mountain PSTR("MST7MDT,M3.2.0,M11.1.0") +#define TZ_Canada_Newfoundland PSTR("NST3:30NDT,M3.2.0,M11.1.0") +#define TZ_Canada_Pacific PSTR("PST8PDT,M3.2.0,M11.1.0") +#define TZ_Canada_Saskatchewan PSTR("CST6") +#define TZ_Canada_Yukon PSTR("MST7") +#define TZ_Chile_Continental PSTR("<-04>4<-03>,M9.1.6/24,M4.1.6/24") +#define TZ_Chile_EasterIsland PSTR("<-06>6<-05>,M9.1.6/22,M4.1.6/22") +#define TZ_Cuba PSTR("CST5CDT,M3.2.0/0,M11.1.0/1") +#define TZ_EET PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4") +#define TZ_EST PSTR("EST5") +#define TZ_EST5EDT PSTR("EST5EDT,M3.2.0,M11.1.0") +#define TZ_Egypt PSTR("EET-2EEST,M4.5.5/0,M10.5.4/24") +#define TZ_Eire PSTR("IST-1GMT0,M10.5.0,M3.5.0/1") +#define TZ_Etc_GMT PSTR("GMT0") +#define TZ_Etc_GMTp0 PSTR("GMT0") +#define TZ_Etc_GMTp1 PSTR("<-01>1") +#define TZ_Etc_GMTp10 PSTR("<-10>10") +#define TZ_Etc_GMTp11 PSTR("<-11>11") +#define TZ_Etc_GMTp12 PSTR("<-12>12") +#define TZ_Etc_GMTp2 PSTR("<-02>2") +#define TZ_Etc_GMTp3 PSTR("<-03>3") +#define TZ_Etc_GMTp4 PSTR("<-04>4") +#define TZ_Etc_GMTp5 PSTR("<-05>5") +#define TZ_Etc_GMTp6 PSTR("<-06>6") +#define TZ_Etc_GMTp7 PSTR("<-07>7") +#define TZ_Etc_GMTp8 PSTR("<-08>8") +#define TZ_Etc_GMTp9 PSTR("<-09>9") +#define TZ_Etc_GMTm0 PSTR("GMT0") +#define TZ_Etc_GMTm1 PSTR("<+01>-1") +#define TZ_Etc_GMTm10 PSTR("<+10>-10") +#define TZ_Etc_GMTm11 PSTR("<+11>-11") +#define TZ_Etc_GMTm12 PSTR("<+12>-12") +#define TZ_Etc_GMTm13 PSTR("<+13>-13") +#define TZ_Etc_GMTm14 PSTR("<+14>-14") +#define TZ_Etc_GMTm2 PSTR("<+02>-2") +#define TZ_Etc_GMTm3 PSTR("<+03>-3") +#define TZ_Etc_GMTm4 PSTR("<+04>-4") +#define TZ_Etc_GMTm5 PSTR("<+05>-5") +#define TZ_Etc_GMTm6 PSTR("<+06>-6") +#define TZ_Etc_GMTm7 PSTR("<+07>-7") +#define TZ_Etc_GMTm8 PSTR("<+08>-8") +#define TZ_Etc_GMTm9 PSTR("<+09>-9") +#define TZ_Etc_GMT0 PSTR("GMT0") +#define TZ_Etc_Greenwich PSTR("GMT0") +#define TZ_Etc_UCT PSTR("UTC0") +#define TZ_Etc_UTC PSTR("UTC0") +#define TZ_Etc_Universal PSTR("UTC0") +#define TZ_Etc_Zulu PSTR("UTC0") #define TZ_Europe_Amsterdam PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Europe_Andorra PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Europe_Astrakhan PSTR("<+04>-4") #define TZ_Europe_Athens PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4") +#define TZ_Europe_Belfast PSTR("GMT0BST,M3.5.0/1,M10.5.0") #define TZ_Europe_Belgrade PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Europe_Berlin PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Europe_Bratislava PSTR("CET-1CEST,M3.5.0,M10.5.0/3") @@ -351,7 +454,8 @@ #define TZ_Europe_Jersey PSTR("GMT0BST,M3.5.0/1,M10.5.0") #define TZ_Europe_Kaliningrad PSTR("EET-2") #define TZ_Europe_Kiev PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4") -#define TZ_Europe_Kirov PSTR("<+03>-3") +#define TZ_Europe_Kirov PSTR("MSK-3") +#define TZ_Europe_Kyiv PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4") #define TZ_Europe_Lisbon PSTR("WET0WEST,M3.5.0/1,M10.5.0") #define TZ_Europe_Ljubljana PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Europe_London PSTR("GMT0BST,M3.5.0/1,M10.5.0") @@ -362,6 +466,7 @@ #define TZ_Europe_Minsk PSTR("<+03>-3") #define TZ_Europe_Monaco PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Europe_Moscow PSTR("MSK-3") +#define TZ_Europe_Nicosia PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4") #define TZ_Europe_Oslo PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Europe_Paris PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Europe_Podgorica PSTR("CET-1CEST,M3.5.0,M10.5.0/3") @@ -378,17 +483,31 @@ #define TZ_Europe_Stockholm PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Europe_Tallinn PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4") #define TZ_Europe_Tirane PSTR("CET-1CEST,M3.5.0,M10.5.0/3") +#define TZ_Europe_Tiraspol PSTR("EET-2EEST,M3.5.0,M10.5.0/3") #define TZ_Europe_Ulyanovsk PSTR("<+04>-4") #define TZ_Europe_Uzhgorod PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4") +#define TZ_Europe_Uzhhorod PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4") #define TZ_Europe_Vaduz PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Europe_Vatican PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Europe_Vienna PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Europe_Vilnius PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4") -#define TZ_Europe_Volgograd PSTR("<+03>-3") +#define TZ_Europe_Volgograd PSTR("MSK-3") #define TZ_Europe_Warsaw PSTR("CET-1CEST,M3.5.0,M10.5.0/3") #define TZ_Europe_Zagreb PSTR("CET-1CEST,M3.5.0,M10.5.0/3") +#define TZ_Europe_Zaporizhzhia PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4") #define TZ_Europe_Zaporozhye PSTR("EET-2EEST,M3.5.0/3,M10.5.0/4") #define TZ_Europe_Zurich PSTR("CET-1CEST,M3.5.0,M10.5.0/3") +#define TZ_Factory PSTR("<-00>0") +#define TZ_GB PSTR("GMT0BST,M3.5.0/1,M10.5.0") +#define TZ_GBmEire PSTR("GMT0BST,M3.5.0/1,M10.5.0") +#define TZ_GMT PSTR("GMT0") +#define TZ_GMTp0 PSTR("GMT0") +#define TZ_GMTm0 PSTR("GMT0") +#define TZ_GMT0 PSTR("GMT0") +#define TZ_Greenwich PSTR("GMT0") +#define TZ_HST PSTR("HST10") +#define TZ_Hongkong PSTR("HKT-8") +#define TZ_Iceland PSTR("GMT0") #define TZ_Indian_Antananarivo PSTR("EAT-3") #define TZ_Indian_Chagos PSTR("<+06>-6") #define TZ_Indian_Christmas PSTR("<+07>-7") @@ -400,7 +519,24 @@ #define TZ_Indian_Mauritius PSTR("<+04>-4") #define TZ_Indian_Mayotte PSTR("EAT-3") #define TZ_Indian_Reunion PSTR("<+04>-4") -#define TZ_Pacific_Apia PSTR("<+13>-13<+14>,M9.5.0/3,M4.1.0/4") +#define TZ_Iran PSTR("<+0330>-3:30") +#define TZ_Israel PSTR("IST-2IDT,M3.4.4/26,M10.5.0") +#define TZ_Jamaica PSTR("EST5") +#define TZ_Japan PSTR("JST-9") +#define TZ_Kwajalein PSTR("<+12>-12") +#define TZ_Libya PSTR("EET-2") +#define TZ_MET PSTR("MET-1MEST,M3.5.0,M10.5.0/3") +#define TZ_MST PSTR("MST7") +#define TZ_MST7MDT PSTR("MST7MDT,M3.2.0,M11.1.0") +#define TZ_Mexico_BajaNorte PSTR("PST8PDT,M3.2.0,M11.1.0") +#define TZ_Mexico_BajaSur PSTR("MST7") +#define TZ_Mexico_General PSTR("CST6") +#define TZ_NZ PSTR("NZST-12NZDT,M9.5.0,M4.1.0/3") +#define TZ_NZmCHAT PSTR("<+1245>-12:45<+1345>,M9.5.0/2:45,M4.1.0/3:45") +#define TZ_Navajo PSTR("MST7MDT,M3.2.0,M11.1.0") +#define TZ_PRC PSTR("CST-8") +#define TZ_PST8PDT PSTR("PST8PDT,M3.2.0,M11.1.0") +#define TZ_Pacific_Apia PSTR("<+13>-13") #define TZ_Pacific_Auckland PSTR("NZST-12NZDT,M9.5.0,M4.1.0/3") #define TZ_Pacific_Bougainville PSTR("<+11>-11") #define TZ_Pacific_Chatham PSTR("<+1245>-12:45<+1345>,M9.5.0/2:45,M4.1.0/3:45") @@ -409,13 +545,15 @@ #define TZ_Pacific_Efate PSTR("<+11>-11") #define TZ_Pacific_Enderbury PSTR("<+13>-13") #define TZ_Pacific_Fakaofo PSTR("<+13>-13") -#define TZ_Pacific_Fiji PSTR("<+12>-12<+13>,M11.2.0,M1.2.3/99") +#define TZ_Pacific_Fiji PSTR("<+12>-12") #define TZ_Pacific_Funafuti PSTR("<+12>-12") #define TZ_Pacific_Galapagos PSTR("<-06>6") #define TZ_Pacific_Gambier PSTR("<-09>9") #define TZ_Pacific_Guadalcanal PSTR("<+11>-11") #define TZ_Pacific_Guam PSTR("ChST-10") #define TZ_Pacific_Honolulu PSTR("HST10") +#define TZ_Pacific_Johnston PSTR("HST10") +#define TZ_Pacific_Kanton PSTR("<+13>-13") #define TZ_Pacific_Kiritimati PSTR("<+14>-14") #define TZ_Pacific_Kosrae PSTR("<+11>-11") #define TZ_Pacific_Kwajalein PSTR("<+12>-12") @@ -430,48 +568,39 @@ #define TZ_Pacific_Palau PSTR("<+09>-9") #define TZ_Pacific_Pitcairn PSTR("<-08>8") #define TZ_Pacific_Pohnpei PSTR("<+11>-11") +#define TZ_Pacific_Ponape PSTR("<+11>-11") #define TZ_Pacific_Port_Moresby PSTR("<+10>-10") #define TZ_Pacific_Rarotonga PSTR("<-10>10") #define TZ_Pacific_Saipan PSTR("ChST-10") +#define TZ_Pacific_Samoa PSTR("SST11") #define TZ_Pacific_Tahiti PSTR("<-10>10") #define TZ_Pacific_Tarawa PSTR("<+12>-12") #define TZ_Pacific_Tongatapu PSTR("<+13>-13") +#define TZ_Pacific_Truk PSTR("<+10>-10") #define TZ_Pacific_Wake PSTR("<+12>-12") #define TZ_Pacific_Wallis PSTR("<+12>-12") -#define TZ_Etc_GMT PSTR("GMT0") -#define TZ_Etc_GMTm0 PSTR("GMT0") -#define TZ_Etc_GMTm1 PSTR("<+01>-1") -#define TZ_Etc_GMTm2 PSTR("<+02>-2") -#define TZ_Etc_GMTm3 PSTR("<+03>-3") -#define TZ_Etc_GMTm4 PSTR("<+04>-4") -#define TZ_Etc_GMTm5 PSTR("<+05>-5") -#define TZ_Etc_GMTm6 PSTR("<+06>-6") -#define TZ_Etc_GMTm7 PSTR("<+07>-7") -#define TZ_Etc_GMTm8 PSTR("<+08>-8") -#define TZ_Etc_GMTm9 PSTR("<+09>-9") -#define TZ_Etc_GMTm10 PSTR("<+10>-10") -#define TZ_Etc_GMTm11 PSTR("<+11>-11") -#define TZ_Etc_GMTm12 PSTR("<+12>-12") -#define TZ_Etc_GMTm13 PSTR("<+13>-13") -#define TZ_Etc_GMTm14 PSTR("<+14>-14") -#define TZ_Etc_GMT0 PSTR("GMT0") -#define TZ_Etc_GMTp0 PSTR("GMT0") -#define TZ_Etc_GMTp1 PSTR("<-01>1") -#define TZ_Etc_GMTp2 PSTR("<-02>2") -#define TZ_Etc_GMTp3 PSTR("<-03>3") -#define TZ_Etc_GMTp4 PSTR("<-04>4") -#define TZ_Etc_GMTp5 PSTR("<-05>5") -#define TZ_Etc_GMTp6 PSTR("<-06>6") -#define TZ_Etc_GMTp7 PSTR("<-07>7") -#define TZ_Etc_GMTp8 PSTR("<-08>8") -#define TZ_Etc_GMTp9 PSTR("<-09>9") -#define TZ_Etc_GMTp10 PSTR("<-10>10") -#define TZ_Etc_GMTp11 PSTR("<-11>11") -#define TZ_Etc_GMTp12 PSTR("<-12>12") -#define TZ_Etc_UCT PSTR("UTC0") -#define TZ_Etc_UTC PSTR("UTC0") -#define TZ_Etc_Greenwich PSTR("GMT0") -#define TZ_Etc_Universal PSTR("UTC0") -#define TZ_Etc_Zulu PSTR("UTC0") - -#endif // TZDB_H +#define TZ_Pacific_Yap PSTR("<+10>-10") +#define TZ_Poland PSTR("CET-1CEST,M3.5.0,M10.5.0/3") +#define TZ_Portugal PSTR("WET0WEST,M3.5.0/1,M10.5.0") +#define TZ_ROC PSTR("CST-8") +#define TZ_ROK PSTR("KST-9") +#define TZ_Singapore PSTR("<+08>-8") +#define TZ_Turkey PSTR("<+03>-3") +#define TZ_UCT PSTR("UTC0") +#define TZ_US_Alaska PSTR("AKST9AKDT,M3.2.0,M11.1.0") +#define TZ_US_Aleutian PSTR("HST10HDT,M3.2.0,M11.1.0") +#define TZ_US_Arizona PSTR("MST7") +#define TZ_US_Central PSTR("CST6CDT,M3.2.0,M11.1.0") +#define TZ_US_EastmIndiana PSTR("EST5EDT,M3.2.0,M11.1.0") +#define TZ_US_Eastern PSTR("EST5EDT,M3.2.0,M11.1.0") +#define TZ_US_Hawaii PSTR("HST10") +#define TZ_US_IndianamStarke PSTR("CST6CDT,M3.2.0,M11.1.0") +#define TZ_US_Michigan PSTR("EST5EDT,M3.2.0,M11.1.0") +#define TZ_US_Mountain PSTR("MST7MDT,M3.2.0,M11.1.0") +#define TZ_US_Pacific PSTR("PST8PDT,M3.2.0,M11.1.0") +#define TZ_US_Samoa PSTR("SST11") +#define TZ_UTC PSTR("UTC0") +#define TZ_Universal PSTR("UTC0") +#define TZ_WmSU PSTR("MSK-3") +#define TZ_WET PSTR("WET0WEST,M3.5.0/1,M10.5.0") +#define TZ_Zulu PSTR("UTC0") diff --git a/cores/esp8266/Udp.h b/cores/esp8266/Udp.h index 6c8ee4b385..37f2fb922b 100644 --- a/cores/esp8266/Udp.h +++ b/cores/esp8266/Udp.h @@ -43,6 +43,7 @@ class UDP: public Stream { public: virtual ~UDP() {}; virtual uint8_t begin(uint16_t) =0; // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use + virtual uint8_t beginMulticast(IPAddress, uint16_t) { return 0; } // initialize, start listening on specified multicast IP address and port. Returns 1 if successful, 0 on failure virtual void stop() =0; // Finish with the UDP socket // Sending UDP packets diff --git a/cores/esp8266/Updater.cpp b/cores/esp8266/Updater.cpp index 19a9dad528..ef79a5cbf3 100644 --- a/cores/esp8266/Updater.cpp +++ b/cores/esp8266/Updater.cpp @@ -1,9 +1,12 @@ +#include #include "Updater.h" #include "eboot_command.h" #include #include #include "StackThunk.h" +#include + //#define DEBUG_UPDATER Serial #include @@ -24,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() { @@ -42,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 } @@ -67,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; } @@ -83,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.")); @@ -118,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; @@ -130,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; @@ -151,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 { @@ -159,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; } @@ -171,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 @@ -183,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; } @@ -224,58 +240,88 @@ bool UpdaterClass::end(bool evenIfRemaining){ _size = progress(); } - uint32_t sigLen = 0; if (_verify) { - ESP.flashRead(_startAddress + _size - sizeof(uint32_t), &sigLen, sizeof(uint32_t)); + // If expectedSigLen is non-zero, we expect the last four bytes of the buffer to + // contain a matching length field, preceded by the bytes of the signature itself. + // But if expectedSigLen is zero, we expect neither a signature nor a length field; + static constexpr uint32_t SigSize = sizeof(uint32_t); + const uint32_t expectedSigLen = _verify->length(); + const uint32_t sigLenAddr = _startAddress + _size - SigSize; + uint32_t sigLen = 0; + +#ifdef DEBUG_UPDATER + DEBUG_UPDATER.printf_P(PSTR("[Updater] expected sigLen: %u\n"), expectedSigLen); +#endif + if (expectedSigLen > 0) { + ESP.flashRead(sigLenAddr, &sigLen, SigSize); #ifdef DEBUG_UPDATER - DEBUG_UPDATER.printf_P(PSTR("[Updater] sigLen: %d\n"), sigLen); + DEBUG_UPDATER.printf_P(PSTR("[Updater] sigLen from flash: %u\n"), sigLen); #endif - if (sigLen != _verify->length()) { + } + + if (sigLen != expectedSigLen) { _setError(UPDATE_ERROR_SIGN); _reset(); return false; } - int binSize = _size - sigLen - sizeof(uint32_t) /* The siglen word */; - _hash->begin(); + auto binSize = _size; + if (expectedSigLen > 0) { + if (binSize < (sigLen + SigSize)) { + _setError(UPDATE_ERROR_SIGN); + _reset(); + return false; + } + binSize -= (sigLen + SigSize); #ifdef DEBUG_UPDATER - DEBUG_UPDATER.printf_P(PSTR("[Updater] Adjusted binsize: %d\n"), binSize); + DEBUG_UPDATER.printf_P(PSTR("[Updater] Adjusted size (without the signature and sigLen): %zu\n"), binSize); #endif - // Calculate the MD5 and hash using proper size - uint8_t buff[128] __attribute__((aligned(4))); - for(int i = 0; i < binSize; i += sizeof(buff)) { - ESP.flashRead(_startAddress + i, (uint32_t *)buff, sizeof(buff)); - size_t read = std::min((int)sizeof(buff), binSize - i); - _hash->add(buff, read); + } + + // Calculate hash of the payload, 128 bytes at a time + alignas(alignof(uint32_t)) uint8_t buff[128]; + + _hash->begin(); + for (uint32_t offset = 0; offset < binSize; offset += sizeof(buff)) { + auto len = std::min(sizeof(buff), binSize - offset); + ESP.flashRead(_startAddress + offset, buff, len); + _hash->add(buff, len); } _hash->end(); + #ifdef DEBUG_UPDATER - unsigned char *ret = (unsigned char *)_hash->hash(); - DEBUG_UPDATER.printf_P(PSTR("[Updater] Computed Hash:")); - for (int i=0; i<_hash->len(); i++) DEBUG_UPDATER.printf(" %02x", ret[i]); - DEBUG_UPDATER.printf("\n"); + auto debugByteArray = [](const char *name, const unsigned char *hash, int len) { + DEBUG_UPDATER.printf_P("[Updater] %s:", name); + for (int i = 0; i < len; ++i) { + DEBUG_UPDATER.printf(" %02x", hash[i]); + } + DEBUG_UPDATER.printf("\n"); + }; + debugByteArray(PSTR("Computed Hash"), + reinterpret_cast(_hash->hash()), + _hash->len()); #endif - uint8_t *sig = (uint8_t*)malloc(sigLen); - if (!sig) { - _setError(UPDATE_ERROR_SIGN); - _reset(); - return false; - } - ESP.flashRead(_startAddress + binSize, sig, sigLen); + + std::unique_ptr sig; + if (expectedSigLen > 0) { + const uint32_t sigAddr = _startAddress + binSize; + sig.reset(new (std::nothrow) uint8_t[sigLen]); + if (!sig) { + _setError(UPDATE_ERROR_OOM); + _reset(); + return false; + } + ESP.flashRead(sigAddr, sig.get(), sigLen); #ifdef DEBUG_UPDATER - DEBUG_UPDATER.printf_P(PSTR("[Updater] Received Signature:")); - for (size_t i=0; iverify(_hash, (void *)sig, sigLen)) { - free(sig); + } + if (!_verify->verify(_hash, sig.get(), sigLen)) { _setError(UPDATE_ERROR_SIGN); _reset(); return false; } - free(sig); + _size = binSize; // Adjust size to remove signature, not part of bin payload #ifdef DEBUG_UPDATER @@ -288,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 } @@ -314,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 @@ -360,7 +406,7 @@ bool UpdaterClass::_writeBuffer(){ modifyFlashMode = true; } } - + if (eraseResult) { if(!_async) yield(); writeResult = ESP.flashWrite(_currentAddress, _buffer, _bufferLen); @@ -444,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; } @@ -456,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) { @@ -538,45 +588,85 @@ size_t UpdaterClass::writeStream(Stream &data, uint16_t streamTimeout) { void UpdaterClass::_setError(int error){ _error = error; + if (_error_callback) { + _error_callback(error); + } #ifdef DEBUG_UPDATER printError(DEBUG_UPDATER); #endif _reset(); // Any error condition invalidates the entire update, so clear partial status } -void UpdaterClass::printError(Print &out){ - out.printf_P(PSTR("ERROR[%u]: "), _error); - if(_error == UPDATE_ERROR_OK){ - out.println(F("No Error")); - } else if(_error == UPDATE_ERROR_WRITE){ - out.println(F("Flash Write Failed")); - } else if(_error == UPDATE_ERROR_ERASE){ - out.println(F("Flash Erase Failed")); - } else if(_error == UPDATE_ERROR_READ){ - out.println(F("Flash Read Failed")); - } else if(_error == UPDATE_ERROR_SPACE){ - out.println(F("Not Enough Space")); - } else if(_error == UPDATE_ERROR_SIZE){ - out.println(F("Bad Size Given")); - } else if(_error == UPDATE_ERROR_STREAM){ - out.println(F("Stream Read Timeout")); - } else if(_error == UPDATE_ERROR_NO_DATA){ - out.println(F("No data supplied")); - } else if(_error == UPDATE_ERROR_MD5){ - out.printf_P(PSTR("MD5 Failed: expected:%s, calculated:%s\n"), _target_md5.c_str(), _md5.toString().c_str()); - } else if(_error == UPDATE_ERROR_SIGN){ - out.println(F("Signature verification failed")); - } else if(_error == UPDATE_ERROR_FLASH_CONFIG){ - out.printf_P(PSTR("Flash config wrong real: %d IDE: %d\n"), ESP.getFlashChipRealSize(), ESP.getFlashChipSize()); - } else if(_error == UPDATE_ERROR_NEW_FLASH_CONFIG){ - out.printf_P(PSTR("new Flash config wrong real: %d\n"), ESP.getFlashChipRealSize()); - } else if(_error == UPDATE_ERROR_MAGIC_BYTE){ - out.println(F("Magic byte is wrong, not 0xE9")); - } else if (_error == UPDATE_ERROR_BOOTSTRAP){ - out.println(F("Invalid bootstrapping state, reset ESP8266 before updating")); - } else { - out.println(F("UNKNOWN")); +String UpdaterClass::getErrorString() const { + String out; + + switch (_error) { + case UPDATE_ERROR_OK: + out = F("No Error"); + break; + case UPDATE_ERROR_WRITE: + out = F("Flash Write Failed"); + break; + case UPDATE_ERROR_ERASE: + out = F("Flash Erase Failed"); + break; + case UPDATE_ERROR_READ: + out = F("Flash Read Failed"); + break; + case UPDATE_ERROR_SPACE: + out = F("Not Enough Space"); + break; + case UPDATE_ERROR_SIZE: + out = F("Bad Size Given"); + break; + case UPDATE_ERROR_STREAM: + out = F("Stream Read Timeout"); + break; + case UPDATE_ERROR_MD5: + out += F("MD5 verification failed: "); + out += F("expected: ") + _target_md5; + out += F(", calculated: ") + _md5.toString(); + break; + case UPDATE_ERROR_FLASH_CONFIG: + out += F("Flash config wrong: "); + out += F("real: ") + String(ESP.getFlashChipRealSize(), 10); + out += F(", SDK: ") + String(ESP.getFlashChipSize(), 10); + break; + case UPDATE_ERROR_NEW_FLASH_CONFIG: + out += F("new Flash config wrong, real size: "); + out += String(ESP.getFlashChipRealSize(), 10); + break; + case UPDATE_ERROR_MAGIC_BYTE: + out = F("Magic byte is not 0xE9"); + break; + case UPDATE_ERROR_BOOTSTRAP: + out = F("Invalid bootstrapping state, reset ESP8266 before updating"); + break; + case UPDATE_ERROR_SIGN: + out = F("Signature verification failed"); + break; + case UPDATE_ERROR_NO_DATA: + out = F("No data supplied"); + break; + case UPDATE_ERROR_OOM: + out = F("Out of memory"); + break; + case UPDATE_ERROR_RUNNING_ALREADY: + out = F("Update already running"); + break; + case UPDATE_ERROR_UNKNOWN_COMMAND: + out = F("Unknown update command"); + break; + default: + out = F("UNKNOWN"); + break; } + + return out; +} + +void UpdaterClass::printError(Print &out){ + out.printf_P(PSTR("ERROR[%hhu]: %s\n"), _error, getErrorString().c_str()); } UpdaterClass Update; diff --git a/cores/esp8266/Updater.h b/cores/esp8266/Updater.h index 911ddf7784..7ee1d28311 100644 --- a/cores/esp8266/Updater.h +++ b/cores/esp8266/Updater.h @@ -20,6 +20,9 @@ #define UPDATE_ERROR_BOOTSTRAP (11) #define UPDATE_ERROR_SIGN (12) #define UPDATE_ERROR_NO_DATA (13) +#define UPDATE_ERROR_OOM (14) +#define UPDATE_ERROR_RUNNING_ALREADY (15) +#define UPDATE_ERROR_UNKNOWN_COMMAND (16) #define U_FLASH 0 #define U_FS 100 @@ -51,8 +54,10 @@ class UpdaterVerifyClass { class UpdaterClass { public: - typedef std::function THandlerFunction_Progress; - + using THandlerFunction_Progress = std::function; + using THandlerFunction_Error = std::function; + using THandlerFunction = std::function; + UpdaterClass(); ~UpdaterClass(); @@ -66,7 +71,7 @@ class UpdaterClass { bool begin(size_t size, int command = U_FLASH, int ledPin = -1, uint8_t ledOn = LOW); /* - Run Updater from asynchronous callbacs + Run Updater from asynchronous callbacks */ void runAsync(bool async){ _async = async; } @@ -97,6 +102,11 @@ class UpdaterClass { */ bool end(bool evenIfRemaining = false); + /* + Gets the last error description as string + */ + String getErrorString() const; + /* Prints the last error to an output stream */ @@ -120,7 +130,34 @@ class UpdaterClass { /* This callback will be called when Updater is receiving data */ - UpdaterClass& onProgress(THandlerFunction_Progress fn); + UpdaterClass& onProgress(THandlerFunction_Progress fn) { + _progress_callback = std::move(fn); + return *this; + } + + /* + This callback will be called when Updater ends + */ + UpdaterClass& onError(THandlerFunction_Error fn) { + _error_callback = std::move(fn); + return *this; + } + + /* + This callback will be called when Updater begins + */ + UpdaterClass& onStart(THandlerFunction fn) { + _start_callback = std::move(fn); + return *this; + } + + /* + This callback will be called when Updater ends + */ + UpdaterClass& onEnd(THandlerFunction fn) { + _end_callback = std::move(fn); + return *this; + } //Helpers uint8_t getError(){ return _error; } @@ -175,13 +212,13 @@ class UpdaterClass { } private: - void _reset(); + void _reset(bool callback = true); bool _writeBuffer(); bool _verifyHeader(uint8_t data); bool _verifyEnd(); - void _setError(int error); + void _setError(int error); bool _async = false; uint8_t _error = 0; @@ -202,8 +239,12 @@ class UpdaterClass { // Optional signed binary verification UpdaterHashClass *_hash = nullptr; UpdaterVerifyClass *_verify = nullptr; - // Optional progress callback function + + // Optional lifetime callback functions THandlerFunction_Progress _progress_callback = nullptr; + THandlerFunction_Error _error_callback = nullptr; + THandlerFunction _start_callback = nullptr; + THandlerFunction _end_callback = nullptr; }; extern UpdaterClass Update; diff --git a/cores/esp8266/WString.cpp b/cores/esp8266/WString.cpp index 6e6dc94beb..1e608c3c92 100644 --- a/cores/esp8266/WString.cpp +++ b/cores/esp8266/WString.cpp @@ -25,6 +25,8 @@ #include "WString.h" #include "stdlib_noniso.h" +#include + #define OOM_STRING_BORDER_DISPLAY 10 #define OOM_STRING_THRESHOLD_REALLOC_WARN 128 @@ -32,111 +34,148 @@ #define STR(x) __STRHELPER(x) // stringifier /*********************************************/ -/* Constructors */ +/* Conversion helpers */ /*********************************************/ -String::String(const char *cstr) { - init(); - if (cstr) - copy(cstr, strlen_P(cstr)); -} +static String toString(unsigned char value, unsigned char base) { + String out; -String::String(const String &value) { - init(); - *this = value; -} + char buf[1 + std::numeric_limits::digits]; + out = utoa(value, buf, base); -String::String(const __FlashStringHelper *pstr) { - init(); - *this = pstr; // see operator = + return out; } -String::String(String &&rval) noexcept { - init(); - move(rval); +static String toString(int value, unsigned char base) { + String out; + + char buf[2 + std::numeric_limits::digits]; + out = itoa(value, buf, base); + + return out; } -String::String(unsigned char value, unsigned char base) { - init(); - char buf[1 + 8 * sizeof(unsigned char)]; - utoa(value, buf, base); - *this = buf; +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; } -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; +static String toString(long value, unsigned char base) { + String out; + + char buf[2 + std::numeric_limits::digits]; + out = ltoa(value, buf, base); + + return out; } -String::String(unsigned int value, unsigned char base) { - init(); - char buf[1 + 8 * sizeof(unsigned int)]; - utoa(value, buf, base); - *this = buf; +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; } -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; +// 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; } -String::String(unsigned long value, unsigned char base) { - init(); - char buf[1 + 8 * sizeof(unsigned long)]; - ultoa(value, buf, base); - *this = buf; +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; } -String::String(long long value) { - init(); - char buf[2 + 8 * sizeof(long long)]; - sprintf(buf, "%lld", value); - *this = buf; +static String toString(double value, unsigned char decimalPlaces) { + String out; + + char buf[33]; + out = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf); + + return out; } -String::String(unsigned long long value) { - init(); - char buf[1 + 8 * sizeof(unsigned long long)]; - sprintf(buf, "%llu", value); - *this = buf; +static String toString(float value, unsigned char decimalPlaces) { + return toString(static_cast(value), decimalPlaces); } -String::String(long long value, unsigned char base) { +/*********************************************/ +/* Constructors */ +/*********************************************/ + +String::String(const char *cstr) { init(); - char buf[2 + 8 * sizeof(long long)]; - *this = lltoa(value, buf, sizeof(buf), base); + if (cstr) + copy(cstr, strlen_P(cstr)); } -String::String(unsigned long long value, unsigned char base) { +String::String(const String &value) { init(); - char buf[1 + 8 * sizeof(unsigned long long)]; - *this = ulltoa(value, buf, sizeof(buf), base); + *this = value; } -String::String(float value, unsigned char decimalPlaces) { +String::String(const __FlashStringHelper *pstr) { init(); - char buf[33]; - *this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf); + *this = pstr; // see operator = } -String::String(double value, unsigned char decimalPlaces) { +String::String(String &&rval) noexcept { init(); - char buf[33]; - *this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf); + move(rval); } +String::String(unsigned char value, unsigned char base) : + String(toString(value, base)) +{} + +String::String(int value, unsigned char base) : + String(toString(value, base)) +{} + +String::String(unsigned int value, unsigned char base) : + String(toString(value, base)) +{} + +String::String(long value, unsigned char base) : + String(toString(value, base)) +{} + +String::String(unsigned long value, unsigned char base) : + String(toString(value, base)) +{} + +String::String(long long value, unsigned char base) : + String(toString(value, base)) +{} + +String::String(unsigned long long value, unsigned char base) : + String(toString(value, base)) +{} + +String::String(float value, unsigned char decimalPlaces) : + String(toString(value, decimalPlaces)) +{} + +String::String(double value, unsigned char decimalPlaces) : + String(toString(value, decimalPlaces)) +{} + /*********************************************/ /* Memory Management */ /*********************************************/ @@ -158,6 +197,15 @@ bool String::reserve(unsigned int size) { return false; } +#ifdef DEBUG_ESP_PORT +static void identifyString (const String& badOne) +{ + DEBUG_ESP_PORT.printf("[String] '%." STR(OOM_STRING_BORDER_DISPLAY) "s ... %." STR(OOM_STRING_BORDER_DISPLAY) "s': ", + badOne.c_str(), + badOne.length() > OOM_STRING_BORDER_DISPLAY? badOne.c_str() + std::max((int)badOne.length() - OOM_STRING_BORDER_DISPLAY, OOM_STRING_BORDER_DISPLAY): ""); +} +#endif + bool String::changeBuffer(unsigned int maxStrLen) { // Can we use SSO here to avoid allocation? if (maxStrLen < sizeof(sso.buff) - 1) { @@ -179,16 +227,19 @@ bool String::changeBuffer(unsigned int maxStrLen) { } // Fallthrough to normal allocator size_t newSize = (maxStrLen + 16) & (~0xf); -#ifdef DEBUG_ESP_OOM +#ifdef DEBUG_ESP_PORT if (!isSSO() && capacity() >= OOM_STRING_THRESHOLD_REALLOC_WARN && maxStrLen > capacity()) { // warn when badly re-allocating - DEBUGV("[String] Reallocating large String(%d -> %d bytes) '%." STR(OOM_STRING_BORDER_DISPLAY) "s ... %." STR(OOM_STRING_BORDER_DISPLAY) "s'\n", - len(), maxStrLen, c_str(), - len() > OOM_STRING_BORDER_DISPLAY? c_str() + std::max((int)len() - OOM_STRING_BORDER_DISPLAY, OOM_STRING_BORDER_DISPLAY): ""); + identifyString(*this); + DEBUG_ESP_PORT.printf("Reallocating large String(%d -> %d bytes)\n", len(), maxStrLen); } #endif // Make sure we can fit newsize in the buffer if (newSize > CAPACITY_MAX) { +#ifdef DEBUG_ESP_PORT + identifyString(*this); + DEBUG_ESP_PORT.printf("Maximum capacity reached (" STR(CAPACITY_MAX) ")\n"); +#endif return false; } uint16_t oldLen = len(); @@ -208,6 +259,10 @@ bool String::changeBuffer(unsigned int maxStrLen) { setBuffer(newbuffer); return true; } +#ifdef DEBUG_ESP_PORT + identifyString(*this); + DEBUG_ESP_PORT.printf("OOM: %d -> %zu bytes\n", isSSO() ? 0: capacity(), newSize); +#endif return false; } @@ -221,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; } @@ -231,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; } @@ -273,6 +330,12 @@ String &String::operator =(const __FlashStringHelper *pstr) { return *this; } +String &String::operator =(char c) { + char buffer[2] { c, '\0' }; + *this = buffer; + return *this; +} + /*********************************************/ /* concat */ /*********************************************/ @@ -322,52 +385,39 @@ bool String::concat(char c) { } bool String::concat(unsigned char num) { - char buf[1 + 3 * sizeof(unsigned char)]; - return concat(buf, sprintf(buf, "%d", num)); + return concat(String(num)); } bool String::concat(int num) { - char buf[2 + 3 * sizeof(int)]; - return concat(buf, sprintf(buf, "%d", num)); + return concat(String(num)); } bool String::concat(unsigned int num) { - char buf[1 + 3 * sizeof(unsigned int)]; - utoa(num, buf, 10); - return concat(buf, strlen(buf)); + return concat(String(num)); } bool String::concat(long num) { - char buf[2 + 3 * sizeof(long)]; - return concat(buf, sprintf(buf, "%ld", num)); + return concat(String(num)); } bool String::concat(unsigned long num) { - char buf[1 + 3 * sizeof(unsigned long)]; - ultoa(num, buf, 10); - return concat(buf, strlen(buf)); + return concat(String(num)); } bool String::concat(long long num) { - char buf[2 + 3 * sizeof(long long)]; - return concat(buf, sprintf(buf, "%lld", num)); + return concat(String(num)); } bool String::concat(unsigned long long num) { - char buf[1 + 3 * sizeof(unsigned long long)]; - return concat(buf, sprintf(buf, "%llu", num)); + return concat(String(num)); } bool String::concat(float num) { - char buf[20]; - char *string = dtostrf(num, 4, 2, buf); - return concat(string, strlen(string)); + return concat(String(num)); } bool String::concat(double num) { - char buf[20]; - char *string = dtostrf(num, 4, 2, buf); - return concat(string, strlen(string)); + return concat(String(num)); } bool String::concat(const __FlashStringHelper *str) { @@ -379,8 +429,9 @@ bool String::concat(const __FlashStringHelper *str) { unsigned int newlen = len() + length; if (!reserve(newlen)) return false; - memcpy_P(wbuffer() + len(), (PGM_P)str, length + 1); + memcpy_P(wbuffer() + len(), (PGM_P)str, length); setLen(newlen); + wbuffer()[newlen] = 0; return true; } @@ -500,6 +551,10 @@ bool String::equals(const char *cstr) const { return strcmp(buffer(), cstr) == 0; } +bool String::equals(const __FlashStringHelper *s) const { + return equals(String(s)); +} + bool String::operator<(const String &rhs) const { return compareTo(rhs) < 0; } @@ -532,6 +587,10 @@ bool String::equalsIgnoreCase(const String &s2) const { return true; } +bool String::equalsIgnoreCase(const __FlashStringHelper *s) const { + return equalsIgnoreCase(String(s)); +} + unsigned char String::equalsConstantTime(const String &s2) const { // To avoid possible time-based attacks present function // compares given strings in a constant time. @@ -565,18 +624,37 @@ bool String::startsWith(const String &s2) const { return startsWith(s2, 0); } +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 false; return strncmp(&buffer()[offset], s2.buffer(), s2.len()) == 0; } +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 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 */ /*********************************************/ @@ -648,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 { @@ -678,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; @@ -689,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; } @@ -742,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(); @@ -756,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 37bb3becb6..1a2aebb298 100644 --- a/cores/esp8266/WString.h +++ b/cores/esp8266/WString.h @@ -33,7 +33,7 @@ #include #include -// an abstract class used as a means to proide a unique pointer type +// an abstract class used as a means to provide a unique pointer type // but really has no body class __FlashStringHelper; #define FPSTR(pstr_pointer) (reinterpret_cast(pstr_pointer)) @@ -58,23 +58,59 @@ class String { String(const String &str); String(const __FlashStringHelper *str); String(String &&rval) noexcept; + explicit String(char c) { sso.buff[0] = c; sso.buff[1] = 0; 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(long long /* base 10 */); - explicit String(long long, unsigned char base); - explicit String(unsigned long long /* base 10 */); - explicit String(unsigned long long, unsigned char base); - 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(); } @@ -94,27 +130,69 @@ 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 =(char c) { - char buffer[2] { c, '\0' }; - *this = buffer; + String &operator =(char c); + + String &operator =(unsigned char value) { + *this = String(value); + return *this; + } + + String &operator =(int value) { + *this = String(value); + return *this; + } + + String &operator =(unsigned int value) { + *this = String(value); return *this; } - // concatenate (works w/ built-in types) + String &operator =(long value) { + *this = String(value); + return *this; + } + + String &operator =(unsigned long value) { + *this = String(value); + return *this; + } + + String &operator =(long long value) { + *this = String(value); + return *this; + } + + String &operator =(unsigned long long value) { + *this = String(value); + return *this; + } + + String &operator =(float value) { + *this = String(value); + return *this; + } + + String &operator =(double value) { + *this = String(value); + return *this; + } + + // 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); @@ -124,17 +202,17 @@ class String { bool concat(unsigned long long num); bool concat(float num); bool concat(double num); - bool concat(const __FlashStringHelper *str); - bool concat(const char *cstr, unsigned int length); // if there's not enough memory for the concatenated value, the string - // will be left unchanged (but this isn't signalled in any way) + // will be left unchanged (but this isn't signaled in any way) template String &operator +=(const T &rhs) { concat(rhs); return *this; } + // 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; } @@ -142,6 +220,7 @@ class String { int compareTo(const String &s) 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); } @@ -159,22 +238,16 @@ class String { 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; bool startsWith(const String &prefix) const; - bool startsWith(const char *prefix) const { - return this->startsWith(String(prefix)); - } - bool startsWith(const __FlashStringHelper *prefix) const { - return this->startsWith(String(prefix)); - } + 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 { - return this->endsWith(String(suffix)); - } - bool endsWith(const __FlashStringHelper *suffix) const { - return this->endsWith(String(suffix)); - } + bool endsWith(const char *suffix) const; + bool endsWith(const __FlashStringHelper *suffix) const; // character access char charAt(unsigned int index) const { @@ -204,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()); } @@ -212,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); @@ -273,11 +339,11 @@ 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 capacity of both sides and want to manipulate internal buffers directly + // make sure we still have access to internal methods, since we optimize based on the capacity of both sides and want to manipulate internal buffers directly friend String operator +(const String &lhs, String &&rhs); friend String operator +(String &&lhs, String &&rhs); friend String operator +(char lhs, String &&rhs); @@ -285,6 +351,8 @@ class String { 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; @@ -302,6 +370,8 @@ 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); bool changeBuffer(unsigned int maxStrLen); 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 8fa4421802..6c49a38fcf 100644 --- a/cores/esp8266/cont_util.cpp +++ b/cores/esp8266/cont_util.cpp @@ -18,34 +18,50 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "cont.h" +#include + #include #include -#include "ets_sys.h" -extern "C" { +#include "core_esp8266_features.h" +#include "debug.h" + +#include "cont.h" + +extern "C" +{ -#define CONT_STACKGUARD 0xfeefeffe +static constexpr uint32_t CONT_STACKSIZE_U32 { sizeof(cont_t::stack) / sizeof(*cont_t::stack) }; void cont_init(cont_t* cont) { memset(cont, 0, sizeof(cont_t)); cont->stack_guard1 = CONT_STACKGUARD; cont->stack_guard2 = CONT_STACKGUARD; - cont->stack_end = cont->stack + (sizeof(cont->stack) / 4); + cont->stack_end = &cont->stack[0] + CONT_STACKSIZE_U32; cont->struct_start = (unsigned*) cont; // fill stack with magic values to check high water mark - for(int pos = 0; pos < (int)(sizeof(cont->stack) / 4); pos++) + for(int pos = 0; pos < (int)(CONT_STACKSIZE_U32); pos++) { cont->stack[pos] = CONT_STACKGUARD; } } -int IRAM_ATTR cont_check(cont_t* cont) { - if(cont->stack_guard1 != CONT_STACKGUARD || cont->stack_guard2 != CONT_STACKGUARD) return 1; +void IRAM_ATTR cont_check_guard(cont_t* cont) { + if ((cont->stack_guard1 != CONT_STACKGUARD) + || (cont->stack_guard2 != CONT_STACKGUARD)) + { + __stack_chk_fail(); + __builtin_unreachable(); + } +} - return 0; +void IRAM_ATTR cont_check_overflow(cont_t* cont) { + if (cont->sp_suspend && (cont->sp_suspend < &cont->stack[0])) { + __stack_overflow(cont, cont->sp_suspend); + __builtin_unreachable(); + } } // No need for this to be in IRAM, not expected to be IRQ called @@ -62,9 +78,9 @@ int cont_get_free_stack(cont_t* cont) { return freeWords * 4; } -bool IRAM_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 58f615c784..0c0eac4d48 100644 --- a/cores/esp8266/core_esp8266_app_entry_noextra4k.cpp +++ b/cores/esp8266/core_esp8266_app_entry_noextra4k.cpp @@ -39,7 +39,19 @@ 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(); } diff --git a/cores/esp8266/core_esp8266_features.cpp b/cores/esp8266/core_esp8266_features.cpp index 68f631d05c..fa51ad8431 100644 --- a/cores/esp8266/core_esp8266_features.cpp +++ b/cores/esp8266/core_esp8266_features.cpp @@ -20,30 +20,45 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include #include +#ifdef __cplusplus +extern "C" { +#endif + /* precache() * pre-loads flash data into the flash cache * if f==0, preloads instructions starting at the address we were called from. * otherwise preloads flash at the given address. * All preloads are word aligned. */ -#ifdef __cplusplus -extern "C" { -#endif - void precache(void *f, uint32_t bytes) { // Size of a cache page in bytes. We only need to read one word per // page (ie 1 word in 8) for this to work. #define CACHE_PAGE_SIZE 32 - uint32_t a0; - __asm__("mov.n %0, a0" : "=r"(a0)); - uint32_t lines = (bytes/CACHE_PAGE_SIZE)+2; - volatile uint32_t *p = (uint32_t*)((f ? (uint32_t)f : a0) & ~0x03); - uint32_t x; - for (uint32_t i=0; i 0) + || ((data[1] & (1 << 16)) > 0); } #ifdef __cplusplus diff --git a/cores/esp8266/core_esp8266_features.h b/cores/esp8266/core_esp8266_features.h index 6609a0d082..9c574f20c7 100644 --- a/cores/esp8266/core_esp8266_features.h +++ b/cores/esp8266/core_esp8266_features.h @@ -20,11 +20,9 @@ */ - #ifndef CORE_ESP8266_FEATURES_H #define CORE_ESP8266_FEATURES_H - #define CORE_HAS_LIBB64 #define CORE_HAS_BASE64_CLASS #define CORE_HAS_CXA_GUARD @@ -33,9 +31,10 @@ #define WIFI_HAS_EVENT_CALLBACK #define WIFI_IS_OFF_AT_BOOT -#include // malloc() +#include // bool #include // size_t #include +#include // malloc() #ifndef __STRINGIFY #define __STRINGIFY(a) #a @@ -118,14 +117,26 @@ int esp_get_cpu_freq_mhz() #else inline int esp_get_cpu_freq_mhz() { + uint8_t system_get_cpu_freq(void); return system_get_cpu_freq(); } #endif + // Call this function in your setup() to cause the phase locked version of the generator to // be linked in automatically. Otherwise, the default PWM locked version will be used. void enablePhaseLockedWaveform(void); +// Determine when the sketch runs on ESP8285 +#if !defined(CORE_MOCK) +bool esp_is_8285() __attribute__((const, nothrow)); +#else +inline bool esp_is_8285() +{ + return false; +} +#endif + #ifdef __cplusplus } #endif diff --git a/cores/esp8266/core_esp8266_flash_quirks.cpp b/cores/esp8266/core_esp8266_flash_quirks.cpp index 7128fcfe2d..2ae3a39050 100644 --- a/cores/esp8266/core_esp8266_flash_quirks.cpp +++ b/cores/esp8266/core_esp8266_flash_quirks.cpp @@ -66,12 +66,13 @@ void initFlashQuirks() { newSR3=SR3; if (get_flash_mhz()>26) { // >26Mhz? // Set the output drive to 100% + // These definitions are for the XM25QH32B part. On a XM25QH32C + // part, the XM25QH32B's 100% is C's 25% driver strength. newSR3 &= ~(SPI_FLASH_SR3_XMC_DRV_MASK << SPI_FLASH_SR3_XMC_DRV_S); newSR3 |= (SPI_FLASH_SR3_XMC_DRV_100 << SPI_FLASH_SR3_XMC_DRV_S); } if (newSR3 != SR3) { // only write if changed - if (SPI0Command(SPI_FLASH_CMD_WEVSR,NULL,0,0)==SPI_RESULT_OK) // write enable volatile SR - SPI0Command(SPI_FLASH_CMD_WSR3,&newSR3,8,0); // write to SR3 + SPI0Command(SPI_FLASH_CMD_WSR3,&newSR3,8,0,SPI_FLASH_CMD_WEVSR); // write to SR3, use write enable volatile prefix SPI0Command(SPI_FLASH_CMD_WRDI,NULL,0,0); // write disable - probably not needed } } diff --git a/cores/esp8266/core_esp8266_main.cpp b/cores/esp8266/core_esp8266_main.cpp index ff95d08cfb..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" { @@ -62,7 +66,7 @@ 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. @@ -80,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)); @@ -106,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(); @@ -140,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) @@ -147,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(); @@ -199,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[]; @@ -253,7 +316,6 @@ static void __unhandled_exception_cpp() } #endif } - } void init_done() { @@ -320,7 +382,12 @@ extern "C" void app_entry_redefinable(void) /* 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(); } @@ -348,12 +415,252 @@ extern "C" void __disableWiFiAtBootTime (void) 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(); @@ -370,7 +677,7 @@ extern "C" void user_init(void) { install_vm_exception_handler(); #endif -#if defined(NON32XFER_HANDLER) || defined(MMU_IRAM_HEAP) +#if defined(NON32XFER_HANDLER) || (defined(MMU_IRAM_HEAP) && (NONOSDK < (0x30000))) install_non32xfer_exception_handler(); #endif diff --git a/cores/esp8266/core_esp8266_non32xfer.cpp b/cores/esp8266/core_esp8266_non32xfer.cpp index 5047e8de5a..eaf2323c96 100644 --- a/cores/esp8266/core_esp8266_non32xfer.cpp +++ b/cores/esp8266/core_esp8266_non32xfer.cpp @@ -26,10 +26,14 @@ * Code taken directly from @pvvx's public domain code in * https://github.com/pvvx/esp8266web/blob/master/app/sdklib/system/app_main.c * + * In Espressif versions NONOSDK v3.0.0+ a similar feature was added + * load_non_32_wide_handler. Theirs is always loaded. Add weak attribute to + * theirs so we can choose by adding an alias to ours. * */ #include +#include #define VERIFY_C_ASM_EXCEPTION_FRAME_STRUCTURE #include #include @@ -58,10 +62,13 @@ extern "C" { #define EXCCAUSE_LOAD_STORE_ERROR 3 /* Non 32-bit read/write error */ +#if (defined(NON32XFER_HANDLER) || defined(MMU_IRAM_HEAP)) && (NONOSDK < (0x30000)) static fn_c_exception_handler_t old_c_handler = NULL; +#endif +#if defined(NON32XFER_HANDLER) || (defined(MMU_IRAM_HEAP) && (NONOSDK < (0x30000))) static -IRAM_ATTR void non32xfer_exception_handler(struct __exception_frame *ef, int cause) +IRAM_ATTR void non32xfer_exception_handler(struct __exception_frame *ef, [[maybe_unused]] int cause) { do { uint32_t insn, excvaddr; @@ -138,6 +145,7 @@ IRAM_ATTR void non32xfer_exception_handler(struct __exception_frame *ef, int cau } while(false); /* Fail request, die */ +#if (NONOSDK < (0x30000)) /* The old handler points to the SDK. Be alert for HWDT when Calling with INTLEVEL != 0. I cannot create it any more. I thought I saw this as a @@ -148,16 +156,23 @@ IRAM_ATTR void non32xfer_exception_handler(struct __exception_frame *ef, int cau old_c_handler(ef, cause); return; } - +#endif /* Calling _xtos_unhandled_exception(ef, cause) in the Boot ROM, gets us a hardware wdt. Use panic instead as a fall back. It will produce a stack trace. */ +#if defined(DEBUG_ESP_PORT) || defined(DEBUG_ESP_MMU) panic(); +#else + // For non-debug builds, save on resources + abort(); +#endif } +#endif // #if defined(NON32XFER_HANDLER) || (defined(MMU_IRAM_HEAP) && (NONOSDK < (0x30000))) +#if (defined(NON32XFER_HANDLER) || defined(MMU_IRAM_HEAP)) && (NONOSDK < (0x30000)) /* To operate reliably, this module requires the new `_xtos_set_exception_handler` from `exc-sethandler.cpp` and @@ -174,5 +189,17 @@ void install_non32xfer_exception_handler(void) { non32xfer_exception_handler); } } +#else +// For v3.0.x SDKs, no need for install - call_user_start will do the job. +// Need this for build dependencies +void install_non32xfer_exception_handler(void) __attribute__((weak)); +void install_non32xfer_exception_handler(void) {} +#endif + +#if defined(NON32XFER_HANDLER) +// For SDKs 3.0.x, we made load_non_32_wide_handler in +// libmain.c:user_exceptions.o a weak symbol allowing this override to work. +extern void load_non_32_wide_handler(struct __exception_frame *ef, int cause) __attribute__((alias("non32xfer_exception_handler"))); +#endif }; diff --git a/cores/esp8266/core_esp8266_noniso.cpp b/cores/esp8266/core_esp8266_noniso.cpp index f15fdc0860..fb5d8f573a 100644 --- a/cores/esp8266/core_esp8266_noniso.cpp +++ b/cores/esp8266/core_esp8266_noniso.cpp @@ -28,19 +28,12 @@ #include #include #include + #include "stdlib_noniso.h" extern "C" { -char* ltoa(long value, char* result, int base) { - return itoa((int)value, result, base); -} - -char* ultoa(unsigned long value, char* result, int base) { - return utoa((unsigned int)value, result, base); -} - -char * dtostrf(double number, signed char width, unsigned char prec, char *s) { +char* dtostrf(double number, signed char width, unsigned char prec, char *s) noexcept { bool negative = false; if (isnan(number)) { @@ -125,7 +118,7 @@ char * dtostrf(double number, signed char width, unsigned char prec, char *s) { */ const char* strrstr(const char*__restrict p_pcString, - const char*__restrict p_pcPattern) + const char*__restrict p_pcPattern) noexcept { const char* pcResult = 0; @@ -149,4 +142,4 @@ const char* strrstr(const char*__restrict p_pcString, return pcResult; } -}; +} // extern "C" diff --git a/cores/esp8266/core_esp8266_phy.cpp b/cores/esp8266/core_esp8266_phy.cpp index f06a698bf7..4657bb0081 100644 --- a/cores/esp8266/core_esp8266_phy.cpp +++ b/cores/esp8266/core_esp8266_phy.cpp @@ -303,12 +303,26 @@ extern int __real_spi_flash_read(uint32_t addr, uint32_t* dst, size_t size); extern int IRAM_ATTR __wrap_spi_flash_read(uint32_t addr, uint32_t* dst, size_t size); extern int __get_adc_mode(); +/* + Verified that the wide filtering of all 128 byte flash reads during + spoof_init_data continues to be safe for SDK 3.0.5 + From start call to user_pre_init() to stop call with user_rf_pre_init(). + + flash read count during spoof_init_data 4 + flash read 0x00000 8 // system_get_flash_size_map() + flash read 0x00000 4 // system_partition_table_regist() + flash read 0xFB000 128 // PHY_DATA (EEPROM address space) + flash read 0xFC000 628 // RC_CAL + */ extern int IRAM_ATTR __wrap_spi_flash_read(uint32_t addr, uint32_t* dst, size_t size) { if (!spoof_init_data || size != 128) { return __real_spi_flash_read(addr, dst, size); } +#if (NONOSDK >= (0x30000)) + spoof_init_data = false; +#endif memcpy(dst, phy_init_data, sizeof(phy_init_data)); ((uint8_t*)dst)[107] = __get_adc_mode(); return 0; @@ -329,23 +343,29 @@ extern int __get_adc_mode(void) extern void __run_user_rf_pre_init(void) __attribute__((weak)); extern void __run_user_rf_pre_init(void) { - return; // default do noting + return; // default do nothing } +#if (NONOSDK >= (0x30000)) +void sdk3_begin_phy_data_spoof(void) +{ + spoof_init_data = true; +} +#else uint32_t user_rf_cal_sector_set(void) { spoof_init_data = true; return flashchip->chip_size/SPI_FLASH_SEC_SIZE - 4; } +#endif void user_rf_pre_init() { // *((volatile uint32_t*) 0x60000710) = 0; +#if (NONOSDK < (0x30000)) spoof_init_data = false; - volatile uint32_t* rtc_reg = (volatile uint32_t*) 0x60001000; - rtc_reg[30] = 0; +#endif - system_set_os_print(0); int rf_mode = __get_rf_mode(); if (rf_mode >= 0) { system_phy_set_rfoption(rf_mode); diff --git a/cores/esp8266/core_esp8266_postmortem.cpp b/cores/esp8266/core_esp8266_postmortem.cpp index 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 e4bb1c2b07..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 IRAM_ATTR onSclChange(void); static void IRAM_ATTR onSdaChange(void); - static void eventTask(ETSEvent *e); - static void IRAM_ATTR onTimer(void *unused); + 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 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); + 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 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(); + 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) @@ -234,7 +270,8 @@ 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 IRAM_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 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 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 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 IRAM_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 IRAM_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 IRAM_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 IRAM_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,7 +705,7 @@ 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)) @@ -672,7 +719,7 @@ void IRAM_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 IRAM_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 IRAM_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 IRAM_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 IRAM_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,7 +900,7 @@ void IRAM_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; } } @@ -870,7 +917,7 @@ void IRAM_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 IRAM_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 IRAM_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 IRAM_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 IRAM_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 IRAM_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 5f2ea25f92..cd5e153c27 100644 --- a/cores/esp8266/core_esp8266_spi_utils.cpp +++ b/cores/esp8266/core_esp8266_spi_utils.cpp @@ -32,6 +32,7 @@ #include "core_esp8266_features.h" #include "spi_utils.h" +#include "spi_flash_defs.h" extern "C" uint32_t Wait_SPI_Idle(SpiFlashChip *fc); @@ -51,12 +52,12 @@ namespace experimental { static SpiOpResult PRECACHE_ATTR _SPICommand(volatile uint32_t spiIfNum, uint32_t spic,uint32_t spiu,uint32_t spiu1,uint32_t spiu2, - uint32_t *data,uint32_t writeWords,uint32_t readWords) + uint32_t *data,uint32_t writeWords,uint32_t readWords, uint32_t pre_cmd) { if (spiIfNum>1) return SPI_RESULT_ERR; - // force SPI register access via base+offset. + // force SPI register access via base+offset. // Prevents loading individual address constants from flash. uint32_t *spibase = (uint32_t*)(spiIfNum ? &(SPI1CMD) : &(SPI0CMD)); #define SPIREG(reg) (*((volatile uint32_t *)(spibase+(&(reg) - &(SPI0CMD))))) @@ -65,6 +66,7 @@ _SPICommand(volatile uint32_t spiIfNum, // Everything defined here must be volatile or the optimizer can // treat them as constants, resulting in the flash reads we're // trying to avoid + SpiFlashOpResult (* volatile SPI_write_enablep)(SpiFlashChip *) = SPI_write_enable; uint32_t (* volatile Wait_SPI_Idlep)(SpiFlashChip *) = Wait_SPI_Idle; volatile SpiFlashChip *fchip=flashchip; volatile uint32_t spicmdusr=SPICMDUSR; @@ -77,15 +79,30 @@ _SPICommand(volatile uint32_t spiIfNum, PRECACHE_START(); Wait_SPI_Idlep((SpiFlashChip *)fchip); } - + // preserve essential controller state such as incoming/outgoing // data lengths and IO mode. uint32_t oldSPI0U = SPIREG(SPI0U); uint32_t oldSPI0U2= SPIREG(SPI0U2); uint32_t oldSPI0C = SPIREG(SPI0C); - //SPI0S &= ~(SPISE|SPISBE|SPISSE|SPISCD); SPIREG(SPI0C) = spic; + + if (SPI_FLASH_CMD_WREN == pre_cmd) { + // See SPI_write_enable comments in esp8266_undocumented.h + SPI_write_enablep((SpiFlashChip *)fchip); + } else + if (pre_cmd) { + // Send prefix cmd w/o data - sends 8 bits. eg. Volatile SR Write Enable, 0x50 + SPIREG(SPI0U) = (spiu & ~(SPIUMOSI|SPIUMISO)); + SPIREG(SPI0U1) = 0; + SPIREG(SPI0U2) = (spiu2 & ~0xFFFFu) | pre_cmd; + + SPIREG(SPI0CMD) = spicmdusr; //Send cmd + while ((SPIREG(SPI0CMD) & spicmdusr)); + } + + //SPI0S &= ~(SPISE|SPISBE|SPISSE|SPISCD); SPIREG(SPI0U) = spiu; SPIREG(SPI0U1)= spiu1; SPIREG(SPI0U2)= spiu2; @@ -117,11 +134,22 @@ _SPICommand(volatile uint32_t spiIfNum, SPIREG(SPI0U) = oldSPI0U; SPIREG(SPI0U2)= oldSPI0U2; SPIREG(SPI0C) = oldSPI0C; - - PRECACHE_END(); + if (!spiIfNum) { + // w/o a call to Wait_SPI_Idlep, 'Exception 0' or other exceptions (saw + // 28) may occur later after returning to iCache code. This issue was + // observed with non-volatile status register writes. + // + // My guess is: Returning too soon to uncached iCache executable space. An + // iCache read may not complete properly because the Flash or SPI + // interface is still busy with the last write operation. In such a case, + // I expect new reads from iROM to result in zeros. This would explain + // the Exception 0 for code, and Exception 20, 28, and 29 where a literal + // was misread as 0 and then used as a pointer. + Wait_SPI_Idlep((SpiFlashChip *)fchip); xt_wsr_ps(saved_ps); } + PRECACHE_END(); return (timeout>0 ? SPI_RESULT_OK : SPI_RESULT_TIMEOUT); } @@ -139,12 +167,37 @@ _SPICommand(volatile uint32_t spiIfNum, * miso_bits * Number of bits to read from the SPI bus after the outgoing * data has been sent. + * pre_cmd + * A few SPI Flash commands require enable commands to immediately preceed + * them. Since two calls to SPI0Command from ICACHE memory most likely would + * be separated by SPI Flash read request for iCache, use this option to + * supply a prefix command, 8-bits w/o read or write data. + * + * Case in point from the GD25Q32E datasheet: "The Write Enable for Volatile + * Status Register command must be issued prior to a Write Status Register + * command and any other commands can’t be inserted between them." * * Note: This code has only been tested with SPI bus 0, but should work * equally well with other buses. The ESP8266 has bus 0 and 1, * newer chips may have more one day. + * + * Supplemental Notes: + * + * SPI Bus wire view: Think of *data as an array of bytes, byte[0] goes out + * first with the most significant bit shifted out first and so on. When + * thinking of the data as an array of 32bit-words, the least significant byte + * of the first 32bit-word goes out first on the SPI bus with the most + * significant bit of that byte shifted out first onto the wire. + * + * When presenting a 3 or 4-byte address, the byte order will need to be + * reversed. Don't overthink it. For a 3-byte address, view *data as a byte + * array and set the first 3-bytes to the address. eg. byteData[0] MSB, + * byteData[1] middle, and byteData[2] LSB. + * + * When sending a fractional byte, fill in the most significant bit positions + * of the byte first. */ -SpiOpResult SPI0Command(uint8_t cmd, uint32_t *data, uint32_t mosi_bits, uint32_t miso_bits) { +SpiOpResult SPI0Command(uint8_t cmd, uint32_t *data, uint32_t mosi_bits, uint32_t miso_bits, uint32_t pre_cmd) { if (mosi_bits>(64*8)) return SPI_RESULT_ERR; if (miso_bits>(64*8)) @@ -159,8 +212,16 @@ SpiOpResult SPI0Command(uint8_t cmd, uint32_t *data, uint32_t mosi_bits, uint32_ if (miso_bits % 32 != 0) miso_words++; + // Use SPI_CS_SETUP to add time for #CS to settle (ringing) before SPI CLK + // begins. The BootROM does not do this; however, RTOS SDK and NONOS SDK do + // as part of flash init/configuration. + // + // One SPI bus clock cycle time inserted between #CS active and the 1st SPI + // bus clock cycle. The number of clock cycles is in SPI_CNTRL2 + // SPI_SETUP_TIME, which defaults to 1. + // // Select user defined command mode in the controller - uint32_t spiu=SPIUCOMMAND; //SPI_USR_COMMAND + uint32_t spiu=SPIUCOMMAND | SPIUCSSETUP; //SPI_USR_COMMAND | SPI_CS_SETUP // Set the command byte to send uint32_t spiu2 = ((7 & SPIMCOMMAND)<> (miso_bits % 8u)) & 0xFFu) << whole_byte_bits; + } + data[miso_bits/32u] &= mask; } } return rc; diff --git a/cores/esp8266/core_esp8266_vm.cpp b/cores/esp8266/core_esp8266_vm.cpp index 2a53e69ae2..00f50e3981 100644 --- a/cores/esp8266/core_esp8266_vm.cpp +++ b/cores/esp8266/core_esp8266_vm.cpp @@ -56,7 +56,7 @@ * 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 @@ -71,6 +71,8 @@ extern "C" { +#define VM_OFFSET_MASK 0x007fffffu + #define SHORT_MASK 0x000008u #define LOAD_MASK 0x00f00fu #define L8UI_MATCH 0x000002u @@ -159,20 +161,21 @@ static struct cache_line *__vm_cache; // Always points to MRU (hence the line be constexpr int addrmask = ~(sizeof(__vm_cache[0].w)-1); // Helper to mask off bits present in cache entry - static void spi_init(spi_regs *spi1) { pinMode(sck, SPECIAL); pinMode(miso, SPECIAL); pinMode(mosi, SPECIAL); pinMode(cs, SPECIAL); - spi1->spi_cmd = 0; + // spi_ctrl appears to need setting before other SPI registers + spi1->spi_ctrl = 0; // MSB first + plain SPI mode + asm("" ::: "memory"); GPMUX &= ~(1 << 9); spi1->spi_clock = spi_clkval; - spi1->spi_ctrl = 0 ; // MSB first + plain SPI mode spi1->spi_ctrl1 = 0; // undocumented, clear for safety? spi1->spi_ctrl2 = 0; // No add'l delays on signals spi1->spi_user2 = 0; // No insn or insn_bits to set + spi1->spi_cmd = 0; } // Note: GCC optimization -O2 and -O3 tried and returned *slower* code than the default @@ -324,21 +327,21 @@ static IRAM_ATTR void loadstore_exception_handler(struct __exception_frame *ef, uint32_t val = ef->a_reg[regno]; uint32_t what = insn & STORE_MASK; if (what == S8I_MATCH) { - spi_ramwrite(spi1, excvaddr & 0x1ffff, 8-1, val); + spi_ramwrite(spi1, excvaddr & VM_OFFSET_MASK, 8-1, val); } else if (what == S16I_MATCH) { - spi_ramwrite(spi1, excvaddr & 0x1ffff, 16-1, val); + spi_ramwrite(spi1, excvaddr & VM_OFFSET_MASK, 16-1, val); } else { - spi_ramwrite(spi1, excvaddr & 0x1ffff, 32-1, val); + spi_ramwrite(spi1, excvaddr & VM_OFFSET_MASK, 32-1, val); } } else { if (insn & L32_MASK) { - ef->a_reg[regno] = spi_ramread(spi1, excvaddr & 0x1ffff, 32-1); + 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 & 0x1ffff, 16-1); + 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 & 0x1ffff, 8-1); + ef->a_reg[regno] = spi_ramread(spi1, excvaddr & VM_OFFSET_MASK, 8-1); } } } @@ -389,6 +392,11 @@ void install_vm_exception_handler() __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); } diff --git a/cores/esp8266/core_esp8266_waveform_phase.cpp b/cores/esp8266/core_esp8266_waveform_phase.cpp index de2f914121..4240ccb3c1 100644 --- a/cores/esp8266/core_esp8266_waveform_phase.cpp +++ b/cores/esp8266/core_esp8266_waveform_phase.cpp @@ -211,7 +211,7 @@ int startWaveformClockCycles_weak(uint8_t pin, uint32_t highCcys, uint32_t lowCc } 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; diff --git a/cores/esp8266/core_esp8266_waveform_pwm.cpp b/cores/esp8266/core_esp8266_waveform_pwm.cpp index 916361e2ca..f85ccb76aa 100644 --- a/cores/esp8266/core_esp8266_waveform_pwm.cpp +++ b/cores/esp8266/core_esp8266_waveform_pwm.cpp @@ -40,6 +40,7 @@ #include +#include #include "ets_sys.h" #include "core_esp8266_waveform.h" #include "user_interface.h" @@ -162,7 +163,7 @@ static IRAM_ATTR void _notifyPWM(PWMState *p, bool idle) { forceTimerInterrupt(); while (pwmState.pwmUpdate) { if (idle) { - delay(0); + esp_yield(); } MEMBARRIER(); } @@ -372,8 +373,8 @@ int startWaveformClockCycles_weak(uint8_t pin, uint32_t timeHighCycles, uint32_t 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; @@ -392,8 +393,8 @@ int startWaveformClockCycles_weak(uint8_t pin, uint32_t timeHighCycles, uint32_t 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(); } } diff --git a/cores/esp8266/core_esp8266_wiring.cpp b/cores/esp8266/core_esp8266_wiring.cpp index b956cebe43..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"))); diff --git a/cores/esp8266/coredecls.h b/cores/esp8266/coredecls.h index b9c771df77..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,28 +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 // __cplusplus +// 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()) { + } +} -#endif // __COREDECLS_H +// 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 // __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 06af1e424b..327aace64e 100644 --- a/cores/esp8266/debug.cpp +++ b/cores/esp8266/debug.cpp @@ -30,7 +30,7 @@ void __iamslow(const char* what) #endif IRAM_ATTR -void hexdump(const void *mem, uint32_t len, uint8_t cols) +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); diff --git a/cores/esp8266/debug.h b/cores/esp8266/debug.h index 263d3e9149..437479748c 100644 --- a/cores/esp8266/debug.h +++ b/cores/esp8266/debug.h @@ -4,45 +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 -extern "C" 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__)); \ - } \ + 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) +#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 33b1e5822f..29a15744b0 100644 --- a/cores/esp8266/esp8266_peri.h +++ b/cores/esp8266/esp8266_peri.h @@ -255,7 +255,7 @@ extern volatile uint32_t* const esp8266_gpioToFn[16]; //UART STATUS Registers Bits #define USTX 31 //TX PIN Level (Doesn't seem to work, always reads as 0 for both uarts. HW bug? Possible workaround: Enable loopback UxC0 |= 1< // returns true when addr can be used without "pgm_" functions or non32xfer service -constexpr bool __byteAddressable(const void* addr) +inline bool __byteAddressable(const void* addr) { return addr < (const void*)(XCHAL_DATARAM0_VADDR + XCHAL_DATARAM0_SIZE); } diff --git a/cores/esp8266/exc-sethandler.cpp b/cores/esp8266/exc-sethandler.cpp index b8f9d69a35..64a45b0307 100644 --- a/cores/esp8266/exc-sethandler.cpp +++ b/cores/esp8266/exc-sethandler.cpp @@ -39,8 +39,10 @@ * architecture, I am not convinced it can be done safely. * */ +#include // need NONOSDK -#if defined(NON32XFER_HANDLER) || defined(MMU_IRAM_HEAP) || defined(NEW_EXC_C_WRAPPER) || defined(MMU_EXTERNAL_HEAP) +#if defined(NON32XFER_HANDLER) || defined(MMU_IRAM_HEAP) || \ + defined(NEW_EXC_C_WRAPPER) || defined(MMU_EXTERNAL_HEAP) || (NONOSDK >= (0x30000)) /* * The original module source code came from: diff --git a/cores/esp8266/flash_hal.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/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 896002d2b9..54db1ede4a 100644 --- a/cores/esp8266/heap.cpp +++ b/cores/esp8266/heap.cpp @@ -5,6 +5,13 @@ #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 @@ -153,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; } @@ -172,7 +179,7 @@ void IRAM_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[strlen_P(file) + 1]; @@ -247,8 +254,11 @@ 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; } @@ -287,8 +297,11 @@ void* IRAM_ATTR heap_pvPortCalloc(size_t count, size_t size, const char* file, i 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; } @@ -329,8 +342,10 @@ size_t IRAM_ATTR xPortWantedSizeAlign(size_t size) void system_show_malloc(void) { +#ifdef UMM_INFO HeapSelectDram ephemeral; umm_info(NULL, true); +#endif } /* @@ -341,25 +356,25 @@ void system_show_malloc(void) void* IRAM_ATTR pvPortMalloc(size_t size, const char* file, int line) { HeapSelectDram ephemeral; - return heap_pvPortMalloc(size, file, line);; + return heap_pvPortMalloc(size, file, line);; } void* IRAM_ATTR pvPortCalloc(size_t count, size_t size, const char* file, int line) { HeapSelectDram ephemeral; - return heap_pvPortCalloc(count, size, file, line); + return heap_pvPortCalloc(count, size, file, line); } void* IRAM_ATTR pvPortRealloc(void *ptr, size_t size, const char* file, int line) { HeapSelectDram ephemeral; - return heap_pvPortRealloc(ptr, size, file, line); + return heap_pvPortRealloc(ptr, size, file, line); } void* IRAM_ATTR pvPortZalloc(size_t size, const char* file, int line) { HeapSelectDram ephemeral; - return heap_pvPortZalloc(size, file, line); + return heap_pvPortZalloc(size, file, line); } void IRAM_ATTR vPortFree(void *ptr, const char* file, int line) @@ -369,7 +384,98 @@ void IRAM_ATTR vPortFree(void *ptr, const char* file, int line) // correct context. umm_malloc free internally determines the correct heap. HeapSelectDram ephemeral; #endif - return heap_vPortFree(ptr, file, line); + return heap_vPortFree(ptr, file, line); +} + +#if (NONOSDK >= (0x30000)) +//////////////////////////////////////////////////////////////////////////////// +/* + New for NON-OS SDK 3.0.0 and up + Needed for WPA2 Enterprise support. This was not present in SDK pre 3.0 + + The NON-OS SDK 3.0.x has breaking changes to pvPortMalloc. They added one more + argument for selecting a heap. To avoid breaking the build, I renamed their + breaking version to sdk3_pvPortMalloc. To complete the fix, the LIBS need to + be edited. + + Also in the release are low-level functions pvPortZallocIram and + pvPortCallocIram, which are not documented in the Espressif NONOS SDK manual. + No issues in providing replacements. For the non-Arduino ESP8266 applications, + pvPortZallocIram and pvPortCallocIram would have been selected through the + macros like os_malloc defined in `mem.h`. + + OOM - Implementation strategy - Native v3.0 SDK + * For functions `pvPortMalloc(,,,true);` and `pvPortMallocIram(,,,);` on a + failed IRAM alloc, try DRAM. + * For function `pvPortMalloc(,,,false);` use DRAM only - on fail, do not + try IRAM. + + WPA2 Enterprise connect crashing is fixed at v3.0.2 and up. +*/ +#ifdef UMM_HEAP_IRAM +void* IRAM_ATTR sdk3_pvPortMalloc(size_t size, const char* file, int line, bool iram) +{ + if (iram) { + HeapSelectIram ephemeral; + void* ret = heap_pvPortMalloc(size, file, line); + if (ret) return ret; + } + { + HeapSelectDram ephemeral; + return heap_pvPortMalloc(size, file, line); + } } +void* IRAM_ATTR pvPortCallocIram(size_t count, size_t size, const char* file, int line) +{ + { + HeapSelectIram ephemeral; + void* ret = heap_pvPortCalloc(count, size, file, line); + if (ret) return ret; + } + { + HeapSelectDram ephemeral; + return heap_pvPortCalloc(count, size, file, line); + } +} + +void* IRAM_ATTR pvPortZallocIram(size_t size, const char* file, int line) +{ + { + HeapSelectIram ephemeral; + void* ret = heap_pvPortZalloc(size, file, line); + if (ret) return ret; + } + { + HeapSelectDram ephemeral; + return heap_pvPortZalloc(size, file, line); + } +} +#define CONFIG_IRAM_MEMORY 1 + +#else +// For sdk3_pvPortMalloc, the bool argument is ignored and intentionally omitted. +extern "C" void* sdk3_pvPortMalloc(size_t size, const char* file, int line) __attribute__ ((alloc_size(1), malloc, nothrow, alias("pvPortMalloc"))); +extern "C" void* pvPortCallocIram(size_t count, size_t size, const char* file, int line) __attribute__((alloc_size(1, 2), malloc, nothrow, alias("pvPortCalloc"))); +extern "C" void* pvPortZallocIram(size_t size, const char* file, int line) __attribute__((alloc_size(1), malloc, nothrow, alias("pvPortZalloc"))); +#define CONFIG_IRAM_MEMORY 0 +#endif // #ifdef UMM_HEAP_IRAM + +/* + We do not need the function user_iram_memory_is_enabled(). + 1. It was used by mem_manager.o which was replaced with this custom heap + implementation. IRAM memory selection is handled differently for + Arduino ESP8266. + 2. In libmain.a, Cache_Read_Enable_New uses it for cache size. However, When + using IRAM for memory or running with 48K IRAM for code, we use a + replacement Cache_Read_Enable to correct the cache size ignoring + Cache_Read_Enable_New's selected value. + 3. Create a linker conflicts in the event the sketch author tries to control + IRAM heap through this method. +*/ +uint32 IRAM_ATTR user_iram_memory_is_enabled(void) +{ + return CONFIG_IRAM_MEMORY; +} +#endif // #if (NONOSDK >= (0x30000)) }; diff --git a/cores/esp8266/heap_api_debug.h b/cores/esp8266/heap_api_debug.h new file mode 100644 index 0000000000..62f7e7bad5 --- /dev/null +++ b/cores/esp8266/heap_api_debug.h @@ -0,0 +1,74 @@ +/* + * Issolated heap debug helper code from from umm_malloc/umm_malloc_cfg.h. + * Updated umm_malloc/umm_malloc.h and Arduino.h to reference. + * No #ifdef fenceing was used before. From its previous location, this content + * was reassert multiple times through Arduino.h. In case there are legacy + * projects that depend on the previous unfenced behavior, no fencing has been + * added. + */ + +/* + * *alloc redefinition - Included from Arduino.h for DEBUG_ESP_OOM support. + * + * It can also be directly include by the sketch for UMM_POISON_CHECK or + * UMM_POISON_CHECK_LITE builds to get more info about the caller when they + * report on a fail. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef DEBUG_ESP_OOM +#define MEMLEAK_DEBUG + +#include "umm_malloc/umm_malloc_cfg.h" + +#include +// Reuse pvPort* calls, since they already support passing location information. +// Specifically the debug version (heap_...) that does not force DRAM heap. +void *IRAM_ATTR heap_pvPortMalloc(size_t size, const char *file, int line); +void *IRAM_ATTR heap_pvPortCalloc(size_t count, size_t size, const char *file, int line); +void *IRAM_ATTR heap_pvPortRealloc(void *ptr, size_t size, const char *file, int line); +void *IRAM_ATTR heap_pvPortZalloc(size_t size, const char *file, int line); +void IRAM_ATTR heap_vPortFree(void *ptr, const char *file, int line); + +#define malloc(s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_pvPortMalloc(s, mem_debug_file, __LINE__); }) +#define calloc(n,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_pvPortCalloc(n, s, mem_debug_file, __LINE__); }) +#define realloc(p,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_pvPortRealloc(p, s, mem_debug_file, __LINE__); }) + +#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) +#define dbg_heap_free(p) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_vPortFree(p, mem_debug_file, __LINE__); }) +#else +#define dbg_heap_free(p) free(p) +#endif + +#elif defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE) // #elif for #ifdef DEBUG_ESP_OOM +#include +void *IRAM_ATTR heap_pvPortRealloc(void *ptr, size_t size, const char *file, int line); +#define realloc(p,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_pvPortRealloc(p, s, mem_debug_file, __LINE__); }) + +void IRAM_ATTR heap_vPortFree(void *ptr, const char *file, int line); +// C - to be discussed +/* + Problem, I would like to report the file and line number with the umm poison + event as close as possible to the event. The #define method works for malloc, + calloc, and realloc those names are not as generic as free. A #define free + captures too much. Classes with methods called free are included :( + Inline functions would report the address of the inline function in the .h + not where they are called. + + Anybody know a trick to make this work? + + Create dbg_heap_free() as an alternative for free() when you need a little + more help in debugging the more challenging problems. +*/ +#define dbg_heap_free(p) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_vPortFree(p, mem_debug_file, __LINE__); }) + +#else +#define dbg_heap_free(p) free(p) +#endif /* DEBUG_ESP_OOM */ + +#ifdef __cplusplus +} +#endif diff --git a/cores/esp8266/hwdt_app_entry.cpp b/cores/esp8266/hwdt_app_entry.cpp index 3a657cfcd9..0f0bee8d4e 100644 --- a/cores/esp8266/hwdt_app_entry.cpp +++ b/cores/esp8266/hwdt_app_entry.cpp @@ -180,7 +180,9 @@ * tool performing hardware reset and exiting, then the serial monitor * re-engaging. This is not an issue that needs to be addressed here. */ - #define DEBUG_ESP_HWDT_PRINT_GREETING + #ifndef DEBUG_ESP_HWDT_PRINT_GREETING + #define DEBUG_ESP_HWDT_PRINT_GREETING (1) + #endif /* @@ -275,6 +277,8 @@ #include #include #include +#include "umm_malloc/umm_malloc.h" +#include "mmu_iram.h" extern "C" { #include @@ -952,13 +956,13 @@ STATIC void IRAM_MAYBE handle_hwdt(void) { /* Print separate ctx: cont stack */ /* Check if cont stack is yielding to SYS */ - if (0 == hwdt_info.cont_integrity && 0 != g_pcont->pc_yield) { - ctx_cont_ptr = (const uint32_t *)((uintptr_t)g_pcont->sp_yield - 8u); + 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_yield) { - ETS_PRINTF("\nCont stack is yielding. Active stack starts at 0x%08X.\n", (uint32_t)g_pcont->sp_yield - 8u); + 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); } } @@ -994,7 +998,7 @@ STATIC void IRAM_MAYBE handle_hwdt(void) { } #endif -#if defined(DEBUG_ESP_HWDT_PRINT_GREETING) +#if DEBUG_ESP_HWDT_PRINT_GREETING ETS_PRINTF("\n\nHardware WDT Stack Dump - enabled\n\n"); #else ETS_PRINTF("\n\n"); @@ -1007,6 +1011,18 @@ STATIC void IRAM_MAYBE handle_hwdt(void) { #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 @@ -1019,34 +1035,11 @@ STATIC void IRAM_MAYBE handle_hwdt(void) { * 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. */ -#define ICACHE_SIZE_32 1 -#define ICACHE_SIZE_16 0 - -extern "C" void Cache_Read_Disable(void); -extern "C" void Cache_Read_Enable(uint8_t map, uint8_t p, uint8_t v); - -#ifndef USE_IRAM -static void IRAM_ATTR __attribute__((noinline)) handle_hwdt_icache() __attribute__((used)); -void handle_hwdt_icache() { - Cache_Read_Enable(0, 0, ICACHE_SIZE_16); - handle_hwdt(); - Cache_Read_Disable(); -} -#endif // USE_IRAM - - -#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 /* hwdt_pre_sdk_init() is the result of a hook for development diagnotics which @@ -1071,9 +1064,8 @@ void hwdt_pre_sdk_init(void) { #endif } -static void IRAM_ATTR __attribute__((noinline)) hwdt_pre_sdk_init_icache(void) __attribute__((used)); +static void __attribute__((noinline)) hwdt_pre_sdk_init_icache(void) __attribute__((used)); void hwdt_pre_sdk_init_icache(void) { - Cache_Read_Enable(0, 0, ICACHE_SIZE_16); #ifdef DEBUG_ESP_HWDT_UART_SPEED const uint32_t uart_divisor = set_uart_speed(0, DEBUG_ESP_HWDT_UART_SPEED); #endif @@ -1085,7 +1077,6 @@ void hwdt_pre_sdk_init_icache(void) { adjust_uart_speed(uart_divisor); } #endif - Cache_Read_Disable(); } /* @@ -1106,6 +1097,7 @@ asm ( ".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" @@ -1129,7 +1121,9 @@ asm ( #ifdef USE_IRAM "call0 handle_hwdt\n\t" #else - "call0 handle_hwdt_icache\n\t" + "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. @@ -1160,7 +1154,9 @@ asm ( /* * Allow for running additional diagnotics supplied at link time. */ - "call0 hwdt_pre_sdk_init_icache\n\t" + "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. @@ -1174,7 +1170,12 @@ asm ( * 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" diff --git a/cores/esp8266/mmu_iram.cpp b/cores/esp8266/mmu_iram.cpp index 7a3548e4a3..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) @@ -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 7ba06be9bb..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 @@ -127,8 +171,26 @@ bool mmu_is_icache(const void *addr) { 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/spi_utils.h b/cores/esp8266/spi_utils.h index bf0928f288..181554a55a 100644 --- a/cores/esp8266/spi_utils.h +++ b/cores/esp8266/spi_utils.h @@ -35,7 +35,7 @@ typedef enum { SPI_RESULT_TIMEOUT } SpiOpResult; -SpiOpResult SPI0Command(uint8_t cmd, uint32_t *data, uint32_t mosi_bits, uint32_t miso_bits); +SpiOpResult SPI0Command(uint8_t cmd, uint32_t *data, uint32_t mosi_bits, uint32_t miso_bits, uint32_t pre_cmd=0); } #ifdef __cplusplus diff --git a/cores/esp8266/stdlib_noniso.cpp b/cores/esp8266/stdlib_noniso.cpp index 693dfda850..4fed9b615d 100644 --- a/cores/esp8266/stdlib_noniso.cpp +++ b/cores/esp8266/stdlib_noniso.cpp @@ -21,11 +21,13 @@ #include "stdlib_noniso.h" +extern "C" { + // ulltoa() is slower than std::to_char() (1.6 times) // but is smaller by ~800B/flash and ~250B/rodata // ulltoa fills str backwards and can return a pointer different from str -char* ulltoa(unsigned long long val, char* str, int slen, unsigned int radix) +char* ulltoa(unsigned long long val, char* str, int slen, unsigned int radix) noexcept { str += --slen; *str = 0; @@ -39,7 +41,7 @@ char* ulltoa(unsigned long long val, char* str, int slen, unsigned int radix) } // lltoa fills str backwards and can return a pointer different from str -char* lltoa (long long val, char* str, int slen, unsigned int radix) +char* lltoa(long long val, char* str, int slen, unsigned int radix) noexcept { bool neg; if (val < 0) @@ -60,3 +62,13 @@ char* lltoa (long long val, char* str, int slen, unsigned int radix) } return ret; } + +char* ltoa(long value, char* result, int base) noexcept { + return itoa((int)value, result, base); +} + +char* ultoa(unsigned long value, char* result, int base) noexcept { + return utoa((unsigned int)value, result, base); +} + +} // extern "C" diff --git a/cores/esp8266/stdlib_noniso.h b/cores/esp8266/stdlib_noniso.h index f86f78befc..0c8c488ed1 100644 --- a/cores/esp8266/stdlib_noniso.h +++ b/cores/esp8266/stdlib_noniso.h @@ -22,38 +22,35 @@ #ifndef STDLIB_NONISO_H #define STDLIB_NONISO_H +#include + #ifdef __cplusplus -extern "C"{ +extern "C" { #endif -int atoi(const char *s); - -long atol(const char* s); - -double atof(const char* s); - -char* itoa (int val, char *s, int radix); - -char* ltoa (long val, char *s, int radix); +#ifdef __cplusplus +#define __STDLIB_NONISO_NOEXCEPT noexcept +#else +#define __STDLIB_NONISO_NOEXCEPT +#endif -char* lltoa (long long val, char* str, int slen, unsigned int radix); +char* ltoa (long val, char *s, int radix) __STDLIB_NONISO_NOEXCEPT; -char* utoa (unsigned int val, char *s, int radix); +char* lltoa (long long val, char* str, int slen, unsigned int radix) __STDLIB_NONISO_NOEXCEPT; -char* ultoa (unsigned long val, char *s, int radix); +char* ultoa (unsigned long val, char *s, int radix) __STDLIB_NONISO_NOEXCEPT; -char* ulltoa (unsigned long long val, char* str, int slen, unsigned int radix); +char* ulltoa (unsigned long long val, char* str, int slen, unsigned int radix) __STDLIB_NONISO_NOEXCEPT; -char* dtostrf (double val, signed char width, unsigned char prec, char *s); +char* dtostrf (double val, signed char width, unsigned char prec, char *s) __STDLIB_NONISO_NOEXCEPT; -void reverse(char* begin, char* end); +const char* strrstr (const char*__restrict p_pcString, + const char*__restrict p_pcPattern) __STDLIB_NONISO_NOEXCEPT; -const char* strrstr(const char*__restrict p_pcString, - const char*__restrict p_pcPattern); +#undef __STDLIB_NONISO_NOEXCEPT #ifdef __cplusplus } // extern "C" #endif - #endif diff --git a/cores/esp8266/time.cpp b/cores/esp8266/time.cpp index 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 de574d0bee..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" @@ -493,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; @@ -566,7 +567,7 @@ uart_wait_tx_empty(uart_t* uart) return; while(uart_tx_fifo_available(uart->uart_nr) > 0) - delay(0); + esp_yield(); } @@ -695,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); } @@ -943,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/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/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 2554d23ff6..21f10cfc9e 100644 --- a/cores/esp8266/umm_malloc/dbglog/dbglog.h +++ b/cores/esp8266/umm_malloc/dbglog/dbglog.h @@ -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 a329bf103c..282e87b8ff 100644 --- a/cores/esp8266/umm_malloc/umm_heap_select.h +++ b/cores/esp8266/umm_malloc/umm_heap_select.h @@ -32,70 +32,77 @@ class HeapSelect { public: #if (UMM_NUM_HEAPS == 1) - MAYBE_ALWAYS_INLINE - HeapSelect(size_t id) { (void)id; } - MAYBE_ALWAYS_INLINE - ~HeapSelect() {} +MAYBE_ALWAYS_INLINE +HeapSelect(size_t id) { + (void)id; +} +MAYBE_ALWAYS_INLINE +~HeapSelect() { +} #else - MAYBE_ALWAYS_INLINE - 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); - } +} - MAYBE_ALWAYS_INLINE - ~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 - MAYBE_ALWAYS_INLINE - 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); - } +} - MAYBE_ALWAYS_INLINE - ~HeapSelectIram() { +MAYBE_ALWAYS_INLINE +~HeapSelectIram() { umm_set_heap_by_id(_heap_id); - } +} protected: - size_t _heap_id; +size_t _heap_id; #else - MAYBE_ALWAYS_INLINE - HeapSelectIram() {} - MAYBE_ALWAYS_INLINE - ~HeapSelectIram() {} +MAYBE_ALWAYS_INLINE +HeapSelectIram() { +} +MAYBE_ALWAYS_INLINE +~HeapSelectIram() { +} #endif }; class HeapSelectDram { public: #if (UMM_NUM_HEAPS == 1) - MAYBE_ALWAYS_INLINE - HeapSelectDram() {} - MAYBE_ALWAYS_INLINE - ~HeapSelectDram() {} +MAYBE_ALWAYS_INLINE +HeapSelectDram() { +} +MAYBE_ALWAYS_INLINE +~HeapSelectDram() { +} #else - MAYBE_ALWAYS_INLINE - 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); - } +} - MAYBE_ALWAYS_INLINE - ~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 b88e013b67..c0ebb7948f 100644 --- a/cores/esp8266/umm_malloc/umm_info.c +++ b/cores/esp8266/umm_malloc/umm_info.c @@ -25,174 +25,182 @@ // 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()); } /* @@ -200,60 +208,61 @@ size_t umm_max_block_size( void ) { 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 c1169a8a8f..c08e2a27ca 100644 --- a/cores/esp8266/umm_malloc/umm_local.c +++ b/cores/esp8266/umm_malloc/umm_local.c @@ -12,22 +12,21 @@ 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; - } - /* 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(); - } +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 } -#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,50 +164,112 @@ 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) +/* + For the fallback option using `size_t umm_free_heap_size(void)`, we must have + the UMM_INLINE_METRICS build option enabled to support free heap size + reporting without the use of `umm_info()`. +*/ +size_t umm_free_heap_size_lw(void) __attribute__ ((alias("umm_free_heap_size"))); + +#else /* - I assume xPortGetFreeHeapSize needs to be in IRAM. Since - system_get_free_heap_size is in IRAM. Which would mean, umm_free_heap_size() - in flash, was not a safe alternative for returning the same information. + We require a resource to track and report free Heap size with low overhead. + For an expanded discussion see Notes.h, entry dated "Sep 26, 2022" */ +#error UMM_INLINE_METRICS, UMM_STATS, or UMM_STATS_FULL needs to be defined. +#endif + #if defined(UMM_STATS) || defined(UMM_STATS_FULL) -size_t xPortGetFreeHeapSize(void) __attribute__ ((alias("umm_free_heap_size_lw"))); +size_t umm_free_heap_size_core_lw(umm_heap_context_t *_context) { + return (size_t)_context->UMM_FREE_BLOCKS * sizeof(umm_block); +} + #elif defined(UMM_INFO) -#ifndef UMM_INLINE_METRICS -#warning "No ISR safe function available to implement xPortGetFreeHeapSize()" +// Backfill support for umm_free_heap_size_core_lw() +size_t umm_free_heap_size_core_lw(umm_heap_context_t *_context) __attribute__ ((alias("umm_free_heap_size_core"))); #endif + +/* + This API is called by `system_get_free_heap_size()` which is in IRAM. Driving + the assumption the callee may be in an ISR or Cache_Read_Disable state. Use + IRAM to ensure that the complete call chain is in IRAM. + + To satisfy this requirement, we need UMM_STATS... or UMM_INLINE_METRICS + defined. These support an always available without intense computation + free-Heap value. + + Like the other vPort... APIs used by the SDK, this must always report on the + DRAM Heap not the current Heap. +*/ +#if (UMM_NUM_HEAPS == 1) +// Reduce IRAM usage for the single Heap case +#if defined(UMM_STATS) || defined(UMM_STATS_FULL) +size_t xPortGetFreeHeapSize(void) __attribute__ ((alias("umm_free_heap_size_lw"))); +#else size_t xPortGetFreeHeapSize(void) __attribute__ ((alias("umm_free_heap_size"))); #endif +#else +size_t xPortGetFreeHeapSize(void) { + #if defined(UMM_STATS) || defined(UMM_STATS_FULL) || defined(UMM_INLINE_METRICS) + UMM_CHECK_INITIALIZED(); + umm_heap_context_t *_context = umm_get_heap_by_id(UMM_HEAP_DRAM); + + return umm_free_heap_size_core_lw(_context); + #else + // At this time, this build path is not reachable. In case things change, + // keep build check. + // Not in IRAM, umm_info() would have been used to complete this operation. + #error "No ISR safe function available to implement xPortGetFreeHeapSize()" + #endif +} +#endif + #if defined(UMM_STATS) || defined(UMM_STATS_FULL) void umm_print_stats(int force) { - umm_heap_context_t *_context = umm_get_current_heap(); - - 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 @@ -214,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 @@ -228,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 f73b2a1832..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,22 +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(PSTR(fmt), ##__VA_ARGS__) +#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 a8b93505d6..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 within 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 need 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 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 ); - } - - /* Allocate the memory within 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 9b1d0ba568..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,65 +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 - -#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 - /* * There are a number of defines you can set at compile time that affect how * the memory allocator will operate. @@ -116,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 @@ -143,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 */ @@ -191,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 /* -------------------------------------------------------------------------- */ @@ -207,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! */ @@ -229,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; @@ -238,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 /* @@ -312,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; @@ -344,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) @@ -353,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 @@ -448,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; @@ -462,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 ////////////////////////////////////////////////////////////////////////////////////// @@ -495,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. @@ -550,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 /* @@ -594,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 ///////////////////////////////////////////////// @@ -669,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) /* @@ -705,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 @@ -722,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 ///////////////////////////////////////////////// @@ -739,106 +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. -// 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) -#include -void* IRAM_ATTR heap_pvPortRealloc(void *ptr, size_t size, const char* file, int line); -#define realloc(p,s) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_pvPortRealloc(p, s, mem_debug_file, __LINE__); }) - -void IRAM_ATTR heap_vPortFree(void *ptr, const char* file, int line); -//C - to be discussed -/* - Problem, I would like to report the file and line number with the umm poison - event as close as possible to the event. The #define method works for malloc, - calloc, and realloc those names are not as generic as free. A #define free - captures too much. Classes with methods called free are included :( - Inline functions would report the address of the inline function in the .h - not where they are called. - - Anybody know a trick to make this work? - - Create dbg_heap_free() as an alternative for free() when you need a little - more help in debugging the more challenging problems. -*/ -#define dbg_heap_free(p) ({ static const char mem_debug_file[] PROGMEM STORE_ATTR = __FILE__; heap_vPortFree(p, mem_debug_file, __LINE__); }) - -#else -#define dbg_heap_free(p) free(p) -#endif /* DEBUG_ESP_OOM */ - -#ifdef __cplusplus -} -#endif diff --git a/cores/esp8266/umm_malloc/umm_malloc_cfgport.h b/cores/esp8266/umm_malloc/umm_malloc_cfgport.h 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/doc/Makefile b/doc/Makefile index 36b4923488..61d3e40b3c 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -2,7 +2,7 @@ # # You can set these variables from the command line. -SPHINXOPTS = +SPHINXOPTS = --fail-on-warning --nitpicky SPHINXBUILD = sphinx-build SPHINXPROJ = ESP8266ArduinoCore SOURCEDIR = . @@ -17,4 +17,4 @@ help: # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/doc/Troubleshooting/stack_dump.rst b/doc/Troubleshooting/stack_dump.rst index c977fce56d..ef320392e7 100644 --- a/doc/Troubleshooting/stack_dump.rst +++ b/doc/Troubleshooting/stack_dump.rst @@ -12,42 +12,42 @@ Example: Exception (0): epc1=0x402103f4 epc2=0x00000000 epc3=0x00000000 excvaddr=0x00000000 depc=0x00000000 - ctx: sys + ctx: sys sp: 3ffffc10 end: 3fffffb0 offset: 01a0 >>>stack>>> - 3ffffdb0: 40223e00 3fff6f50 00000010 60000600 - 3ffffdc0: 00000001 4021f774 3fffc250 4000050c - 3ffffdd0: 400043d5 00000030 00000016 ffffffff - 3ffffde0: 400044ab 3fffc718 3ffffed0 08000000 - 3ffffdf0: 60000200 08000000 00000003 00000000 - 3ffffe00: 0000ffff 00000001 04000002 003fd000 - 3ffffe10: 3fff7188 000003fd 3fff2564 00000030 - 3ffffe20: 40101709 00000008 00000008 00000020 - 3ffffe30: c1948db3 394c5e70 7f2060f2 c6ba0c87 - 3ffffe40: 3fff7058 00000001 40238d41 3fff6ff0 - 3ffffe50: 3fff6f50 00000010 60000600 00000020 - 3ffffe60: 402301a8 3fff7098 3fff7014 40238c77 - 3ffffe70: 4022fb6c 40230ebe 3fff1a5b 3fff6f00 - 3ffffe80: 3ffffec8 00000010 40231061 3fff0f90 - 3ffffe90: 3fff6848 3ffed0c0 60000600 3fff6ae0 - 3ffffea0: 3fff0f90 3fff0f90 3fff6848 3fff6d40 - 3ffffeb0: 3fff28e8 40101233 d634fe1a fffeffff - 3ffffec0: 00000001 00000000 4022d5d6 3fff6848 - 3ffffed0: 00000002 4000410f 3fff2394 3fff6848 - 3ffffee0: 3fffc718 40004a3c 000003fd 3fff7188 - 3ffffef0: 3fffc718 40101510 00000378 3fff1a5b - 3fffff00: 000003fd 4021d2e7 00000378 000003ff - 3fffff10: 00001000 4021d37d 3fff2564 000003ff - 3fffff20: 000003fd 60000600 003fd000 3fff2564 - 3fffff30: ffffff00 55aa55aa 00000312 0000001c - 3fffff40: 0000001c 0000008a 0000006d 000003ff - 3fffff50: 4021d224 3ffecf90 00000000 3ffed0c0 - 3fffff60: 00000001 4021c2e9 00000003 3fff1238 - 3fffff70: 4021c071 3ffecf84 3ffecf30 0026a2b0 - 3fffff80: 4021c0b6 3fffdab0 00000000 3fffdcb0 - 3fffff90: 3ffecf40 3fffdab0 00000000 3fffdcc0 - 3fffffa0: 40000f49 40000f49 3fffdab0 40000f49 + 3ffffdb0: 40223e00 3fff6f50 00000010 60000600 + 3ffffdc0: 00000001 4021f774 3fffc250 4000050c + 3ffffdd0: 400043d5 00000030 00000016 ffffffff + 3ffffde0: 400044ab 3fffc718 3ffffed0 08000000 + 3ffffdf0: 60000200 08000000 00000003 00000000 + 3ffffe00: 0000ffff 00000001 04000002 003fd000 + 3ffffe10: 3fff7188 000003fd 3fff2564 00000030 + 3ffffe20: 40101709 00000008 00000008 00000020 + 3ffffe30: c1948db3 394c5e70 7f2060f2 c6ba0c87 + 3ffffe40: 3fff7058 00000001 40238d41 3fff6ff0 + 3ffffe50: 3fff6f50 00000010 60000600 00000020 + 3ffffe60: 402301a8 3fff7098 3fff7014 40238c77 + 3ffffe70: 4022fb6c 40230ebe 3fff1a5b 3fff6f00 + 3ffffe80: 3ffffec8 00000010 40231061 3fff0f90 + 3ffffe90: 3fff6848 3ffed0c0 60000600 3fff6ae0 + 3ffffea0: 3fff0f90 3fff0f90 3fff6848 3fff6d40 + 3ffffeb0: 3fff28e8 40101233 d634fe1a fffeffff + 3ffffec0: 00000001 00000000 4022d5d6 3fff6848 + 3ffffed0: 00000002 4000410f 3fff2394 3fff6848 + 3ffffee0: 3fffc718 40004a3c 000003fd 3fff7188 + 3ffffef0: 3fffc718 40101510 00000378 3fff1a5b + 3fffff00: 000003fd 4021d2e7 00000378 000003ff + 3fffff10: 00001000 4021d37d 3fff2564 000003ff + 3fffff20: 000003fd 60000600 003fd000 3fff2564 + 3fffff30: ffffff00 55aa55aa 00000312 0000001c + 3fffff40: 0000001c 0000008a 0000006d 000003ff + 3fffff50: 4021d224 3ffecf90 00000000 3ffed0c0 + 3fffff60: 00000001 4021c2e9 00000003 3fff1238 + 3fffff70: 4021c071 3ffecf84 3ffecf30 0026a2b0 + 3fffff80: 4021c0b6 3fffdab0 00000000 3fffdcb0 + 3fffff90: 3ffecf40 3fffdab0 00000000 3fffdcc0 + 3fffffa0: 40000f49 40000f49 3fffdab0 40000f49 <<`__ tool. +It's possible to decode the Stack to readable information. +You can get a copy and read about the `Esp Exception Decoder `__ tool. + +For a troubleshooting example using the Exception Decoder Tool, read `FAQ: My ESP Crashes <../faq/a02-my-esp-crashes.rst#exception-decoder>`__. .. figure:: ESP_Exception_Decoderp.png :alt: ESP Exception Decoder diff --git a/doc/boards.rst b/doc/boards.rst index 5721fab624..99421d86b9 100644 --- a/doc/boards.rst +++ b/doc/boards.rst @@ -39,25 +39,30 @@ Minimal Hardware Setup for Bootloading and Usage +-----------------+------------+------------------+ | GND | | GND | +-----------------+------------+------------------+ -| TX or GPIO2\* | | RX | +| TX or GPIO2 | | | +| [#tx_or_gpio2]_ | RX | | +-----------------+------------+------------------+ | RX | | TX | +-----------------+------------+------------------+ | GPIO0 | PullUp | DTR | +-----------------+------------+------------------+ -| Reset\* | PullUp | RTS | +| Reset | | | +| [#reset]_ | PullUp | RTS | +-----------------+------------+------------------+ -| GPIO15\* | PullDown | | +| GPIO15 | | | +| [#gpio15]_ | PullDown | | +-----------------+------------+------------------+ -| CH\_PD | PullUp | | +| CH\_PD | | | +| [#ch_pd]_ | PullUp | | +-----------------+------------+------------------+ -- Note -- GPIO15 is also named MTDO -- Reset is also named RSBT or REST (adding PullUp improves the +.. rubric:: Notes + +.. [#tx_or_gpio2] GPIO15 is also named MTDO +.. [#reset] Reset is also named RSBT or REST (adding PullUp improves the stability of the module) -- GPIO2 is alternative TX for the boot loader mode -- **Directly connecting a pin to VCC or GND is not a substitute for a +.. [#gpio15] GPIO2 is alternative TX for the boot loader mode +.. [#ch_pd] **Directly connecting a pin to VCC or GND is not a substitute for a PullUp or PullDown resistor, doing this can break upload management and the serial console, instability has also been noted in some cases.** @@ -88,15 +93,16 @@ ESPxx Hardware +---------------+------------+------------------+ | GPIO0 | | GND | +---------------+------------+------------------+ -| Reset | | RTS\* | +| Reset | | RTS [#rts]_ | +---------------+------------+------------------+ | GPIO15 | PullDown | | +---------------+------------+------------------+ | CH\_PD | PullUp | | +---------------+------------+------------------+ -- Note -- if no RTS is used a manual power toggle is needed +.. rubric:: Notes + +.. [#rts] if no RTS is used a manual power toggle is needed Minimal Hardware Setup for Running only ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -176,7 +182,11 @@ rst cause boot mode ~~~~~~~~~ -the first value respects the pin setup of the Pins 0, 2 and 15. +the first value respects the pin setup of the Pins 0, 2 and 15 + +.. code-block:: + + Number = (GPIO15 << 2) | (GPIO0 << 1) | GPIO2 +----------+----------+---------+---------+-------------+ | Number | GPIO15 | GPIO0 | GPIO2 | Mode | @@ -198,7 +208,6 @@ the first value respects the pin setup of the Pins 0, 2 and 15. | 7 | 3.3V | 3.3V | 3.3V | SDIO | +----------+----------+---------+---------+-------------+ -note: - number = ((GPIO15 << 2) \| (GPIO0 << 1) \| GPIO2); Generic ESP8285 Module ---------------------- @@ -262,6 +271,13 @@ ESPresso Lite 2.0 ESPresso Lite 2.0 is an Arduino-compatible Wi-Fi development board based on an earlier V1 (beta version). Re-designed together with Cytron Technologies, the newly-revised ESPresso Lite V2.0 features the auto-load/auto-program function, eliminating the previous need to reset the board manually before flashing a new program. It also feature two user programmable side buttons and a reset button. The special distinctive features of on-board pads for I2C sensor and actuator is retained. +Mercury 1.0 +----------- + +Based on ESP8266, Mercury is board developed by Ralio Technologies. Board supports on motor drivers and direct-connect feature for various endpoints. + +Product page: https://www.raliotech.com + Phoenix 1.0 ----------- @@ -351,6 +367,11 @@ LOLIN(WEMOS) D1 R2 & mini Product page: https://www.wemos.cc/ +LOLIN(WEMOS) D1 ESP-WROOM-02 +---------------------------- + +No real product pages. See: https://www.instructables.com/How-to-Use-Wemos-ESP-Wroom-02-D1-Mini-WiFi-Module-/ or https://www.arduino-tech.com/wemos-esp-wroom-02-mainboard-d1-mini-wifi-module-esp826618650-battery/ + LOLIN(WEMOS) D1 mini (clone) ---------------------------- @@ -408,14 +429,10 @@ ThaiEasyElec's ESPino ESPino by ThaiEasyElec using WROOM-02 module from Espressif Systems with 4 MB Flash. -We will update an English description soon. - Product page: -http://thaieasyelec.com/products/wireless-modules/wifi-modules/espino-wifi-development-board-detail.html -- Schematics: -www.thaieasyelec.com/downloads/ETEE052/ETEE052\_ESPino\_Schematic.pdf - -Dimensions: -http://thaieasyelec.com/downloads/ETEE052/ETEE052\_ESPino\_Dimension.pdf -- Pinouts: -http://thaieasyelec.com/downloads/ETEE052/ETEE052\_ESPino\_User\_Manual\_TH\_v1\_0\_20160204.pdf (Please see pg. 8) +* Product page (retired product): https://www.thaieasyelec.com/product/%E0%B8%A2%E0%B8%81%E0%B9%80%E0%B8%A5%E0%B8%B4%E0%B8%81%E0%B8%88%E0%B8%B3%E0%B8%AB%E0%B8%99%E0%B9%88%E0%B8%B2%E0%B8%A2-retired-espino-wifi-development-board/11000833173001086 +* Schematics: https://downloads.thaieasyelec.com/ETEE052/ETEE052\_ESPino\_Schematic.pdf +* Dimensions: https://downloads.thaieasyelec.com/ETEE052/ETEE052\_ESPino\_Dimension.pdf +* Pinouts (Please see pg.8): https://downloads.thaieasyelec.com/ETEE052/ETEE052\_ESPino\_User\_Manual\_TH\_v1\_0\_20160204.pdf WifInfo ------- diff --git a/doc/conf.py b/doc/conf.py index 3b05ae5617..0eef82bc24 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -66,12 +66,12 @@ # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = 'en' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] +exclude_patterns = ['_venv', '_build', 'Thumbs.db', '.DS_Store'] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' @@ -162,11 +162,7 @@ # # on_rtd is whether we are on readthedocs.org env_readthedocs = os.environ.get('READTHEDOCS', None) -print(env_readthedocs) if not env_readthedocs: # only import and set the theme if we're building docs locally import sphinx_rtd_theme html_theme = 'sphinx_rtd_theme' - html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] - - diff --git a/doc/esp8266wifi/bearssl-client-secure-class.rst b/doc/esp8266wifi/bearssl-client-secure-class.rst index 4523759f78..28cf590d6d 100644 --- a/doc/esp8266wifi/bearssl-client-secure-class.rst +++ b/doc/esp8266wifi/bearssl-client-secure-class.rst @@ -48,7 +48,7 @@ As a rule, either keep your objects global, use `new` to create them, or ensure TLS and HTTPS Basics ~~~~~~~~~~~~~~~~~~~~ -The following discussion is only intended to give a rough idea of TLS/HTTPS(which is just HTTP over a TLS connection) and the components an application needs to manage to make a TLS connection. For more detailed information, please check the relevant `RFC 5246 `__ and others. +The following discussion is only intended to give a rough idea of TLS/HTTPS(which is just HTTP over a TLS connection) and the components an application needs to manage to make a TLS connection. For more detailed information, please check the relevant `RFC 5246 `__ and others. TLS can be broken into two stages: verifying the identities of server (and potentially client), and then encrypting blocks of data bidirectionally. Verifying the identity of the other partner is handled via keys encoded in X509 certificates, optionally signed by a series of other entities. diff --git a/doc/esp8266wifi/client-class.rst b/doc/esp8266wifi/client-class.rst index 11e412181e..19bc3e660f 100644 --- a/doc/esp8266wifi/client-class.rst +++ b/doc/esp8266wifi/client-class.rst @@ -29,6 +29,36 @@ Default input value 0 means that effective value is left at the discretion of th ``stop()`` returns ``false`` in case of an issue when closing the client (for instance a timed-out ``flush``). Depending on implementation, its parameter can be passed to ``flush()``. +abort +~~~~~ + +.. code:: cpp + + void abort(); + + +Originally proposed in `#8738 `__ +Unlike ``stop()``, immediately shuts down internal connection object. + +Under usual circumstances, we either enter ``CLOSE_WAIT`` or ``TIME_WAIT`` state. But, the connection object is not freed right away, and requires us to either +* wait until ``malloc()`` returns ``NULL`` when our TCP stack tries to allocate memory for a new connection +* manually call ``tcp_kill_timewait()`` to forcibly stop the 'oldest' connection + +This API frees up resources used by the connection. Consider using it instead of ``stop()`` if your application handles a lot of clients and frequently runs out of available heap memory. + +*Example:* + +.. code:: cpp + + # define MIN_HEAP_FREE 20000 // or whatever min available heap memory convienent for your application + auto client = server.accept(); + // ... do something with the client object ... + if (ESP.getFreeHeap() >= MIN_HEAP_FREE) { + client.stop(); + } else { + client.abort(); + } + setNoDelay ~~~~~~~~~~ diff --git a/doc/esp8266wifi/generic-class.rst b/doc/esp8266wifi/generic-class.rst index f1ed42ea06..f722bff3af 100644 --- a/doc/esp8266wifi/generic-class.rst +++ b/doc/esp8266wifi/generic-class.rst @@ -55,6 +55,10 @@ This change is harmless with standard sketches: Calls to ``WiFi.mode()`` do enable radio as usual. It also smooths current spikes at boot and decreases DHCP stress. +Known side-effects: + +- ``WiFi.mode()`` must be called before changing mac addresses with ``wifi_set_macaddr({SOFTAP,STATION}_IF, ...)``. + Legacy behavior can be restored by calling ``enableWiFiAtBootTime()`` from anywhere in the code (it is a weak void function intended to play with the linker). @@ -223,7 +227,11 @@ Other Function Calls bool enableAP (bool enable) int hostByName (const char *aHostname, IPAddress &aResult) - appeared with SDK pre-V3: + +Also, when using NONOS SDK v3: + +.. code:: cpp + uint8_t getListenInterval (); bool isSleepLevelMax (); diff --git a/doc/esp8266wifi/generic-examples.rst b/doc/esp8266wifi/generic-examples.rst index bbac0cd839..98ccf6c47b 100644 --- a/doc/esp8266wifi/generic-examples.rst +++ b/doc/esp8266wifi/generic-examples.rst @@ -38,23 +38,39 @@ Register the Events To get events to work we need to complete just two steps: -1. Declare the event handler: +1. Declare the event handler in global scope. -``cpp WiFiEventHandler disconnectedEventHandler;`` +.. code-block:: cpp -2. Select particular event (in this case ``onStationModeDisconnected``) - and add the code to be executed when event is fired. + WiFiEventHandler disconnectedEventHandler; -``cpp disconnectedEventHandler = WiFi.onStationModeDisconnected([](const WiFiEventStationModeDisconnected& event) { Serial.println("Station disconnected"); });`` If this event is fired the code will print out information that station has been disconnected. +Alternatively, it can be declared as ``static`` in both function and global scopes. -That's it. It is all we need to do. + +2. Select particular event (in this case ``onStationModeDisconnected``). + When this event is fired the code will print out information that station has been disconnected: + +.. code-block:: cpp + + disconnectedEventHandler = WiFi.onStationModeDisconnected( + [](auto&& event) { + Serial.println("Station disconnected"); + }); + +3. Disable ``disconnectedEventHandler``, so the event is no longer handled by our callback: + +.. code-block:: cpp + + disconnectedEventHandler = nullptr; + +Take note that lifetime of the callback handler is up to the app. e.g. if ``onStationModeDisconnected`` is declared in the function scope, it would be discarded immediately after the function exits. The Code ~~~~~~~~ The complete code, including both methods discussed at the beginning, is provided below. -.. code:: cpp +.. code-block:: cpp #include diff --git a/doc/esp8266wifi/scan-class.rst b/doc/esp8266wifi/scan-class.rst index eb94de8aad..4629a97a94 100644 --- a/doc/esp8266wifi/scan-class.rst +++ b/doc/esp8266wifi/scan-class.rst @@ -236,3 +236,29 @@ The ``networkItem`` is a zero based index of network discovered during scan. All 6: UPC Wi-Free, Ch:11 (-79dBm) For code samples please refer to separate section with `examples `__ dedicated specifically to the Scan Class. + +getScanInfoByIndex +^^^^^^^^^^^^^^^^^^ + +Similar to the ``getNetworkInfo``, but instead returns a pointer to the Nth ``bss_info`` structure which is internally used by the NONOS SDK. + +.. code:: cpp + + WiFi.getScanInfoByIndex(networkItem) + +The ``networkItem`` is a zero based index of network discovered during scan. Function will return ``nullptr`` when ``networkItem`` is greater than the number of networks in the scan result or when there are no scan results available. + +.. code:: cpp + + auto n = WiFi.scanNetworks(false, true); + if (n <= 0) { + // scan failed or there are no results + return; + } + + for (int i = 0; i < n; i++) + const auto* info = WiFi.getScanInfoByIndex(i) + // ... use the raw data from the bss_info structure ... + } + +See ``tools/sdk/include/user_interface.h`` for all available fields and `examples `__. diff --git a/doc/esp8266wifi/scan-examples.rst b/doc/esp8266wifi/scan-examples.rst index 4ad4777507..d05d258f72 100644 --- a/doc/esp8266wifi/scan-examples.rst +++ b/doc/esp8266wifi/scan-examples.rst @@ -1,5 +1,11 @@ :orphan: +IDE example +^^^^^^^^^^^ + +- For the currently installed Core, see Arduino IDE > *Examples* > *ESP8266WiFi* > *WiFiScan*. +- For the latest development version, see `WiFiScan.ino `__. + Scan ~~~~ diff --git a/doc/esp8266wifi/server-class.rst b/doc/esp8266wifi/server-class.rst index 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-examples.rst b/doc/esp8266wifi/soft-access-point-examples.rst index c4cf39c6c2..2c9fee762c 100644 --- a/doc/esp8266wifi/soft-access-point-examples.rst +++ b/doc/esp8266wifi/soft-access-point-examples.rst @@ -79,7 +79,9 @@ Sketch is small so analysis shouldn't be difficult. In first line we are includi Setting up of the access point ``ESPsoftAP_01`` is done by executing: -``cpp boolean result = WiFi.softAP("ESPsoftAP_01", "pass-to-soft-AP");`` +.. code:: cpp + + boolean result = WiFi.softAP("ESPsoftAP_01", "pass-to-soft-AP"); If this operation is successful then ``result`` will be ``true`` or ``false`` if otherwise. Basing on that either ``Ready`` or ``Failed!`` will be printed out by the following ``if - else`` conditional statement. diff --git a/doc/esp8266wifi/station-class.rst b/doc/esp8266wifi/station-class.rst index 787de49684..06e072adc6 100644 --- a/doc/esp8266wifi/station-class.rst +++ b/doc/esp8266wifi/station-class.rst @@ -7,6 +7,8 @@ The number of features provided by ESP8266 in the station mode is far more exten Description of station class has been broken down into four parts. First discusses methods to establish connection to an access point. Second provides methods to manage connection like e.g. ``reconnect`` or ``isConnected``. Third covers properties to obtain information about connection like MAC or IP address. Finally the fourth section provides alternate methods to connect like e.g. Wi-Fi Protected Setup (WPS). +An effort to unify such network device class accross several Arduino core implementations has been made. Recommandations are located at `Arduino-Networking-API `__ and tested with `NetApiHelpers `__. Esp8266 Arduino core's station class is also following these guidelines. + Table of Contents ----------------- @@ -97,8 +99,12 @@ config Disable `DHCP `__ client (Dynamic Host Configuration Protocol) and set the IP configuration of station interface to user defined arbitrary values. The interface will be a static IP configuration instead of values provided by DHCP. +Note that to reenable DHCP, all three parameters (local_ip, gateway and subnet) as IPv4 ``0U`` (= 0.0.0.0) must be passed back to config() and re-connecting is needed. + .. code:: cpp + WiFi.config(local_ip, gateway, subnet) (for Arduino API portability, discouraged as chosen defaults may not match the local network configuration) + WiFi.config(local_ip, gateway, subnet, dns1) WiFi.config(local_ip, gateway, subnet, dns1, dns2) Function will return ``true`` if configuration change is applied successfully. If configuration can not be applied, because e.g. module is not in station or station + soft access point mode, then ``false`` will be returned. @@ -116,6 +122,21 @@ The following IP configuration may be provided: (like e.g. *www.google.co.uk*) and translate them for us to IP addresses +For Arduino networking API compatibility, the ESP8266WiFi library supports IPv4-only additional versions of the ``config`` function: + +.. code:: cpp + + WiFi.config(local_ip) (for Arduino API portability, discouraged as chosen defaults may not match the local network configuration) + WiFi.config(local_ip, dns) (for Arduino API portability, discouraged as chosen defaults may not match the local network configuration) + WiFi.config(local_ip, dns, gateway) (for Arduino API portability, discouraged as chosen defaults may not match the local network configuration) + WiFi.config(local_ip, dns, gateway, subnet) + +Versions where some of ``dns``, ``gateway`` and ``subnet`` parameters are not specified use a default value. Default ``subnet`` is 255.255.255.0. Default ``gateway`` and ``dns`` are derived from ``local_ip`` by changing the last number to 1. It is discouraged to use these default values as they may not apply to every network configuration. + +Reminder : To reenable DHCP you can use ``WiFi.config(0U, 0U, 0U);``. + +**Warning: The default values for dns, gateway and subnet may not match your router's settings.** Also please note, that ``config(local_ip, gateway)`` is not supported and ``WiFi.config(local_ip, gateway, subnet)`` doesn't set the DNS server IP. + *Example code:* .. code:: cpp @@ -157,8 +178,7 @@ The following IP configuration may be provided: . Connected, IP address: 192.168.1.22 -Please note that station with static IP configuration usually connects to the network faster. In the above example it took about 500ms (one dot `.` displayed). This is because obtaining of IP configuration by DHCP client takes time and in this case this step is skipped. If you pass all three parameter as 0.0.0.0 (local_ip, gateway and subnet), it will re enable DHCP. You need to re-connect the device to get new IPs. - +Please note that station with static IP configuration usually connects to the network faster. In the above example it took about 500ms (one dot `.` displayed). This is because obtaining of IP configuration by DHCP client takes time and in this case this step is skipped. Reminder: If you pass all three parameters as 0.0.0.0 (local_ip, gateway and subnet), it will re enable DHCP. You need to re-connect the device to get new IPs. Manage Connection ~~~~~~~~~~~~~~~~~ @@ -453,7 +473,7 @@ Function returns one of the following connection statuses: - ``WL_IDLE_STATUS`` when Wi-Fi is in process of changing between statuses - ``WL_DISCONNECTED`` if module is not configured in station mode -Returned value is type of ``wl_status_t`` defined in `wl\_definitions.h `__ +Returned value is type of ``wl_status_t`` defined in `wl\_definitions.h `__ *Example code:* @@ -491,7 +511,7 @@ Returned value is type of ``wl_status_t`` defined in `wl\_definitions.h `__ as follows: +Particular connection statuses 6 and 3 may be looked up in `wl\_definitions.h `__ as follows: :: diff --git a/doc/esp8266wifi/udp-class.rst b/doc/esp8266wifi/udp-class.rst index 5e02f7b3da..73a9086021 100644 --- a/doc/esp8266wifi/udp-class.rst +++ b/doc/esp8266wifi/udp-class.rst @@ -26,11 +26,11 @@ Multicast UDP .. code:: cpp - uint8_t beginMulticast (IPAddress interfaceAddr, IPAddress multicast, uint16_t port) + uint8_t beginMulticast (IPAddress multicast, uint16_t port) virtual int beginPacketMulticast (IPAddress multicastAddress, uint16_t port, IPAddress interfaceAddress, int ttl=1) IPAddress destinationIP () uint16_t localPort () -The ``WiFiUDP`` class supports sending and receiving multicast packets on STA interface. When sending a multicast packet, replace ``udp.beginPacket(addr, port)`` with ``udp.beginPacketMulticast(addr, port, WiFi.localIP())``. When listening to multicast packets, replace ``udp.begin(port)`` with ``udp.beginMulticast(WiFi.localIP(), multicast_ip_addr, port)``. You can use ``udp.destinationIP()`` to tell whether the packet received was sent to the multicast or unicast address. +The ``WiFiUDP`` class supports sending and receiving multicast packets on STA interface. When sending a multicast packet, replace ``udp.beginPacket(addr, port)`` with ``udp.beginPacketMulticast(addr, port, WiFi.localIP())``. When listening to multicast packets, replace ``udp.begin(port)`` with ``udp.beginMulticast(multicast_ip_addr, port)``. You can use ``udp.destinationIP()`` to tell whether the packet received was sent to the multicast or unicast address. For code samples please refer to separate section with `examples `__ dedicated specifically to the UDP Class. diff --git a/doc/faq/a01-espcomm_sync-failed.rst b/doc/faq/a01-espcomm_sync-failed.rst index df4d0aa177..d47a5d2eeb 100644 --- a/doc/faq/a01-espcomm_sync-failed.rst +++ b/doc/faq/a01-espcomm_sync-failed.rst @@ -9,7 +9,7 @@ I am getting "espcomm\_sync failed" error when trying to upload my ESP. How to r - `Reset Methods <#reset-methods>`__ - `Ck <#ck>`__ - `Nodemcu <#nodemcu>`__ -- `I'm Stuck <#im-stuck>`__ +- `I'm Stuck <#i-m-stuck>`__ - `Conclusion <#conclusion>`__ Introduction diff --git a/doc/faq/a02-my-esp-crashes.rst b/doc/faq/a02-my-esp-crashes.rst index a000bd0f79..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 - IRAM_ATTR attribute declared. Not only that, but the entire function tree + By default, all functions are compiled into flash, which means that the + cache may kick in for that code. However, the cache currently can't be used + during hardware interrupts. That means that, if you use a hardware ISR, such as + attachInterrupt(gpio, myISR, CHANGE) for a GPIO change, the ISR must have the + IRAM_ATTR attribute declared. Not only that, but the entire function tree called from the ISR must also have the IRAM_ATTR declared. Be aware that every function that has this attribute reduces available memory. - In addition, it is not possible to execute delay() or yield() from an ISR, + In addition, it is not possible to execute delay() or yield() from an ISR, or do blocking operations, or operations that disable the interrupts, e.g.: read a DHT. Finally, an ISR has very high restrictions on timing for the executed code, meaning - that executed code should not take longer than a very few microseconds. It is + that executed code should not take longer than a very few microseconds. It is considered best practice to set a flag within the ISR, and then from within the loop() check and clear that flag, and execute code. @@ -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/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 4fb065a38f..cfd65eca90 100644 --- a/doc/faq/readme.rst +++ b/doc/faq/readme.rst @@ -186,3 +186,17 @@ 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 85ee96abb0..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. @@ -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) -------------------------------------------- diff --git a/doc/gdb.rst b/doc/gdb.rst index 53e9f07fbb..79e221e844 100644 --- a/doc/gdb.rst +++ b/doc/gdb.rst @@ -380,4 +380,33 @@ breakpoint) command in GDB while debugging instead of the more common ``break`` command, since ``thb`` will remove the breakpoint once it is reached automatically and save you some trouble. +Because of the single hardware breakpoint limitation, you must pay careful +attention to the output from ``gdb`` when you set a breakpoint. If your +breakpoint expression matches multiple locations, as in this example: +.. code:: bash + + (gdb) break loop + Breakpoint 1 at 0x40202c84: loop. (2 locations) + +Then you will be unable to ``continue``: + +.. code:: bash + + (gdb) cont + Continuing. + Note: automatically using hardware breakpoints for read-only addresses. + Warning: + Cannot insert hardware breakpoint 1. + Could not insert hardware breakpoints: + You may have requested too many hardware breakpoints/watchpoints. + +You can resolve this situation by deleting the previous breakpoint and +using a more specific breakpoint expression: + +.. code:: bash + + (gdb) delete + Delete all breakpoints? (y or n) y + (gdb) break mysketch.ino:loop + Breakpoint 2 at 0x40202c84: file .../mysketch.ino, line 113. diff --git a/doc/ideoptions.rst b/doc/ideoptions.rst index f790d02fc0..25fd08defa 100644 --- a/doc/ideoptions.rst +++ b/doc/ideoptions.rst @@ -127,7 +127,31 @@ There are a number of options. - The last (``NoAssert - NDEBUG``) is even quieter than the first (some internal guards are skipped to save more flash). - The other ones may be used when asked by a maintainer or if you are a - developper trying to debug some issues. + developer trying to debug some issues. + +Debug Optimization +~~~~~~~~~~~~~~~~~~ + +Due to the limited resources on the device, our default compiler optimizations +focus on creating the smallest code size (``.bin`` file). That is fine for +release but not ideal for debugging. + +``Debug Optimization`` use to improve Exception Decoder results. + +- ``Lite`` impact on code size uses ``-fno-optimize-sibling-calls`` to alter + the ``-Os`` compiler option to place more caller addresses on the Stack. +- ``Optimum`` offers better quality stack content for the Exception Decoder at + the expense of a larger code size. It uses the ``-Og`` compiler option, which + turns off optimizations that can make debugging difficult while keeping + others. +- ``None`` no changes for debugging continue using ``-Os``. + +Take note some sketches may start working after changing the optimization. Or +fail less often. And it is also possible (not likely) that source code that +was working with ``-Os`` may break with ``-Og``. + +For more topic depth, read `Improving Exception Decoder Results `__ + lwIP variant ~~~~~~~~~~~~ @@ -219,13 +243,31 @@ Erase Flash - ``All Flash``: WiFi settings and Filesystems are erased. -Espressif Firmware +NONOS SDK Version ~~~~~~~~~~~~~~~~~~ -There are a number of available espressif firmwares. The first / default -choice is fine. Only try with others after reading on the issue tracker -that something has to be tried with them. Note that Espressif obsoleted -all of them at the time of writing. +Our Core is based on [Espressif NONOS SDK](https://github.com/espressif/ESP8266_NONOS_SDK). + +- **2.2.1+100 (190703)** (default) +- 2.2.1+119 (191122) +- 2.2.1+113 (191105) +- 2.2.1+111 (191024) +- 2.2.1+61 (190313) +- 2.2.1 (legacy) +- 3.0.5 (experimental) + +See our issue tracker in regards to default version selection. + +* `#6724 (comment) `__ +* `#6826 `__ + +Notice that 3.x.x is provided **as-is** and remains **experimental**. + +Floating Point operations +~~~~~~~~~~~~~~~~~~~~~~~~~ + +- ``in IROM``: This provides more free space in IRAM but disallows using floating operations inside ISRs. +- ``allowed in ISR``: Floats can be used in ISRs, cost is ~1KB IRAM when floats are used. SSL Support ~~~~~~~~~~~ diff --git a/doc/installing.rst b/doc/installing.rst index 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/mmu.rst b/doc/mmu.rst index 9c3ec48acb..27ea4d4d7d 100644 --- a/doc/mmu.rst +++ b/doc/mmu.rst @@ -76,19 +76,28 @@ The Arduino IDE Tools menu option, ``MMU`` has the following selections: MMU related build defines and possible values. These values change as indicated with the menu options above: -+-------------------------+--------------+--------------+------------------------------------+------------------------------+ -| ``#define`` | balanced | IRAM | shared (IRAM and Heap) | not shared (IRAM and Heap) | -+=========================+==============+==============+====================================+==============================+ -| ``MMU_IRAM_SIZE`` | ``0x8000`` | ``0xC000`` | ``0xC000`` | ``0x8000`` | -+-------------------------+--------------+--------------+------------------------------------+------------------------------+ -| ``MMU_ICACHE_SIZE`` | ``0x8000`` | ``0x4000`` | ``0x4000`` | ``0x4000`` | -+-------------------------+--------------+--------------+------------------------------------+------------------------------+ -| ``MMU_IRAM_HEAP`` | -- | -- | defined, enables\ ``umm_malloc`` | -- | -+-------------------------+--------------+--------------+------------------------------------+------------------------------+ -| ``MMU_SEC_HEAP`` | -- | \*\* | \*\* | ``0x40108000`` | -+-------------------------+--------------+--------------+------------------------------------+------------------------------+ -| ``MMU_SEC_HEAP_SIZE`` | -- | \*\* | \*\* | ``0x4000`` | -+-------------------------+--------------+--------------+------------------------------------+------------------------------+ ++-------------+------------+------------+-------------+-------------+ +| ``#define`` | balanced | IRAM | shared | not shared | +| | | | (IRAM and | (IRAM and | +| | | | Heap) | Heap) | ++=============+============+============+=============+=============+ +| ``MMU_ | ``0x8000`` | ``0xC000`` | ``0xC000`` | ``0x8000`` | +| IRAM_SIZE`` | | | | | ++-------------+------------+------------+-------------+-------------+ +| ``MMU_IC | ``0x8000`` | ``0x4000`` | ``0x4000`` | ``0x4000`` | +| ACHE_SIZE`` | | | | | ++-------------+------------+------------+-------------+-------------+ +| ``MMU_ | – | – | defined, | – | +| IRAM_HEAP`` | | | e | | +| | | | nables\ ``u | | +| | | | mm_malloc`` | | ++-------------+------------+------------+-------------+-------------+ +| ``MMU | – | \*\* | \*\* | ``0 | +| _SEC_HEAP`` | | | | x40108000`` | ++-------------+------------+------------+-------------+-------------+ +| ``MMU_SEC_ | – | \*\* | \*\* | ``0x4000`` | +| HEAP_SIZE`` | | | | | ++-------------+------------+------------+-------------+-------------+ \*\* This define is to an inline function that calculates the value, based on unused code space, requires ``#include ``. @@ -112,8 +121,8 @@ The Arduino IDE Tools menu option, ``Non-32-Bit Access`` has the following selec option ``16KB cache + 48KB IRAM and 2nd Heap (shared)``. IRAM, unlike DRAM, must be accessed as aligned full 32-bit words, no -byte or short access. The pgm\_read macros are an option; however, the -store operation remains an issue. For a block copy, ets\_memcpy appears +byte or short access. The pgm_read macros are an option; however, the +store operation remains an issue. For a block copy, ets_memcpy appears to work well as long as the byte count is rounded up to be evenly divided by 4, and source and destination addresses are 4 bytes aligned. @@ -125,6 +134,11 @@ over-optimization. To get a sense of how memory access time is effected, see examples ``MMU48K`` and ``irammem`` in ``ESP8266``. +NON-OS SDK v3.0.0 and above have builtin support for Non-32-Bit Access. +Selecting ``Byte/Word access to IRAM/PROGMEM`` will override the builtin +version with ours. However, there is no known reason to do this other +than debugging. + Miscellaneous ------------- @@ -133,41 +147,43 @@ For calls to ``umm_malloc`` with interrupts disabled. - ``malloc`` will always allocate from the ``DRAM`` heap when called with interrupts disabled. -- ``realloc`` with a NULL pointer will use ``malloc`` and return a - ``DRAM`` heap allocation. Note, calling ``realloc`` with interrupts - disabled is **not** officially supported. You are on your own if you - do this. + + - ``realloc`` with a NULL pointer will use ``malloc`` and return a + ``DRAM`` heap allocation. Note, calling ``realloc`` with + interrupts disabled is **not** officially supported. You are on + your own if you do this. + - If you must use IRAM memory in your ISR, allocate the memory in your init code. To reduce the time spent in the ISR, avoid non32-bit access that would trigger the exception handler. For short or byte access, consider using the inline functions described in section - "Performance Functions" below. + “Performance Functions” below. How to Select Heap ~~~~~~~~~~~~~~~~~~ The ``MMU`` selection ``16KB cache + 48KB IRAM and 2nd Heap (shared)`` allows you to use the standard heap API function calls (``malloc``, -``calloc``, ``free``, ... ). to allocate memory from DRAM or IRAM. This +``calloc``, ``free``, … ). to allocate memory from DRAM or IRAM. This selection can be made by instantiating the class ``HeapSelectIram`` or ``HeapSelectDram``. The usage is similar to that of the ``InterruptLock`` class. The default/initial heap source is DRAM. The class is in ``umm_malloc/umm_heap_select.h``. -:: - - ... - char *bufferDram; - bufferDram = (char *)malloc(33); - char *bufferIram; - { - HeapSelectIram ephemeral; - bufferIram = (char *)malloc(33); - } - ... - free(bufferIram); - free(bufferDram); - ... +.. code:: cpp + + ... + char *bufferDram; + bufferDram = (char *)malloc(33); + char *bufferIram; + { + HeapSelectIram ephemeral; + bufferIram = (char *)malloc(33); + } + ... + free(bufferIram); + free(bufferDram); + ... ``free`` will always return memory to the correct heap. There is no need for tracking and selecting before freeing. @@ -182,8 +198,9 @@ Classes: - ``umm_get_current_heap_id()`` - ``umm_set_heap_by_id( ID value )`` - Possible ID values -- ``UMM_HEAP_DRAM`` -- ``UMM_HEAP_IRAM`` + + - ``UMM_HEAP_DRAM`` + - ``UMM_HEAP_IRAM`` Also, an alternate stack select method API is available. This is not as easy as the class method; however, for some small set of cases, it may @@ -201,9 +218,9 @@ a pointer: .. code:: cpp - bool mmu_is_iram(const void *addr); - bool mmu_is_dram(const void *addr); - bool mmu_is_icache(const void *addr); + bool mmu_is_iram(const void *addr); + bool mmu_is_dram(const void *addr); + bool mmu_is_icache(const void *addr); Performance Functions ~~~~~~~~~~~~~~~~~~~~~ @@ -213,23 +230,23 @@ exception handler reducing execution time and stack use, it comes at the cost of increased code size. These are an alternative to the ``pgm_read`` macros for reading from -IRAM. When compiled with 'Debug Level: core' range checks are performed +IRAM. When compiled with ‘Debug Level: core’ range checks are performed on the pointer value to make sure you are reading from the address range of IRAM, DRAM, or ICACHE. .. code:: cpp - uint8_t mmu_get_uint8(const void *p8); - uint16_t mmu_get_uint16(const uint16_t *p16); - int16_t mmu_get_int16(const int16_t *p16); + uint8_t mmu_get_uint8(const void *p8); + uint16_t mmu_get_uint16(const uint16_t *p16); + int16_t mmu_get_int16(const int16_t *p16); While these functions are intended for writing to IRAM, they will work -with DRAM. When compiled with 'Debug Level: core', range checks are +with DRAM. When compiled with ‘Debug Level: core’, range checks are performed on the pointer value to make sure you are writing to the address range of IRAM or DRAM. .. code:: cpp - uint8_t mmu_set_uint8(void *p8, const uint8_t val); - uint16_t mmu_set_uint16(uint16_t *p16, const uint16_t val); - int16_t mmu_set_int16(int16_t *p16, const int16_t val); + uint8_t mmu_set_uint8(void *p8, const uint8_t val); + uint16_t mmu_set_uint16(uint16_t *p16, const uint16_t val); + int16_t mmu_set_int16(int16_t *p16, const int16_t val); diff --git a/doc/ota_updates/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 94195aabd2..de351fe3f2 100755 --- a/doc/ota_updates/readme.rst +++ b/doc/ota_updates/readme.rst @@ -216,7 +216,7 @@ Requirements Application Example ~~~~~~~~~~~~~~~~~~~ -Instructions below show configuration of OTA on a NodeMCU 1.0 (ESP-12E Module) board. You can use any other board that meets the `requirements <#basic-requirements>`__ described above. This instruction is valid for all operating systems supported by the Arduino IDE. Screen captures have been made on Windows 7 and you may see small differences (like name of the serial port), if you are using Linux or MacOS. +Instructions below show configuration of OTA on a NodeMCU 1.0 (ESP-12E Module) board. You can use any other board that meets the `requirements <#ota-basic-requirements>`__ described above. This instruction is valid for all operating systems supported by the Arduino IDE. Screen captures have been made on Windows 7 and you may see small differences (like name of the serial port), if you are using Linux or MacOS. 1. Before you begin, please make sure that you have the following software installed: @@ -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,6 +523,8 @@ Simple updater downloads the file every time the function is called. .. code:: cpp + #include + WiFiClient client; ESPhttpUpdate.update(client, "192.168.0.2", 80, "/arduino.bin"); @@ -542,6 +537,8 @@ The server-side script can respond as follows: - response code 200, and send the .. code:: cpp + #include + WiFiClient client; t_httpUpdate_return ret = ESPhttpUpdate.update(client, "192.168.0.2", 80, "/esp/update/arduino.php", "optional current version string here"); switch(ret) { @@ -595,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 ---------------- @@ -675,9 +702,29 @@ Updater class Updater is in the Core and deals with writing the firmware to the flash, checking its integrity and telling the bootloader (eboot) to load the new firmware on the next boot. -**Note:** The bootloader command will be stored into the first 128 bytes of user RTC memory, then it will be retrieved by eboot on boot. That means that user data present there will be lost `(per discussion in #5330) `__. +The following `Updater ; + void onProgress(THandlerFunction_Progress); // current and total number of bytes + + using THandlerFunction_Error = std::function; + void onStart(THandlerFunction_Error); // error code + + using THandlerFunction = std::function; + void onEnd(THandlerFunction); + void onError(THandlerFunction); + +Using RTC memory +~~~~~~~~~~~~~~~~ + +The bootloader command will be stored into the first 128 bytes of user RTC memory, then it will be retrieved by eboot on boot. That means that user data present there will be lost `(per discussion in #5330) `__. + +Flash mode and size +~~~~~~~~~~~~~~~~~~~ -**Note:** For uncompressed firmware images, the Updater will change the flash mode bits if they differ from the flash mode the device is currently running at. This ensures that the flash mode is not changed to an incompatible mode when the device is in a remote or hard to access area. Compressed images are not modified, thus changing the flash mode in this instance could result in damage to the ESP8266 and/or flash memory chip or your device no longer be accessible via OTA, and requiring re-flashing via a serial connection `(per discussion in #7307) `__. +For uncompressed firmware images, the Updater will change the flash mode bits if they differ from the flash mode the device is currently running at. This ensures that the flash mode is not changed to an incompatible mode when the device is in a remote or hard to access area. Compressed images are not modified, thus changing the flash mode in this instance could result in damage to the ESP8266 and/or flash memory chip or your device no longer be accessible via OTA, and requiring re-flashing via a serial connection `(per discussion in #7307) `__. Update process - memory view ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/reference.rst b/doc/reference.rst index 792e867a47..e06dfd8a3d 100644 --- a/doc/reference.rst +++ b/doc/reference.rst @@ -27,15 +27,22 @@ and have several limitations: or use a scheduled function (which will be called outside of the interrupt context when it is safe) to do long-running work. -* Memory operations can be dangerous and should be avoided in interrupts. - Calls to ``new`` or ``malloc`` should be minimized because they may require - a long running time if memory is fragmented. Calls to ``realloc`` and - ``free`` must NEVER be called. Using any routines or objects which call - ``free`` or ``realloc`` themselves is also forbidden for the same reason. - This means that ``String``, ``std::string``, ``std::vector`` and other - classes which use contiguous memory that may be resized must be used with - extreme care (ensuring strings aren't changed, vector elements aren't - added, etc.). +* Heap API operations can be dangerous and should be avoided in interrupts. + Calls to ``malloc`` should be minimized because they may require a long + running time if memory is fragmented. Calls to ``realloc`` and ``free`` + must NEVER be called. Using any routines or objects which call ``free`` or + ``realloc`` themselves is also forbidden for the same reason. This means + that ``String``, ``std::string``, ``std::vector`` and other classes which + use contiguous memory that may be resized must be used with extreme care + (ensuring strings aren't changed, vector elements aren't added, etc.). + The underlying problem, an allocation address could be actively in use at + the instant of an interrupt. Upon return, the address actively in use may + be invalid after an ISR uses ``realloc`` or ``free`` against the same + allocation. + +* The C++ ``new`` and ``delete`` operators must NEVER be used in an ISR. Their + call path is not in IRAM. Using any routines or objects that use the ``new`` + or ``delete`` operator is also forbidden. Digital IO ---------- diff --git a/doc/requirements.txt b/doc/requirements.txt index 8d90d8f59a..6b2684762d 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1,10 +1,8 @@ # Requirements file for pip # list of Python packages used in documentation build -sphinx -sphinx-rtd-theme -breathe -nbsphinx -docutils<0.17 -testresources -#at the time of writing, requirement is pygments<3,>=2.4.1 -pygments>=2.4.1 +sphinx>=8.1.2,<9.0.0 +sphinx-rtd-theme>=3.0.2,<4.0.0 +breathe>=4.36.0,<5.0.0 +nbsphinx>=0.9.7,<1.0.0 +testresources>=2.0.1,<3.0.0 +pygments>=2.19.1,<3.0.0 diff --git a/keywords.txt b/keywords.txt index bad947b0df..ca7b2f1337 100644 --- a/keywords.txt +++ b/keywords.txt @@ -19,4 +19,3 @@ 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 ad50516c81..44af7e4d90 100644 --- a/libraries/DNSServer/src/DNSServer.h +++ b/libraries/DNSServer/src/DNSServer.h @@ -1,7 +1,17 @@ #ifndef DNSServer_h #define DNSServer_h + +#include #include +// #define DEBUG_DNSSERVER + +// https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.txt +#ifndef IANA_DNS_PORT +#define IANA_DNS_PORT 53 // AKA domain +constexpr inline uint16_t kIanaDnsPort = IANA_DNS_PORT; +#endif + #define DNS_QR_QUERY 0 #define DNS_QR_RESPONSE 1 #define DNS_OPCODE_QUERY 0 @@ -45,6 +55,15 @@ struct DNSHeader uint16_t ARCount; // number of resource entries }; +constexpr inline size_t kDNSSQueSizeAddrBits = 3; // The number of bits used to address que entries +constexpr inline size_t kDNSSQueSize = BIT(kDNSSQueSizeAddrBits); + +struct DNSS_REQUESTER { + uint32_t ip; + uint16_t port; + uint16_t id; +}; + class DNSServer { public: @@ -52,24 +71,60 @@ class DNSServer ~DNSServer() { stop(); }; + /* + If specified, `enableForwarder` will update the `domainName` that is used + to match DNS request to this AP's IP Address. A non-matching request will + be forwarded to the DNS server specified by `dns`. + + Returns `true` on success. + + Returns `false`, + * when forwarding `dns` is not set, or + * unable to allocate resources for managing the DNS forward function. + */ + bool enableForwarder(const String &domainName = emptyString, const IPAddress &dns = (uint32_t)0); + /* + `disableForwarder` will stop forwarding DNS requests. If specified, + updates the `domainName` that is matched for returning this AP's IP Address. + Optionally, resources used for the DNS forward function can be freed. + */ + void disableForwarder(const String &domainName = emptyString, bool freeResources = false); + bool isForwarding() { return _forwarder && _dns.isSet(); } + void setDNS(const IPAddress& dns) { _dns = dns; } + IPAddress getDNS() { return _dns; } + bool isDNSSet() { return _dns.isSet(); } + void processNextRequest(); void setErrorReplyCode(const DNSReplyCode &replyCode); void setTTL(const uint32_t &ttl); + uint32_t getTTL(); + String getDomainName() { return _domainName; } // Returns true if successful, false if there are no sockets available bool start(const uint16_t &port, const String &domainName, - const IPAddress &resolvedIP); + const IPAddress &resolvedIP, + const IPAddress &dns = (uint32_t)0); // stops the DNS server void stop(); private: WiFiUDP _udp; - uint16_t _port; String _domainName; - unsigned char _resolvedIP[4]; + IPAddress _dns; + std::unique_ptr _que; uint32_t _ttl; +#ifdef DEBUG_DNSSERVER + // There are 2 possiblities for OverFlow: + // 1) we have more than kDNSSQueSize request already outstanding. + // 2) we have request that never received a reply. + uint32_t _que_ov; + uint32_t _que_drop; +#endif DNSReplyCode _errorReplyCode; + bool _forwarder; + unsigned char _resolvedIP[4]; + uint16_t _port; void downcaseAndRemoveWwwPrefix(String &domainName); void replyWithIP(DNSHeader *dnsHeader, @@ -81,7 +136,9 @@ class DNSServer size_t queryLength); void replyWithError(DNSHeader *dnsHeader, DNSReplyCode rcode); - void respondToRequest(uint8_t *buffer, size_t length); + bool respondToRequest(uint8_t *buffer, size_t length); + void forwardRequest(uint8_t *buffer, size_t length); + void forwardReply(uint8_t *buffer, size_t length); void writeNBOShort(uint16_t value); }; #endif diff --git a/libraries/EEPROM/EEPROM.cpp b/libraries/EEPROM/EEPROM.cpp index 9df8bbcf73..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)) { } 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 cc2fb84e3c..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() { 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 aaa4af2b4d..0e174c2790 100644 --- a/libraries/ESP8266HTTPClient/examples/BasicHttpsClient/BasicHttpsClient.ino +++ b/libraries/ESP8266HTTPClient/examples/BasicHttpsClient/BasicHttpsClient.ino @@ -9,12 +9,15 @@ #include #include - #include - #include -// Fingerprint for demo URL, expires on June 2, 2021, needs to be updated well before this date -const uint8_t fingerprint[20] = {0x40, 0xaf, 0x00, 0x6b, 0xec, 0x90, 0x22, 0x41, 0x8e, 0xa3, 0xad, 0xfa, 0x1a, 0xe8, 0x25, 0x41, 0x1d, 0x1a, 0x54, 0xb3}; + +#include "certs.h" + +#ifndef STASSID +#define STASSID "your-ssid" +#define STAPSK "your-password" +#endif ESP8266WiFiMulti WiFiMulti; @@ -27,30 +30,30 @@ void setup() { Serial.println(); Serial.println(); - for (uint8_t t = 4; t > 0; t--) { - Serial.printf("[SETUP] WAIT %d...\n", t); - Serial.flush(); - delay(1000); - } - WiFi.mode(WIFI_STA); - WiFiMulti.addAP("SSID", "PASSWORD"); + WiFiMulti.addAP(STASSID, STAPSK); + Serial.println("setup() done connecting to ssid '" STASSID "'"); } void loop() { // wait for WiFi connection if ((WiFiMulti.run() == WL_CONNECTED)) { - std::unique_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 happy to ignore the SSL certificate, then use the following line instead: + // Or, if you are *absolutely* sure it is ok to ignore the SSL certificate: // client->setInsecure(); HTTPClient https; Serial.print("[HTTPS] begin...\n"); - if (https.begin(*client, "https://jigsaw.w3.org/HTTP/connection.html")) { // HTTPS + if (https.begin(*client, jigsaw_host, jigsaw_port)) { // HTTPS Serial.print("[HTTPS] GET...\n"); // start connection and send HTTP header @@ -64,6 +67,7 @@ void loop() { // file found at server if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) { String payload = https.getString(); + // String payload = https.getString(1024); // optionally pre-reserve string to avoid reallocations in chunk mode Serial.println(payload); } } else { diff --git a/libraries/ESP8266HTTPClient/examples/BasicHttpsClient/certUpdate b/libraries/ESP8266HTTPClient/examples/BasicHttpsClient/certUpdate new file mode 100755 index 0000000000..71b036dc85 --- /dev/null +++ b/libraries/ESP8266HTTPClient/examples/BasicHttpsClient/certUpdate @@ -0,0 +1,2 @@ +cd ${0%/*} 2>/dev/null +python3 ../../../../tools/cert.py -s jigsaw.w3.org -n jigsaw > certs.h diff --git a/libraries/ESP8266HTTPClient/examples/BasicHttpsClient/certs.h b/libraries/ESP8266HTTPClient/examples/BasicHttpsClient/certs.h new file mode 100644 index 0000000000..76451ff76a --- /dev/null +++ b/libraries/ESP8266HTTPClient/examples/BasicHttpsClient/certs.h @@ -0,0 +1,58 @@ + +// this file is autogenerated - any modification will be overwritten +// unused symbols will not be linked in the final binary +// generated on 2024-07-30 22:46:21 +// by ['../../../../tools/cert.py', '-s', 'jigsaw.w3.org', '-n', 'jigsaw'] + +#pragma once + +//////////////////////////////////////////////////////////// +// certificate chain for jigsaw.w3.org:443 + +const char* jigsaw_host = "jigsaw.w3.org"; +const uint16_t jigsaw_port = 443; + +// CN: w3.org => name: w3_org +// not valid before: 2024-01-26 00:00:00+00:00 +// not valid after: 2024-12-31 23:59:59+00:00 +const char fingerprint_w3_org [] PROGMEM = "07:f2:bd:4c:d0:ce:58:da:13:03:9d:a9:0d:df:e9:5b:60:5f:7f:a5"; +const char pubkey_w3_org [] PROGMEM = R"PUBKEY( +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPwx1EbG8lugJ74owfhQChFkoxc9R +EZ9D7g5JfO7TUZH+nxWxCT7njoKgD9yvJZYTy/oijTdhB7o7knUsBLRj8A== +-----END PUBLIC KEY----- +)PUBKEY"; + +// http://cacerts.digicert.com/CloudflareIncECCCA-3.crt +// CN: Cloudflare Inc ECC CA-3 => name: Cloudflare_Inc_ECC_CA_3 +// not valid before: 2020-01-27 12:48:08+00:00 +// not valid after: 2024-12-31 23:59:59+00:00 +const char cert_Cloudflare_Inc_ECC_CA_3 [] PROGMEM = R"CERT( +-----BEGIN CERTIFICATE----- +MIIDzTCCArWgAwIBAgIQCjeHZF5ftIwiTv0b7RQMPDANBgkqhkiG9w0BAQsFADBa +MQswCQYDVQQGEwJJRTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJl +clRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTIw +MDEyNzEyNDgwOFoXDTI0MTIzMTIzNTk1OVowSjELMAkGA1UEBhMCVVMxGTAXBgNV +BAoTEENsb3VkZmxhcmUsIEluYy4xIDAeBgNVBAMTF0Nsb3VkZmxhcmUgSW5jIEVD +QyBDQS0zMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEua1NZpkUC0bsH4HRKlAe +nQMVLzQSfS2WuIg4m4Vfj7+7Te9hRsTJc9QkT+DuHM5ss1FxL2ruTAUJd9NyYqSb +16OCAWgwggFkMB0GA1UdDgQWBBSlzjfq67B1DpRniLRF+tkkEIeWHzAfBgNVHSME +GDAWgBTlnVkwgkdYzKz6CFQ2hns6tQRN8DAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0l +BBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBIGA1UdEwEB/wQIMAYBAf8CAQAwNAYI +KwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5j +b20wOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL09t +bmlyb290MjAyNS5jcmwwbQYDVR0gBGYwZDA3BglghkgBhv1sAQEwKjAoBggrBgEF +BQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzALBglghkgBhv1sAQIw +CAYGZ4EMAQIBMAgGBmeBDAECAjAIBgZngQwBAgMwDQYJKoZIhvcNAQELBQADggEB +AAUkHd0bsCrrmNaF4zlNXmtXnYJX/OvoMaJXkGUFvhZEOFp3ArnPEELG4ZKk40Un ++ABHLGioVplTVI+tnkDB0A+21w0LOEhsUCxJkAZbZB2LzEgwLt4I4ptJIsCSDBFe +lpKU1fwg3FZs5ZKTv3ocwDfjhUkV+ivhdDkYD7fa86JXWGBPzI6UAPxGezQxPk1H +goE6y/SJXQ7vTQ1unBuCJN0yJV0ReFEQPaA1IwQvZW+cwdFD19Ae8zFnWSfda9J1 +CZMRJCQUzym+5iPDuI9yP+kHyCREU3qzuWFloUwOxkgAyXVjBYdwRVKD05WdRerw +6DEdfgkfCv4+3ao8XnTSrLE= +-----END CERTIFICATE----- +)CERT"; + +// end of certificate chain for jigsaw.w3.org:443 +//////////////////////////////////////////////////////////// + diff --git a/libraries/ESP8266HTTPClient/examples/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/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 7e1593f52b..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 @@ -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 8098986650..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 @@ -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 c933b5c21f..480699e453 100644 --- a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp +++ b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp @@ -22,13 +22,24 @@ * */ #include +#include #include "ESP8266HTTPClient.h" #include #include #include -static int StreamReportToHttpClientReport (Stream::Report streamSendError) +// 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, ""); + +static const char defaultUserAgentPstr[] PROGMEM = "ESP8266HTTPClient"; +const String HTTPClient::defaultUserAgent = defaultUserAgentPstr; + +int HTTPClient::StreamReportToHttpClientReport (Stream::Report streamSendError) { switch (streamSendError) { @@ -41,27 +52,6 @@ static int StreamReportToHttpClientReport (Stream::Report streamSendError) return 0; // never reached, keep gcc quiet } -/** - * constructor - */ -HTTPClient::HTTPClient() - : _client(nullptr), _userAgent(F("ESP8266HTTPClient")) -{ -} - -/** - * destructor - */ -HTTPClient::~HTTPClient() -{ - if(_client) { - _client->stop(); - } - if(_currentHeaders) { - delete[] _currentHeaders; - } -} - void HTTPClient::clear() { _returnCode = 0; @@ -80,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) { @@ -97,6 +85,8 @@ bool HTTPClient::begin(WiFiClient &client, const String& url) { } _port = (protocol == "https" ? 443 : 80); + _client = client.clone(); + return beginInternal(url, protocol.c_str()); } @@ -112,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; @@ -165,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) { @@ -174,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) { @@ -276,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 @@ -354,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 @@ -445,7 +468,7 @@ int HTTPClient::sendRequest(const char * type, const uint8_t * payload, size_t s } // transfer all of it, with send-timeout - if (size && StreamConstPtr(payload, size).sendAll(_client) != size) + if (size && StreamConstPtr(payload, size).sendAll(_client.get()) != size) return returnError(HTTPC_ERROR_SEND_PAYLOAD_FAILED); // handle Server Response (Header) @@ -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; @@ -546,10 +569,11 @@ int HTTPClient::sendRequest(const char * type, Stream * stream, size_t size) } // transfer all of it, with timeout - size_t transferred = stream->sendSize(_client, size); + size_t transferred = stream->sendSize(_client.get(), size); if (transferred != size) { - DEBUG_HTTPCLIENT("[HTTP-Client][sendRequest] short write, asked for %d but got %d failed.\n", size, transferred); + 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); } @@ -596,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) { - // len < 0: transfer all of it, with timeout - // len >= 0: max:len, with timeout - ret = _client->sendSize(stream, 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(stream, 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); - } - - 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; @@ -704,12 +639,12 @@ const String& HTTPClient::getString(void) _payload.reset(new StreamString()); - if(_size > 0) { - // try to reserve needed memory - if(!_payload->reserve((_size + 1))) { - DEBUG_HTTPCLIENT("[HTTP-Client][getString] not enough memory to reserve a string! need: %d\n", (_size + 1)); - return *_payload; - } + if (_size > 0 && _size > reserve) + reserve = _size; + + if (reserve > 0 && !_payload->reserve(reserve)) { + DEBUG_HTTPCLIENT("[HTTP-Client][getString] not enough memory to reserve a string! need: %d\n", reserve); + return *_payload; } writeToStream(_payload.get()); @@ -791,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() @@ -914,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"); @@ -936,7 +870,7 @@ bool HTTPClient::sendHeader(const char * type) DEBUG_HTTPCLIENT("[HTTP-Client] sending request header\n-----\n%s-----\n", header.c_str()); // transfer all of it, with timeout - return StreamConstPtr(header).sendAll(_client) == header.length(); + return StreamConstPtr(header).sendAll(_client.get()) == header.length(); } /** @@ -1047,7 +981,7 @@ int HTTPClient::handleHeaderResponse() if((millis() - lastDataTime) > _tcpTimeout) { return HTTPC_ERROR_READ_TIMEOUT; } - delay(0); + esp_yield(); } } diff --git a/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h b/libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.h index 3605a807f3..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__ ) @@ -151,13 +152,12 @@ typedef std::unique_ptr TransportTraitsPtr; 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); @@ -176,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 @@ -187,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); @@ -201,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 @@ -213,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: @@ -231,8 +240,17 @@ class HTTPClient bool sendHeader(const char * type); int handleHeaderResponse(); int writeToStreamDataBlock(Stream * stream, int len); + static int StreamReportToHttpClientReport (Stream::Report streamSendError); - WiFiClient* _client; + // 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 + + std::unique_ptr _client; /// request handling String _host; @@ -244,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; @@ -261,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 3357f2ac63..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()) { @@ -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/examples/LLMNR_Web_Server/LLMNR_Web_Server.ino b/libraries/ESP8266LLMNR/examples/LLMNR_Web_Server/LLMNR_Web_Server.ino index 6b29bb5141..f36da85aeb 100644 --- a/libraries/ESP8266LLMNR/examples/LLMNR_Web_Server/LLMNR_Web_Server.ino +++ b/libraries/ESP8266LLMNR/examples/LLMNR_Web_Server/LLMNR_Web_Server.ino @@ -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 9472c86140..eaab1369d5 160000 --- a/libraries/ESP8266SdFat +++ b/libraries/ESP8266SdFat @@ -1 +1 @@ -Subproject commit 9472c8614033efdaa6064906e7065963581746af +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 39f69fe3ff..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 @@ -174,18 +168,12 @@ void handleStatus() { 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,37 +394,25 @@ 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); } } @@ -484,15 +424,11 @@ void handleFileUpload() { 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) { @@ -562,9 +495,7 @@ void setup(void) { 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 b0eb5f847b..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; } @@ -139,11 +135,9 @@ bool handleFileRead(String path) { 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 7fdee1667a..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"); @@ -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 185914f305..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")); @@ -211,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 2cba08fe9c..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,7 +119,7 @@ void setup(void) { }); server.onNotFound(handleNotFound); - //ask server to track these headers + // 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 8c5cdf401e..dbcb251135 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h @@ -68,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(); @@ -95,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; } @@ -223,18 +230,61 @@ void ESP8266WebServerTemplate::requestAuthentication(HTTPAuthMethod } template -void ESP8266WebServerTemplate::on(const Uri &uri, ESP8266WebServerTemplate::THandlerFunction handler) { - on(uri, HTTP_ANY, handler); +RequestHandler& ESP8266WebServerTemplate::on(const Uri &uri, ESP8266WebServerTemplate::THandlerFunction handler) { + return on(uri, HTTP_ANY, handler); +} + +template +RequestHandler& ESP8266WebServerTemplate::on(const Uri &uri, HTTPMethod method, ESP8266WebServerTemplate::THandlerFunction fn) { + return on(uri, method, fn, _fileUploadHandler); +} + +template +RequestHandler& ESP8266WebServerTemplate::on(const Uri &uri, HTTPMethod method, ESP8266WebServerTemplate::THandlerFunction fn, ESP8266WebServerTemplate::THandlerFunction ufn) { + RequestHandler *handler = new FunctionRequestHandler(fn, ufn, uri, method); + _addRequestHandler(handler); + return *handler; +} + +template +bool ESP8266WebServerTemplate::removeRoute(const char *uri) { + return removeRoute(String(uri), HTTP_ANY); } template -void ESP8266WebServerTemplate::on(const Uri &uri, HTTPMethod method, ESP8266WebServerTemplate::THandlerFunction fn) { - on(uri, method, fn, _fileUploadHandler); +bool ESP8266WebServerTemplate::removeRoute(const char *uri, HTTPMethod method) { + return removeRoute(String(uri), method); } template -void ESP8266WebServerTemplate::on(const Uri &uri, HTTPMethod method, ESP8266WebServerTemplate::THandlerFunction fn, ESP8266WebServerTemplate::THandlerFunction ufn) { - _addRequestHandler(new FunctionRequestHandler(fn, ufn, uri, method)); +bool ESP8266WebServerTemplate::removeRoute(const String &uri) { + return removeRoute(uri, HTTP_ANY); +} + +template +bool ESP8266WebServerTemplate::removeRoute(const String &uri, HTTPMethod method) { + bool anyHandlerRemoved = false; + RequestHandlerType *handler = _firstHandler; + RequestHandlerType *previousHandler = nullptr; + + while (handler) { + if (handler->canHandle(method, uri)) { + if (_removeRequestHandler(handler)) { + anyHandlerRemoved = true; + // Move to the next handler + if (previousHandler) { + handler = previousHandler->next(); + } else { + handler = _firstHandler; + } + continue; + } + } + previousHandler = handler; + handler = handler->next(); + } + + return anyHandlerRemoved; } template @@ -242,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) { @@ -254,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; @@ -264,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(); } @@ -288,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) { @@ -335,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; @@ -436,6 +548,7 @@ void ESP8266WebServerTemplate::_prepareHeader(String& response, int sendHeader(String(F("Keep-Alive")), String(F("timeout=")) + HTTP_MAX_CLOSE_WAIT); } + response += _responseHeaders; response += "\r\n"; _responseHeaders = ""; @@ -499,7 +612,7 @@ void ESP8266WebServerTemplate::sendContent(Stream* content, ssize_t ssize_t sent = content->sendSize(&_currentClient, content_length); if (sent != content_length) { - DBGWS("HTTPServer: error: short send after timeout (%d<%d)\n", sent, content_length); + DBGWS("HTTPServer: error: short send after timeout (%zu < %zu)\n", sent, content_length); } if(_chunked) { _currentClient.printf_P(PSTR("\r\n")); @@ -552,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; @@ -584,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; @@ -696,9 +801,11 @@ void ESP8266WebServerTemplate::_handleRequest() { _finalizeResponse(); } _currentUri = ""; + delete[] _currentArgs; + _currentArgs = nullptr; + _currentArgCount = 0; } - template void ESP8266WebServerTemplate::_finalizeResponse() { if (_chunked) { diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.h b/libraries/ESP8266WebServer/src/ESP8266WebServer.h index eeb98c15a7..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; } @@ -167,7 +177,9 @@ class ESP8266WebServerTemplate void send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength); void send(int code, const char* content_type, Stream* stream, size_t content_length = 0); - void send(int code, const char* content_type, Stream& stream, size_t content_length = 0); + void send(int code, const char* content_type, Stream& stream, size_t content_length = 0) { + send(code, content_type, &stream, content_length); + } void setContentLength(const size_t contentLength); void sendHeader(const String& name, const String& value, bool first = false); @@ -201,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. @@ -271,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); @@ -314,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; @@ -343,4 +381,4 @@ class ESP8266WebServerTemplate using ESP8266WebServer = esp8266webserver::ESP8266WebServerTemplate; using RequestHandler = esp8266webserver::RequestHandler; -#endif //ESP8266WEBSERVER_H \ No newline at end of file +#endif //ESP8266WEBSERVER_H diff --git a/libraries/ESP8266WebServer/src/Parsing-impl.h b/libraries/ESP8266WebServer/src/Parsing-impl.h index 8e4a6d1ae9..672682706e 100644 --- a/libraries/ESP8266WebServer/src/Parsing-impl.h +++ b/libraries/ESP8266WebServer/src/Parsing-impl.h @@ -106,7 +106,7 @@ typename ESP8266WebServerTemplate::ClientFuture ESP8266WebServerTemp //attach handler RequestHandlerType* handler; for (handler = _firstHandler; handler; handler = handler->next()) { - if (handler->canHandle(_currentMethod, _currentUri)) + if (handler->canHandle(*this, _currentMethod, _currentUri)) break; } _currentHandler = handler; @@ -182,7 +182,7 @@ typename ESP8266WebServerTemplate::ClientFuture ESP8266WebServerTemp if (!isForm) { if (contentLength) { // add key=value: plain={body} (post json or other data) - RequestArgument& arg = _currentArgs[_currentArgCount++]; + RequestArgument& arg = _currentArgs[_currentArgCount]; arg.key = F("plain"); arg.value = plainBuf; _currentArgsHavePlain = 1; @@ -324,7 +324,7 @@ int ESP8266WebServerTemplate::_parseArgumentsPrivate(const String& d template void ESP8266WebServerTemplate::_uploadWriteByte(uint8_t b){ if (_currentUpload->currentSize == HTTP_UPLOAD_BUFLEN){ - if(_currentHandler && _currentHandler->canUpload(_currentUri)) + if(_currentHandler && _currentHandler->canUpload(*this, _currentUri)) _currentHandler->upload(*this, _currentUri, *_currentUpload); _currentUpload->totalSize += _currentUpload->currentSize; _currentUpload->currentSize = 0; @@ -358,9 +358,8 @@ bool ESP8266WebServerTemplate::_parseForm(ClientType& client, const client.readStringUntil('\n'); //start reading the form if (line == ("--"+boundary)){ - if(_postArgs) delete[] _postArgs; - _postArgs = new RequestArgument[WEBSERVER_MAX_POST_ARGS]; - _postArgsLen = 0; + std::unique_ptr postArgs(new RequestArgument[WEBSERVER_MAX_POST_ARGS]); + int postArgsLen = 0; while(1){ String argName; String argValue; @@ -408,7 +407,7 @@ bool ESP8266WebServerTemplate::_parseForm(ClientType& client, const } DBGWS("PostArg Value: %s\n\n", argValue.c_str()); - RequestArgument& arg = _postArgs[_postArgsLen++]; + RequestArgument& arg = postArgs[postArgsLen++]; arg.key = argName; arg.value = argValue; @@ -426,7 +425,7 @@ bool ESP8266WebServerTemplate::_parseForm(ClientType& client, const _currentUpload->currentSize = 0; _currentUpload->contentLength = len; DBGWS("Start File: %s Type: %s\n", _currentUpload->filename.c_str(), _currentUpload->type.c_str()); - if(_currentHandler && _currentHandler->canUpload(_currentUri)) + if(_currentHandler && _currentHandler->canUpload(*this, _currentUri)) _currentHandler->upload(*this, _currentUri, *_currentUpload); _currentUpload->status = UPLOAD_FILE_WRITE; @@ -464,11 +463,11 @@ bool ESP8266WebServerTemplate::_parseForm(ClientType& client, const } } // Found the boundary string, finish processing this file upload - if (_currentHandler && _currentHandler->canUpload(_currentUri)) + if (_currentHandler && _currentHandler->canUpload(*this, _currentUri)) _currentHandler->upload(*this, _currentUri, *_currentUpload); _currentUpload->totalSize += _currentUpload->currentSize; _currentUpload->status = UPLOAD_FILE_END; - if (_currentHandler && _currentHandler->canUpload(_currentUri)) + if (_currentHandler && _currentHandler->canUpload(*this, _currentUri)) _currentHandler->upload(*this, _currentUri, *_currentUpload); DBGWS("End File: %s Type: %s Size: %d\n", _currentUpload->filename.c_str(), @@ -488,25 +487,20 @@ bool ESP8266WebServerTemplate::_parseForm(ClientType& client, const } int iarg; - int totalArgs = ((WEBSERVER_MAX_POST_ARGS - _postArgsLen) < _currentArgCount)?(WEBSERVER_MAX_POST_ARGS - _postArgsLen):_currentArgCount; + int totalArgs = ((WEBSERVER_MAX_POST_ARGS - postArgsLen) < _currentArgCount)?(WEBSERVER_MAX_POST_ARGS - postArgsLen):_currentArgCount; for (iarg = 0; iarg < totalArgs; iarg++){ - RequestArgument& arg = _postArgs[_postArgsLen++]; + RequestArgument& arg = postArgs[postArgsLen++]; arg.key = _currentArgs[iarg].key; arg.value = _currentArgs[iarg].value; } - if (_currentArgs) delete[] _currentArgs; - _currentArgs = new RequestArgument[_postArgsLen]; - for (iarg = 0; iarg < _postArgsLen; iarg++){ + delete[] _currentArgs; + _currentArgs = new RequestArgument[postArgsLen]; + for (iarg = 0; iarg < postArgsLen; iarg++){ RequestArgument& arg = _currentArgs[iarg]; - arg.key = _postArgs[iarg].key; - arg.value = _postArgs[iarg].value; + arg.key = postArgs[iarg].key; + arg.value = postArgs[iarg].value; } _currentArgCount = iarg; - if (_postArgs) { - delete[] _postArgs; - _postArgs = nullptr; - _postArgsLen = 0; - } return true; } DBGWS("Error: line: %s\n", line.c_str()); @@ -548,7 +542,7 @@ String ESP8266WebServerTemplate::urlDecode(const String& text) template bool ESP8266WebServerTemplate::_parseFormUploadAborted(){ _currentUpload->status = UPLOAD_FILE_ABORTED; - if(_currentHandler && _currentHandler->canUpload(_currentUri)) + if(_currentHandler && _currentHandler->canUpload(*this, _currentUri)) _currentHandler->upload(*this, _currentUri, *_currentUpload); return false; } diff --git a/libraries/ESP8266WebServer/src/detail/RequestHandler.h b/libraries/ESP8266WebServer/src/detail/RequestHandler.h index 4195f0ff3f..c373c58f1b 100644 --- a/libraries/ESP8266WebServer/src/detail/RequestHandler.h +++ b/libraries/ESP8266WebServer/src/detail/RequestHandler.h @@ -12,13 +12,60 @@ class RequestHandler { using WebServerType = ESP8266WebServerTemplate; public: virtual ~RequestHandler() { } - virtual bool canHandle(HTTPMethod method, const String& uri) { (void) method; (void) uri; return false; } - virtual bool canUpload(const String& uri) { (void) uri; return false; } - virtual bool handle(WebServerType& server, HTTPMethod requestMethod, const String& requestUri) { (void) server; (void) requestMethod; (void) requestUri; return false; } - virtual void upload(WebServerType& server, const String& requestUri, HTTPUpload& upload) { (void) server; (void) requestUri; (void) upload; } - RequestHandler* next() { return _next; } - void next(RequestHandler* r) { _next = r; } + /* + note: old handler API for backward compatibility + */ + + virtual bool canHandle(HTTPMethod method, const String& uri) { + (void) method; + (void) uri; + return false; + } + virtual bool canUpload(const String& uri) { + (void) uri; + return false; + } + + /* + note: new handler API with support for filters etc. + */ + + virtual bool canHandle(WebServerType& server, HTTPMethod method, const String& uri) { + (void) server; + (void) method; + (void) uri; + return false; + } + virtual bool canUpload(WebServerType& server, const String& uri) { + (void) server; + (void) uri; + return false; + } + virtual bool handle(WebServerType& server, HTTPMethod requestMethod, const String& requestUri) { + (void) server; + (void) requestMethod; + (void) requestUri; + return false; + } + virtual void upload(WebServerType& server, const String& requestUri, HTTPUpload& upload) { + (void) server; + (void) requestUri; + (void) upload; + } + + RequestHandler* next() { + return _next; + } + + void next(RequestHandler* r) { + _next = r; + } + + virtual RequestHandler& setFilter(std::function&)> filter) { + (void)filter; + return *this; + } private: RequestHandler* _next = nullptr; diff --git a/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h b/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h index 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 708d906eb1..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); @@ -79,14 +76,11 @@ int fetchMaxFragmentLength() { BearSSL::WiFiClientSecure client; client.setInsecure(); - bool mfln = client.probeMaxFragmentLength("tls.mbed.org", 443, 512); - Serial.printf("\nConnecting to https://tls.mbed.org\n"); + bool mfln = client.probeMaxFragmentLength("api.my-ip.io", 443, 512); + Serial.printf("\nConnecting to https://api.my-ip.io\n"); Serial.printf("MFLN supported: %s\n", mfln ? "yes" : "no"); - if (mfln) { - client.setBufferSizes(512, 512); - } - client.connect("tls.mbed.org", 443); - if (client.connected()) { + if (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 30bc07034e..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 developer 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 fa03c7fa38..cf227403f3 100644 --- a/libraries/ESP8266WiFi/examples/BearSSL_Sessions/BearSSL_Sessions.ino +++ b/libraries/ESP8266WiFi/examples/BearSSL_Sessions/BearSSL_Sessions.ino @@ -1,5 +1,12 @@ // 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 @@ -9,13 +16,13 @@ #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 * path = "/"; +const char *path = "/"; void setup() { Serial.begin(115200); @@ -53,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; } @@ -71,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) { @@ -98,12 +100,14 @@ void fetchURL(BearSSL::WiFiClientSecure *client, const char *host, const uint16_ void loop() { uint32_t start, finish; BearSSL::WiFiClientSecure client; - BearSSL::X509List cert(cert_DigiCert_High_Assurance_EV_Root_CA); + BearSSL::X509List cert(certForum); + const char *host = "esp8266.com"; + const int port = 443; Serial.printf("Connecting without sessions..."); start = millis(); client.setTrustAnchors(&cert); - fetchURL(&client, github_host, github_port, path); + fetchURL(&client, host, port, path); finish = millis(); Serial.printf("Total time: %dms\n", finish - start); @@ -112,24 +116,23 @@ void loop() { Serial.printf("Connecting with an uninitialized session..."); start = millis(); client.setTrustAnchors(&cert); - fetchURL(&client, github_host, github_port, path); + fetchURL(&client, host, port, path); finish = millis(); Serial.printf("Total time: %dms\n", finish - start); Serial.printf("Connecting with the just initialized session..."); start = millis(); client.setTrustAnchors(&cert); - fetchURL(&client, github_host, github_port, path); + fetchURL(&client, host, port, path); finish = millis(); Serial.printf("Total time: %dms\n", finish - start); Serial.printf("Connecting again with the initialized session..."); start = millis(); client.setTrustAnchors(&cert); - fetchURL(&client, github_host, github_port, path); + fetchURL(&client, host, port, path); 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/certUpdate b/libraries/ESP8266WiFi/examples/BearSSL_Sessions/certUpdate deleted file mode 100755 index ba08b87c32..0000000000 --- a/libraries/ESP8266WiFi/examples/BearSSL_Sessions/certUpdate +++ /dev/null @@ -1,2 +0,0 @@ -cd ${0%/*} 2>/dev/null -python3 ../../../../tools/cert.py -s api.github.com -n github > certs.h diff --git a/libraries/ESP8266WiFi/examples/BearSSL_Sessions/certs.h b/libraries/ESP8266WiFi/examples/BearSSL_Sessions/certs.h index 04765d956c..2d21efdf3a 100644 --- a/libraries/ESP8266WiFi/examples/BearSSL_Sessions/certs.h +++ b/libraries/ESP8266WiFi/examples/BearSSL_Sessions/certs.h @@ -1,90 +1,34 @@ - -// this file is autogenerated - any modification will be overwritten -// unused symbols will not be linked in the final binary -// generated on 2021-07-26 22:04:48 -// 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: 2021-03-25 00:00:00 -// not valid after: 2022-03-30 23:59:59 -const char fingerprint___github_com [] PROGMEM = "96:84:07:df:0b:1c:f6:58:14:df:d7:33:35:57:51:9b:15:4d:8c:e7"; -const char pubkey___github_com [] PROGMEM = R"PUBKEY( ------BEGIN PUBLIC KEY----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAElL9/+0TidTIALPfU/tiS6pI8zAIk -rU4pohUldVc0bb6O3FARl3cnqIDK9SoF65z3xiR6XsnFS8F0Oy/chXR/kQ== ------END PUBLIC KEY----- -)PUBKEY"; - -// http://cacerts.digicert.com/DigiCertHighAssuranceTLSHybridECCSHA2562020CA1.crt -// CN: DigiCert High Assurance TLS Hybrid ECC SHA256 2020 CA1 => name: DigiCert_High_Assurance_TLS_Hybrid_ECC_SHA256_2020_CA1 -// not valid before: 2020-12-17 00:00:00 -// not valid after: 2030-12-16 23:59:59 -const char cert_DigiCert_High_Assurance_TLS_Hybrid_ECC_SHA256_2020_CA1 [] PROGMEM = R"CERT( +const char certForum [] PROGMEM = R"EOF( -----BEGIN CERTIFICATE----- -MIIEGzCCAwOgAwIBAgIQBmcDW7sU/WOvwNaoU07+FjANBgkqhkiG9w0BAQsFADBs -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBFViBSb290IENBMB4XDTIwMTIxNzAwMDAwMFoXDTMwMTIxNjIzNTk1OVowZzEL -MAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMT8wPQYDVQQDEzZE -aWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBUTFMgSHlicmlkIEVDQyBTSEEyNTYgMjAy -MCBDQTEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARnvW/xPOudvtC252wTq9ef -6fbdFeWPkOscfpRTkciuHj7UcumQSH3lzkPEIx0KpesWa8epsks7QwkZ4fU/Tkf9 -o4IBhzCCAYMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUUGGmoNI1xBEq -II0fD6xC8M0pz0swHwYDVR0jBBgwFoAUsT7DaQP4v0cB1JgmGggC72NkK8MwDgYD -VR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjB/Bggr -BgEFBQcBAQRzMHEwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNv -bTBJBggrBgEFBQcwAoY9aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lD -ZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNydDBLBgNVHR8ERDBCMECgPqA8hjpo -dHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZS -b290Q0EuY3JsMDAGA1UdIAQpMCcwCAYGZ4EMAQICMAgGBmeBDAECAzAHBgVngQwB -ATAIBgZngQwBAgEwDQYJKoZIhvcNAQELBQADggEBAHMQH8hhiBfNbxwEwxbbTAnu -jPyUh/oi0JrfZI3u9JuiLqca720D6foS/AB5+4EIxpm7CMG4MdN/l7oAiDipaCPv -mOmpYUpnT7A63Cr0q4g84rI1ZmdqA40lVUUf6qC6E34tC73qDQF8TJSrfscWFdCl -RXR9J4QGrkZ2VNMSDzlDRzWCaA95MfO8x01l+ZdopdE8FvM78gGd4zxeWb8v991+ -mBxTDepqKuy/jF5Rm6Bhfxr33ADRs60s1t16dtZ3pOYLALBTPD5KhZ6a+/dk5dnh -6c4PaeZQYBUAh+GuxfaBlU4qQ8EtjBMCQHreMIwXHYHW5FRYGjgR4NMuaIw2jD0= +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----- -)CERT"; - -// http://cacerts.digicert.com/DigiCertHighAssuranceEVRootCA.crt -// CN: DigiCert High Assurance EV Root CA => name: DigiCert_High_Assurance_EV_Root_CA -// not valid before: 2006-11-10 00:00:00 -// not valid after: 2031-11-10 00:00:00 -const char cert_DigiCert_High_Assurance_EV_Root_CA [] PROGMEM = R"CERT( ------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----- -)CERT"; - - -// end of certificate chain for api.github.com:443 -//////////////////////////////////////////////////////////// +)EOF"; diff --git a/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino b/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino index b91570da11..2f6c08a51a 100644 --- a/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino +++ b/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino @@ -10,15 +10,19 @@ #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 * path = "/"; +const char *path = "/"; // Set time via NTP, as required for x.509 validation void setClock() { @@ -40,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; } @@ -60,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) { @@ -91,7 +90,7 @@ If there are no CAs or insecure options specified, BearSSL will not connect. Expect the following call to fail as none have been configured. )EOF"); BearSSL::WiFiClientSecure client; - fetchURL(&client, gitlab_host, gitlab_port, path); + fetchURL(&client, SSL_host, SSL_port, path); } void fetchInsecure() { @@ -102,7 +101,7 @@ which is subject to man-in-the-middle (MITM) attacks. )EOF"); BearSSL::WiFiClientSecure client; client.setInsecure(); - fetchURL(&client, gitlab_host, gitlab_port, path); + fetchURL(&client, SSL_host, SSL_port, path); } void fetchFingerprint() { @@ -115,8 +114,8 @@ fingerprints will change if anything changes in the certificate chain the root authorities, etc.). )EOF"); BearSSL::WiFiClientSecure client; - client.setFingerprint(fingerprint_gitlab_com); - fetchURL(&client, gitlab_host, gitlab_port, path); + client.setFingerprint(FINGERPRINT); + fetchURL(&client, SSL_host, SSL_port, path); } void fetchSelfSigned() { @@ -141,9 +140,9 @@ private and not shared. A MITM without the private key would not be able to establish communications. )EOF"); BearSSL::WiFiClientSecure client; - BearSSL::PublicKey key(pubkey_gitlab_com); + BearSSL::PublicKey key(PUBKEY); client.setKnownKey(&key); - fetchURL(&client, gitlab_host, gitlab_port, path); + fetchURL(&client, SSL_host, SSL_port, path); } void fetchCertAuthority() { @@ -157,14 +156,14 @@ BearSSL does verify the notValidBefore/After fields. )EOF"); BearSSL::WiFiClientSecure client; - BearSSL::X509List cert(cert_USERTrust_RSA_Certification_Authority); + BearSSL::X509List cert(CERT); client.setTrustAnchors(&cert); Serial.printf("Try validating without setting the time (should fail)\n"); - fetchURL(&client, gitlab_host, gitlab_port, path); + fetchURL(&client, SSL_host, SSL_port, path); Serial.printf("Try again after setting NTP time (should pass)\n"); setClock(); - fetchURL(&client, gitlab_host, gitlab_port, path); + fetchURL(&client, SSL_host, SSL_port, path); } void fetchFaster() { @@ -175,20 +174,23 @@ you won't want to do this. If you need to maximize battery life, these may make sense )EOF"); BearSSL::WiFiClientSecure client; + Serial.printf("Insecure, all ciphers:\n"); client.setInsecure(); uint32_t now = millis(); - fetchURL(&client, gitlab_host, gitlab_port, path); + fetchURL(&client, SSL_host, SSL_port, path); uint32_t delta = millis() - now; + Serial.printf("Insecure, less secure ciphers:\n"); client.setInsecure(); client.setCiphersLessSecure(); now = millis(); - fetchURL(&client, gitlab_host, gitlab_port, path); + fetchURL(&client, SSL_host, SSL_port, path); uint32_t delta2 = millis() - now; + Serial.printf("Insecure, few ciphers:\n"); std::vector myCustomList = { BR_TLS_RSA_WITH_AES_256_CBC_SHA256, BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, BR_TLS_RSA_WITH_3DES_EDE_CBC_SHA }; client.setInsecure(); client.setCiphers(myCustomList); now = millis(); - fetchURL(&client, gitlab_host, gitlab_port, path); + fetchURL(&client, SSL_host, SSL_port, path); uint32_t delta3 = millis() - now; Serial.printf("Using more secure: %dms\nUsing less secure ciphers: %dms\nUsing custom cipher list: %dms\n", delta, delta2, delta3); } diff --git a/libraries/ESP8266WiFi/examples/BearSSL_Validation/certUpdate b/libraries/ESP8266WiFi/examples/BearSSL_Validation/certUpdate index 2162b78e97..58468ae7a7 100755 --- a/libraries/ESP8266WiFi/examples/BearSSL_Validation/certUpdate +++ b/libraries/ESP8266WiFi/examples/BearSSL_Validation/certUpdate @@ -1,2 +1,2 @@ cd ${0%/*} 2>/dev/null -python3 ../../../../tools/cert.py -s www.gitlab.com -n gitlab > certs.h +python3 ../../../../tools/cert.py -s www.example.com -n SSL > certs.h diff --git a/libraries/ESP8266WiFi/examples/BearSSL_Validation/certs.h b/libraries/ESP8266WiFi/examples/BearSSL_Validation/certs.h index cf6c1ca76d..c6b99041a4 100644 --- a/libraries/ESP8266WiFi/examples/BearSSL_Validation/certs.h +++ b/libraries/ESP8266WiFi/examples/BearSSL_Validation/certs.h @@ -1,115 +1,98 @@ // this file is autogenerated - any modification will be overwritten // unused symbols will not be linked in the final binary -// generated on 2021-07-26 22:04:47 -// by ['../../../../tools/cert.py', '-s', 'www.gitlab.com', '-n', 'gitlab'] +// generated on 2024-07-30 23:28:11 +// by ['../../../../tools/cert.py', '-s', 'www.example.com', '-n', 'SSL'] #pragma once //////////////////////////////////////////////////////////// -// certificate chain for www.gitlab.com:443 +// certificate chain for www.example.com:443 -const char* gitlab_host = "www.gitlab.com"; -const uint16_t gitlab_port = 443; +const char* SSL_host = "www.example.com"; +const uint16_t SSL_port = 443; -// CN: gitlab.com => name: gitlab_com -// not valid before: 2021-04-12 00:00:00 -// not valid after: 2022-05-11 23:59:59 -const char fingerprint_gitlab_com [] PROGMEM = "71:55:5e:29:68:99:43:98:c8:85:35:bd:4c:10:4c:f5:cf:17:09:e6"; -const char pubkey_gitlab_com [] PROGMEM = R"PUBKEY( +// CN: www.example.org => name: www_example_org +// not valid before: 2024-01-30 00:00:00+00:00 +// not valid after: 2025-03-01 23:59:59+00:00 +const char fingerprint_www_example_org [] PROGMEM = "4d:a2:5a:6d:5e:f6:2c:5f:95:c7:bd:0a:73:ea:3c:17:7b:36:99:9d"; +const char pubkey_www_example_org [] PROGMEM = R"PUBKEY( -----BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1eeFy86Xbz3ygyCVprHp -sPP3zyg0yldkIfqwjsXPH0b+KwQ85s3pzI/5+MVrR2/BGY4ed6mTZ6hvNwQJ2B0E -sJrsTb2nuUsXQ0UVO4hvnZ7Dnx8r/bT1cndqa+Mn+bms8/TS4etP72+TLaORBRCz -O4L1Hi8r61+zZLnP3DqqHeHAgl5wKHNYpx7yFFl2I71LuLH/pk2ICDBjaHwCIbRW -u484no9s1c4VROxqMrQQ/wDMl80MiO9YeNQ5rBHfnabh4rFe9eb2Sd0H/DWBj3SO -YBD0kiLI6b5CWYfA76pBSlZg7G3ledvQ+n9FEcS3EOCPKBBZqMDCzEahvHqwJ/r6 -pwIDAQAB +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhoUPuw75yl/Z9eAKMiwz +2aoOBymoLwiteL3CBr/3LSumpyc9U6ZMw0uyJ3cg1sFUSbgI2vlwqWH2skmdaVfa ++20kNHIuR/AEP52xW+K8ZjFZMuapfr/UsNRk9WvKe/9yW16a2D/UBrLzyNyPZlpG +hGaoGBV5pwjOBTz7OYnvbfpOcVJ7t+SgpJyWwGE9pApwTcOOzW6zMmzyx0QJBN2g +Vf0jpSB4soVe2DutF/+Fxbl0jTO5uFdutbxpZdsLPJJVmfRztGQkymdMKJnM3Gc9 +eccWnCvmq6qqNXI39oEqSOg/Thmav55GqjKT/6WyWrSxLx5phJIdsLmNr/IxbJWG +8wIDAQAB -----END PUBLIC KEY----- )PUBKEY"; -// http://crt.sectigo.com/SectigoRSADomainValidationSecureServerCA.crt -// CN: Sectigo RSA Domain Validation Secure Server CA => name: Sectigo_RSA_Domain_Validation_Secure_Server_CA -// not valid before: 2018-11-02 00:00:00 -// not valid after: 2030-12-31 23:59:59 -const char cert_Sectigo_RSA_Domain_Validation_Secure_Server_CA [] PROGMEM = R"CERT( +// http://cacerts.digicert.com/DigiCertGlobalG2TLSRSASHA2562020CA1-1.crt +// CN: DigiCert Global G2 TLS RSA SHA256 2020 CA1 => name: DigiCert_Global_G2_TLS_RSA_SHA256_2020_CA1 +// not valid before: 2021-03-30 00:00:00+00:00 +// not valid after: 2031-03-29 23:59:59+00:00 +const char cert_DigiCert_Global_G2_TLS_RSA_SHA256_2020_CA1 [] PROGMEM = R"CERT( -----BEGIN CERTIFICATE----- -MIIGEzCCA/ugAwIBAgIQfVtRJrR2uhHbdBYLvFMNpzANBgkqhkiG9w0BAQwFADCB -iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl -cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV -BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTgx -MTAyMDAwMDAwWhcNMzAxMjMxMjM1OTU5WjCBjzELMAkGA1UEBhMCR0IxGzAZBgNV -BAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEYMBYGA1UE -ChMPU2VjdGlnbyBMaW1pdGVkMTcwNQYDVQQDEy5TZWN0aWdvIFJTQSBEb21haW4g -VmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEA1nMz1tc8INAA0hdFuNY+B6I/x0HuMjDJsGz99J/LEpgPLT+N -TQEMgg8Xf2Iu6bhIefsWg06t1zIlk7cHv7lQP6lMw0Aq6Tn/2YHKHxYyQdqAJrkj -eocgHuP/IJo8lURvh3UGkEC0MpMWCRAIIz7S3YcPb11RFGoKacVPAXJpz9OTTG0E -oKMbgn6xmrntxZ7FN3ifmgg0+1YuWMQJDgZkW7w33PGfKGioVrCSo1yfu4iYCBsk -Haswha6vsC6eep3BwEIc4gLw6uBK0u+QDrTBQBbwb4VCSmT3pDCg/r8uoydajotY -uK3DGReEY+1vVv2Dy2A0xHS+5p3b4eTlygxfFQIDAQABo4IBbjCCAWowHwYDVR0j -BBgwFoAUU3m/WqorSs9UgOHYm8Cd8rIDZsswHQYDVR0OBBYEFI2MXsRUrYrhd+mb -+ZsF4bgBjWHhMA4GA1UdDwEB/wQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEAMB0G -A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAbBgNVHSAEFDASMAYGBFUdIAAw -CAYGZ4EMAQIBMFAGA1UdHwRJMEcwRaBDoEGGP2h0dHA6Ly9jcmwudXNlcnRydXN0 -LmNvbS9VU0VSVHJ1c3RSU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDB2Bggr -BgEFBQcBAQRqMGgwPwYIKwYBBQUHMAKGM2h0dHA6Ly9jcnQudXNlcnRydXN0LmNv -bS9VU0VSVHJ1c3RSU0FBZGRUcnVzdENBLmNydDAlBggrBgEFBQcwAYYZaHR0cDov -L29jc3AudXNlcnRydXN0LmNvbTANBgkqhkiG9w0BAQwFAAOCAgEAMr9hvQ5Iw0/H -ukdN+Jx4GQHcEx2Ab/zDcLRSmjEzmldS+zGea6TvVKqJjUAXaPgREHzSyrHxVYbH -7rM2kYb2OVG/Rr8PoLq0935JxCo2F57kaDl6r5ROVm+yezu/Coa9zcV3HAO4OLGi -H19+24rcRki2aArPsrW04jTkZ6k4Zgle0rj8nSg6F0AnwnJOKf0hPHzPE/uWLMUx -RP0T7dWbqWlod3zu4f+k+TY4CFM5ooQ0nBnzvg6s1SQ36yOoeNDT5++SR2RiOSLv -xvcRviKFxmZEJCaOEDKNyJOuB56DPi/Z+fVGjmO+wea03KbNIaiGCpXZLoUmGv38 -sbZXQm2V0TP2ORQGgkE49Y9Y3IBbpNV9lXj9p5v//cWoaasm56ekBYdbqbe4oyAL -l6lFhd2zi+WJN44pDfwGF/Y4QA5C5BIG+3vzxhFoYt/jmPQT2BVPi7Fp2RBgvGQq -6jG35LWjOhSbJuMLe/0CjraZwTiXWTb2qHSihrZe68Zk6s+go/lunrotEbaGmAhY -LcmsJWTyXnW0OMGuf1pGg+pRyrbxmRE1a6Vqe8YAsOf4vmSyrcjC8azjUeqkk+B5 -yOGBQMkKW+ESPMFgKuOXwIlCypTPRpgSabuY0MLTDXJLR27lk8QyKGOHQ+SwMj4K -00u/I5sUKUErmgQfky3xxzlIPK1aEn8= +MIIEyDCCA7CgAwIBAgIQDPW9BitWAvR6uFAsI8zwZjANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0yMTAzMzAwMDAwMDBaFw0zMTAzMjkyMzU5NTlaMFkxCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxMzAxBgNVBAMTKkRpZ2lDZXJ0IEdsb2Jh +bCBHMiBUTFMgUlNBIFNIQTI1NiAyMDIwIENBMTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAMz3EGJPprtjb+2QUlbFbSd7ehJWivH0+dbn4Y+9lavyYEEV +cNsSAPonCrVXOFt9slGTcZUOakGUWzUb+nv6u8W+JDD+Vu/E832X4xT1FE3LpxDy +FuqrIvAxIhFhaZAmunjZlx/jfWardUSVc8is/+9dCopZQ+GssjoP80j812s3wWPc +3kbW20X+fSP9kOhRBx5Ro1/tSUZUfyyIxfQTnJcVPAPooTncaQwywa8WV0yUR0J8 +osicfebUTVSvQpmowQTCd5zWSOTOEeAqgJnwQ3DPP3Zr0UxJqyRewg2C/Uaoq2yT +zGJSQnWS+Jr6Xl6ysGHlHx+5fwmY6D36g39HaaECAwEAAaOCAYIwggF+MBIGA1Ud +EwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFHSFgMBmx9833s+9KTeqAx2+7c0XMB8G +A1UdIwQYMBaAFE4iVCAYlebjbuYP+vq5Eu0GF485MA4GA1UdDwEB/wQEAwIBhjAd +BgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwdgYIKwYBBQUHAQEEajBoMCQG +CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQAYIKwYBBQUHMAKG +NGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RH +Mi5jcnQwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2NybDMuZGlnaWNlcnQuY29t +L0RpZ2lDZXJ0R2xvYmFsUm9vdEcyLmNybDA9BgNVHSAENjA0MAsGCWCGSAGG/WwC +ATAHBgVngQwBATAIBgZngQwBAgEwCAYGZ4EMAQICMAgGBmeBDAECAzANBgkqhkiG +9w0BAQsFAAOCAQEAkPFwyyiXaZd8dP3A+iZ7U6utzWX9upwGnIrXWkOH7U1MVl+t +wcW1BSAuWdH/SvWgKtiwla3JLko716f2b4gp/DA/JIS7w7d7kwcsr4drdjPtAFVS +slme5LnQ89/nD/7d+MS5EHKBCQRfz5eeLjJ1js+aWNJXMX43AYGyZm0pGrFmCW3R +bpD0ufovARTFXFZkAdl9h6g4U5+LXUZtXMYnhIHUfoyMo5tS58aI7Dd8KvvwVVo4 +chDYABPPTHPbqjc1qCmBaZx2vN4Ye5DUys/vZwP9BFohFrH/6j/f3IL16/RZkiMN +JCqVJUzKoZHm1Lesh3Sz8W2jmdv51b2EQJ8HmA== -----END CERTIFICATE----- )CERT"; -// http://crt.usertrust.com/USERTrustRSAAddTrustCA.crt -// CN: USERTrust RSA Certification Authority => name: USERTrust_RSA_Certification_Authority -// not valid before: 2019-03-12 00:00:00 -// not valid after: 2028-12-31 23:59:59 -const char cert_USERTrust_RSA_Certification_Authority [] PROGMEM = R"CERT( +// http://cacerts.digicert.com/DigiCertGlobalRootG2.crt +// CN: DigiCert Global Root G2 => name: DigiCert_Global_Root_G2 +// not valid before: 2013-08-01 12:00:00+00:00 +// not valid after: 2038-01-15 12:00:00+00:00 +const char cert_DigiCert_Global_Root_G2 [] PROGMEM = R"CERT( -----BEGIN CERTIFICATE----- -MIIFgTCCBGmgAwIBAgIQOXJEOvkit1HX02wQ3TE1lTANBgkqhkiG9w0BAQwFADB7 -MQswCQYDVQQGEwJHQjEbMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYD -VQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UE -AwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTE5MDMxMjAwMDAwMFoXDTI4 -MTIzMTIzNTk1OVowgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5 -MRQwEgYDVQQHEwtKZXJzZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBO -ZXR3b3JrMS4wLAYDVQQDEyVVU0VSVHJ1c3QgUlNBIENlcnRpZmljYXRpb24gQXV0 -aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAgBJlFzYOw9sI -s9CsVw127c0n00ytUINh4qogTQktZAnczomfzD2p7PbPwdzx07HWezcoEStH2jnG -vDoZtF+mvX2do2NCtnbyqTsrkfjib9DsFiCQCT7i6HTJGLSR1GJk23+jBvGIGGqQ -Ijy8/hPwhxR79uQfjtTkUcYRZ0YIUcuGFFQ/vDP+fmyc/xadGL1RjjWmp2bIcmfb -IWax1Jt4A8BQOujM8Ny8nkz+rwWWNR9XWrf/zvk9tyy29lTdyOcSOk2uTIq3XJq0 -tyA9yn8iNK5+O2hmAUTnAU5GU5szYPeUvlM3kHND8zLDU+/bqv50TmnHa4xgk97E -xwzf4TKuzJM7UXiVZ4vuPVb+DNBpDxsP8yUmazNt925H+nND5X4OpWaxKXwyhGNV -icQNwZNUMBkTrNN9N6frXTpsNVzbQdcS2qlJC9/YgIoJk2KOtWbPJYjNhLixP6Q5 -D9kCnusSTJV882sFqV4Wg8y4Z+LoE53MW4LTTLPtW//e5XOsIzstAL81VXQJSdhJ -WBp/kjbmUZIO8yZ9HE0XvMnsQybQv0FfQKlERPSZ51eHnlAfV1SoPv10Yy+xUGUJ -5lhCLkMaTLTwJUdZ+gQek9QmRkpQgbLevni3/GcV4clXhB4PY9bpYrrWX1Uu6lzG -KAgEJTm4Diup8kyXHAc/DVL17e8vgg8CAwEAAaOB8jCB7zAfBgNVHSMEGDAWgBSg -EQojPpbxB+zirynvgqV/0DCktDAdBgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rID -ZsswDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0gBAowCDAG -BgRVHSAAMEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwuY29tb2RvY2EuY29t -L0FBQUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDQGCCsGAQUFBwEBBCgwJjAkBggr -BgEFBQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2EuY29tMA0GCSqGSIb3DQEBDAUA -A4IBAQAYh1HcdCE9nIrgJ7cz0C7M7PDmy14R3iJvm3WOnnL+5Nb+qh+cli3vA0p+ -rvSNb3I8QzvAP+u431yqqcau8vzY7qN7Q/aGNnwU4M309z/+3ri0ivCRlv79Q2R+ -/czSAaF9ffgZGclCKxO/WIu6pKJmBHaIkU4MiRTOok3JMrO66BQavHHxW/BBC5gA -CiIDEOUMsfnNkjcZ7Tvx5Dq2+UUTJnWvu6rvP3t3O9LEApE9GQDTF1w52z97GA1F -zZOFli9d31kWTz9RvdVFGD/tSo7oBmF0Ixa1DVBzJ0RHfxBdiSprhTEUxOipakyA -vGp4z7h/jnZymQyd/teRCBaho1+V +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI +2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx +1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ +q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz +tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ +vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV +5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY +1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 +NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG +Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 +8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe +pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= -----END CERTIFICATE----- )CERT"; -// end of certificate chain for www.gitlab.com:443 +// end of certificate chain for www.example.com:443 //////////////////////////////////////////////////////////// diff --git a/libraries/ESP8266WiFi/examples/CustomOffer/CustomOffer.ino b/libraries/ESP8266WiFi/examples/CustomOffer/CustomOffer.ino new file mode 100644 index 0000000000..fa746277c9 --- /dev/null +++ b/libraries/ESP8266WiFi/examples/CustomOffer/CustomOffer.ino @@ -0,0 +1,20 @@ +#include +#include + +void setup() { + auto& server = WiFi.softAPDhcpServer(); + server.onSendOptions([](const DhcpServer& server, auto& options) { + // VENDOR is... vendor specific + options.add(43, { 0xca, 0xfe, 0xca, 0xfe, 0xfe }); + + // Captive Portal URI + const IPAddress gateway = netif_ip4_addr(server.getNetif()); + const String captive = F("http://") + gateway.toString(); + options.add(114, captive.c_str(), captive.length()); + }); + WiFi.softAP("TEST", "testtesttest"); +} + +void loop() { + delay(100); +} diff --git a/libraries/ESP8266WiFi/examples/HTTPSRequest/HTTPSRequest.ino b/libraries/ESP8266WiFi/examples/HTTPSRequest/HTTPSRequest.ino index 9ccf3b6b37..750abfa733 100644 --- a/libraries/ESP8266WiFi/examples/HTTPSRequest/HTTPSRequest.ino +++ b/libraries/ESP8266WiFi/examples/HTTPSRequest/HTTPSRequest.ino @@ -13,17 +13,18 @@ #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; -X509List cert(cert_DigiCert_High_Assurance_EV_Root_CA); +X509List cert(cert_Sectigo_ECC_Domain_Validation_Secure_Server_CA); void setup() { Serial.begin(115200); @@ -62,7 +63,7 @@ void setup() { Serial.print("Connecting to "); Serial.println(github_host); - Serial.printf("Using certificate: %s\n", cert_DigiCert_High_Assurance_EV_Root_CA); + Serial.printf("Using certificate: %s\n", cert_Sectigo_ECC_Domain_Validation_Secure_Server_CA); client.setTrustAnchors(&cert); if (!client.connect(github_host, github_port)) { @@ -74,13 +75,10 @@ void setup() { Serial.print("Requesting URL: "); Serial.println(url); - 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"); + 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"); @@ -100,5 +98,4 @@ void setup() { Serial.println("Closing connection"); } -void loop() { -} +void loop() {} diff --git a/libraries/ESP8266WiFi/examples/HTTPSRequest/certs.h b/libraries/ESP8266WiFi/examples/HTTPSRequest/certs.h index 04765d956c..97012d7a59 100644 --- a/libraries/ESP8266WiFi/examples/HTTPSRequest/certs.h +++ b/libraries/ESP8266WiFi/examples/HTTPSRequest/certs.h @@ -1,7 +1,7 @@ // this file is autogenerated - any modification will be overwritten // unused symbols will not be linked in the final binary -// generated on 2021-07-26 22:04:48 +// generated on 2024-07-30 23:28:12 // by ['../../../../tools/cert.py', '-s', 'api.github.com', '-n', 'github'] #pragma once @@ -13,74 +13,72 @@ const char* github_host = "api.github.com"; const uint16_t github_port = 443; // CN: *.github.com => name: __github_com -// not valid before: 2021-03-25 00:00:00 -// not valid after: 2022-03-30 23:59:59 -const char fingerprint___github_com [] PROGMEM = "96:84:07:df:0b:1c:f6:58:14:df:d7:33:35:57:51:9b:15:4d:8c:e7"; +// not valid before: 2024-03-07 00:00:00+00:00 +// not valid after: 2025-03-07 23:59:59+00:00 +const char fingerprint___github_com [] PROGMEM = "0d:f6:ec:50:fa:ed:ae:6e:13:af:82:94:52:f7:11:1b:0a:cf:7c:20"; const char pubkey___github_com [] PROGMEM = R"PUBKEY( -----BEGIN PUBLIC KEY----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAElL9/+0TidTIALPfU/tiS6pI8zAIk -rU4pohUldVc0bb6O3FARl3cnqIDK9SoF65z3xiR6XsnFS8F0Oy/chXR/kQ== +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEcAMYSUSbAQpBM6MJN5kRD5gVpxvK +QgpD4jQ4jY1CqNOeWP7fOkn+PxdiJq76Qv5bPmv3tTxD6plhoNDYDohvMg== -----END PUBLIC KEY----- )PUBKEY"; -// http://cacerts.digicert.com/DigiCertHighAssuranceTLSHybridECCSHA2562020CA1.crt -// CN: DigiCert High Assurance TLS Hybrid ECC SHA256 2020 CA1 => name: DigiCert_High_Assurance_TLS_Hybrid_ECC_SHA256_2020_CA1 -// not valid before: 2020-12-17 00:00:00 -// not valid after: 2030-12-16 23:59:59 -const char cert_DigiCert_High_Assurance_TLS_Hybrid_ECC_SHA256_2020_CA1 [] PROGMEM = R"CERT( +// http://crt.sectigo.com/SectigoECCDomainValidationSecureServerCA.crt +// CN: Sectigo ECC Domain Validation Secure Server CA => name: Sectigo_ECC_Domain_Validation_Secure_Server_CA +// not valid before: 2018-11-02 00:00:00+00:00 +// not valid after: 2030-12-31 23:59:59+00:00 +const char cert_Sectigo_ECC_Domain_Validation_Secure_Server_CA [] PROGMEM = R"CERT( -----BEGIN CERTIFICATE----- -MIIEGzCCAwOgAwIBAgIQBmcDW7sU/WOvwNaoU07+FjANBgkqhkiG9w0BAQsFADBs -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBFViBSb290IENBMB4XDTIwMTIxNzAwMDAwMFoXDTMwMTIxNjIzNTk1OVowZzEL -MAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMT8wPQYDVQQDEzZE -aWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBUTFMgSHlicmlkIEVDQyBTSEEyNTYgMjAy -MCBDQTEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARnvW/xPOudvtC252wTq9ef -6fbdFeWPkOscfpRTkciuHj7UcumQSH3lzkPEIx0KpesWa8epsks7QwkZ4fU/Tkf9 -o4IBhzCCAYMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUUGGmoNI1xBEq -II0fD6xC8M0pz0swHwYDVR0jBBgwFoAUsT7DaQP4v0cB1JgmGggC72NkK8MwDgYD -VR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjB/Bggr -BgEFBQcBAQRzMHEwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNv -bTBJBggrBgEFBQcwAoY9aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lD -ZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNydDBLBgNVHR8ERDBCMECgPqA8hjpo -dHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZS -b290Q0EuY3JsMDAGA1UdIAQpMCcwCAYGZ4EMAQICMAgGBmeBDAECAzAHBgVngQwB -ATAIBgZngQwBAgEwDQYJKoZIhvcNAQELBQADggEBAHMQH8hhiBfNbxwEwxbbTAnu -jPyUh/oi0JrfZI3u9JuiLqca720D6foS/AB5+4EIxpm7CMG4MdN/l7oAiDipaCPv -mOmpYUpnT7A63Cr0q4g84rI1ZmdqA40lVUUf6qC6E34tC73qDQF8TJSrfscWFdCl -RXR9J4QGrkZ2VNMSDzlDRzWCaA95MfO8x01l+ZdopdE8FvM78gGd4zxeWb8v991+ -mBxTDepqKuy/jF5Rm6Bhfxr33ADRs60s1t16dtZ3pOYLALBTPD5KhZ6a+/dk5dnh -6c4PaeZQYBUAh+GuxfaBlU4qQ8EtjBMCQHreMIwXHYHW5FRYGjgR4NMuaIw2jD0= +MIIDqDCCAy6gAwIBAgIRAPNkTmtuAFAjfglGvXvh9R0wCgYIKoZIzj0EAwMwgYgx +CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5MRQwEgYDVQQHEwtKZXJz +ZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMS4wLAYDVQQD +EyVVU0VSVHJ1c3QgRUNDIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTE4MTEw +MjAwMDAwMFoXDTMwMTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAkdCMRswGQYDVQQI +ExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGDAWBgNVBAoT +D1NlY3RpZ28gTGltaXRlZDE3MDUGA1UEAxMuU2VjdGlnbyBFQ0MgRG9tYWluIFZh +bGlkYXRpb24gU2VjdXJlIFNlcnZlciBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEH +A0IABHkYk8qfbZ5sVwAjBTcLXw9YWsTef1Wj6R7W2SUKiKAgSh16TwUwimNJE4xk +IQeV/To14UrOkPAY9z2vaKb71EijggFuMIIBajAfBgNVHSMEGDAWgBQ64QmG1M8Z +wpZ2dEl23OA1xmNjmjAdBgNVHQ4EFgQU9oUKOxGG4QR9DqoLLNLuzGR7e64wDgYD +VR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0lBBYwFAYIKwYB +BQUHAwEGCCsGAQUFBwMCMBsGA1UdIAQUMBIwBgYEVR0gADAIBgZngQwBAgEwUAYD +VR0fBEkwRzBFoEOgQYY/aHR0cDovL2NybC51c2VydHJ1c3QuY29tL1VTRVJUcnVz +dEVDQ0NlcnRpZmljYXRpb25BdXRob3JpdHkuY3JsMHYGCCsGAQUFBwEBBGowaDA/ +BggrBgEFBQcwAoYzaHR0cDovL2NydC51c2VydHJ1c3QuY29tL1VTRVJUcnVzdEVD +Q0FkZFRydXN0Q0EuY3J0MCUGCCsGAQUFBzABhhlodHRwOi8vb2NzcC51c2VydHJ1 +c3QuY29tMAoGCCqGSM49BAMDA2gAMGUCMEvnx3FcsVwJbZpCYF9z6fDWJtS1UVRs +cS0chWBNKPFNpvDKdrdKRe+oAkr2jU+ubgIxAODheSr2XhcA7oz9HmedGdMhlrd9 +4ToKFbZl+/OnFFzqnvOhcjHvClECEQcKmc8fmA== -----END CERTIFICATE----- )CERT"; -// http://cacerts.digicert.com/DigiCertHighAssuranceEVRootCA.crt -// CN: DigiCert High Assurance EV Root CA => name: DigiCert_High_Assurance_EV_Root_CA -// not valid before: 2006-11-10 00:00:00 -// not valid after: 2031-11-10 00:00:00 -const char cert_DigiCert_High_Assurance_EV_Root_CA [] PROGMEM = R"CERT( +// http://crt.usertrust.com/USERTrustECCAddTrustCA.crt +// CN: USERTrust ECC Certification Authority => name: USERTrust_ECC_Certification_Authority +// not valid before: 2019-03-12 00:00:00+00:00 +// not valid after: 2028-12-31 23:59:59+00:00 +const char cert_USERTrust_ECC_Certification_Authority [] PROGMEM = R"CERT( -----BEGIN CERTIFICATE----- -MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL -MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 -LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug -RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm -+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW -PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM -xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB -Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 -hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg -EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF -MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA -FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec -nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z -eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF -hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 -Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe -vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep -+OkuE6N36B9K +MIID0zCCArugAwIBAgIQVmcdBOpPmUxvEIFHWdJ1lDANBgkqhkiG9w0BAQwFADB7 +MQswCQYDVQQGEwJHQjEbMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYD +VQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UE +AwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTE5MDMxMjAwMDAwMFoXDTI4 +MTIzMTIzNTk1OVowgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5 +MRQwEgYDVQQHEwtKZXJzZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBO +ZXR3b3JrMS4wLAYDVQQDEyVVU0VSVHJ1c3QgRUNDIENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEGqxUWqn5aCPnetUkb1PGWthL +q8bVttHmc3Gu3ZzWDGH926CJA7gFFOxXzu5dP+Ihs8731Ip54KODfi2X0GHE8Znc +JZFjq38wo7Rw4sehM5zzvy5cU7Ffs30yf4o043l5o4HyMIHvMB8GA1UdIwQYMBaA +FKARCiM+lvEH7OKvKe+CpX/QMKS0MB0GA1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1 +xmNjmjAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zARBgNVHSAECjAI +MAYGBFUdIAAwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5j +b20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNAYIKwYBBQUHAQEEKDAmMCQG +CCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wDQYJKoZIhvcNAQEM +BQADggEBABns652JLCALBIAdGN5CmXKZFjK9Dpx1WywV4ilAbe7/ctvbq5AfjJXy +ij0IckKJUAfiORVsAYfZFhr1wHUrxeZWEQff2Ji8fJ8ZOd+LygBkc7xGEJuTI42+ +FsMuCIKchjN0djsoTI0DQoWz4rIjQtUfenVqGtF8qmchxDM6OW1TyaLtYiKou+JV +bJlsQ2uRl9EMC5MCHdK8aXdJ5htN978UeAOwproLtOGFfy/cQjutdAFI3tZs4RmY +CV4Ks2dH/hzg1cEo70qLRDEmBDeNiXQ2Lu+lIg+DdEmSx/cQwgwp+7e9un/jX9Wf +8qn0dNW44bOwgeThpWOjzOoEeJBuv/c= -----END CERTIFICATE----- )CERT"; diff --git a/libraries/ESP8266WiFi/examples/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 index 05fa597ab7..a19ffa42f7 100644 --- a/libraries/ESP8266WiFi/examples/PagerServer/PagerServer.ino +++ b/libraries/ESP8266WiFi/examples/PagerServer/PagerServer.ino @@ -2,7 +2,7 @@ Pager Server The ESP8266WiFi library's WiFiServer and WiFiServerSecure - work differently then WiFiServer and EthernetSever + work differently than WiFiServer and EthernetSever in Arduino networking libraries. This example demonstrates the ArduinoWiFiServer, which enhances the WiFiServer. @@ -25,10 +25,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; ArduinoWiFiServer server(2323); @@ -60,13 +60,13 @@ void setup() { 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 + 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 index 9751ba923a..c4a6cb788d 100644 --- a/libraries/ESP8266WiFi/examples/WiFiEcho/WiFiEcho.ino +++ b/libraries/ESP8266WiFi/examples/WiFiEcho/WiFiEcho.ino @@ -7,11 +7,11 @@ #include #include #include -#include // std::min +#include // std::min #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif constexpr int port = 23; @@ -19,12 +19,12 @@ constexpr int port = 23; WiFiServer server(port); WiFiClient client; -constexpr size_t sizes [] = { 0, 512, 384, 256, 128, 64, 16, 8, 4 }; +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 +int t = 1; // test (1, 2 or 3, see below) +int s = 0; // sizes[] index void setup() { @@ -63,31 +63,40 @@ void loop() { 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)"); - } + 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 + // check if there are any new clients if (server.hasClient()) { - client = server.available(); + 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; + 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(); @@ -113,9 +122,7 @@ void loop() { 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); - } + 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++; } @@ -141,7 +148,7 @@ void loop() { else if (t == 4) { // stream to print, possibly with only one copy - tot += client.sendAll(&client); // this one might not exit until peer close + tot += client.sendAll(&client); // this one might not exit until peer close cnt++; switch (client.getLastSendReport()) { @@ -152,5 +159,4 @@ void loop() { case Stream::Report::ShortOperation: Serial.println("Stream::send: short transfer"); break; } } - } 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/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 index b03357c143..69502de862 100644 --- a/libraries/ESP8266WiFi/examples/WiFiShutdown/WiFiShutdown.ino +++ b/libraries/ESP8266WiFi/examples/WiFiShutdown/WiFiShutdown.ino @@ -17,7 +17,7 @@ #endif #include -#include // WiFiState structure details +#include // WiFiState structure details WiFiState state; @@ -26,7 +26,7 @@ const char* password = STAPSK; void setup() { Serial.begin(74880); - //Serial.setDebugOutput(true); // If you need debug output + // 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 @@ -36,17 +36,14 @@ void setup() { // 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)); + 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)) { + 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)) { + if (!WiFi.mode(WIFI_STA) || !WiFi.begin(ssid, password) || (WiFi.waitForConnectResult(10000) != WL_CONNECTED)) { WiFi.mode(WIFI_OFF); Serial.println("Cannot connect!"); Serial.flush(); @@ -64,7 +61,7 @@ void setup() { // --- WiFi.shutdown(state); - ESP.rtcUserMemoryWrite(RTC_USER_DATA_SLOT_WIFI_STATE, reinterpret_cast(&state), sizeof(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. diff --git a/libraries/ESP8266WiFi/examples/WiFiTelnetToSerial/WiFiTelnetToSerial.ino b/libraries/ESP8266WiFi/examples/WiFiTelnetToSerial/WiFiTelnetToSerial.ino index 9d085bb2ff..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,7 +149,7 @@ void loop() { } } - //check TCP clients for data + // check TCP clients for data #if 1 // Incredibly, this code is faster than the buffered one below - #4620 is needed // loopback/3000000baud average 348KB/s @@ -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/src/ArduinoWiFiServer.h b/libraries/ESP8266WiFi/src/ArduinoWiFiServer.h index 31b8ea0c16..45e5e59d6e 100644 --- a/libraries/ESP8266WiFi/src/ArduinoWiFiServer.h +++ b/libraries/ESP8266WiFi/src/ArduinoWiFiServer.h @@ -28,27 +28,18 @@ #endif template -class ArduinoComptibleWiFiServerTemplate : public TServer { +class ArduinoCompatibleWiFiServerTemplate : public TServer, public Print { public: - ArduinoComptibleWiFiServerTemplate(const IPAddress& addr, uint16_t port) : TServer(addr, port) {} - ArduinoComptibleWiFiServerTemplate(uint16_t port) : TServer(port) {} - virtual ~ArduinoComptibleWiFiServerTemplate() {} - - // https://www.arduino.cc/en/Reference/EthernetServerAccept - TClient accept() { - return TServer::available(); - } + 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() { - // update connected clients - for (uint8_t i = 0; i < MAX_MONITORED_CLIENTS; i++) { - if (!connectedClients[i]) { - connectedClients[i] = accept(); - } - } + acceptClients(); + // find next client with data available for (uint8_t i = 0; i < MAX_MONITORED_CLIENTS; i++) { if (index == MAX_MONITORED_CLIENTS) { @@ -67,6 +58,12 @@ class ArduinoComptibleWiFiServerTemplate : public TServer { } 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; @@ -127,9 +124,16 @@ class ArduinoComptibleWiFiServerTemplate : public TServer { 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 ArduinoComptibleWiFiServerTemplate ArduinoWiFiServer; -typedef ArduinoComptibleWiFiServerTemplate ArduinoWiFiServerSecure; +typedef ArduinoCompatibleWiFiServerTemplate ArduinoWiFiServer; +typedef ArduinoCompatibleWiFiServerTemplate ArduinoWiFiServerSecure; #endif diff --git a/libraries/ESP8266WiFi/src/BearSSLHelpers.cpp b/libraries/ESP8266WiFi/src/BearSSLHelpers.cpp index 08549cc99a..0b18334cbd 100644 --- a/libraries/ESP8266WiFi/src/BearSSLHelpers.cpp +++ b/libraries/ESP8266WiFi/src/BearSSLHelpers.cpp @@ -937,11 +937,15 @@ uint32_t SigningVerifier::length() // directly inside the class function for ease of use. extern "C" bool SigningVerifier_verify(PublicKey *_pubKey, UpdaterHashClass *hash, const void *signature, uint32_t signatureLen) { if (_pubKey->isRSA()) { - bool ret; - unsigned char vrf[hash->len()]; + // see https://github.com/earlephilhower/bearssl-esp8266/blob/6105635531027f5b298aa656d44be2289b2d434f/inc/bearssl_rsa.h#L257 + static constexpr int HashLengthMax = 64; + unsigned char vrf[HashLengthMax]; + if (hash->len() > HashLengthMax) { + return false; + } br_rsa_pkcs1_vrfy vrfy = br_rsa_pkcs1_vrfy_get_default(); - ret = vrfy((const unsigned char *)signature, signatureLen, hash->oid(), sizeof(vrf), _pubKey->getRSA(), vrf); - if (!ret || memcmp(vrf, hash->hash(), sizeof(vrf)) ) { + bool ret = vrfy((const unsigned char *)signature, signatureLen, hash->oid(), hash->len(), _pubKey->getRSA(), vrf); + if (!ret || memcmp(vrf, hash->hash(), std::min(HashLengthMax, hash->len())) ) { return false; } else { return true; diff --git a/libraries/ESP8266WiFi/src/BearSSLHelpers.h b/libraries/ESP8266WiFi/src/BearSSLHelpers.h index 397a557522..3033a80aec 100644 --- a/libraries/ESP8266WiFi/src/BearSSLHelpers.h +++ b/libraries/ESP8266WiFi/src/BearSSLHelpers.h @@ -58,6 +58,7 @@ class PublicKey { // Disable the copy constructor, we're pointer based PublicKey(const PublicKey& that) = delete; + PublicKey& operator=(const PublicKey& that) = delete; private: brssl::public_key *_key; @@ -86,6 +87,7 @@ class PrivateKey { // Disable the copy constructor, we're pointer based PrivateKey(const PrivateKey& that) = delete; + PrivateKey& operator=(const PrivateKey& that) = delete; private: brssl::private_key *_key; @@ -122,6 +124,7 @@ class X509List { // Disable the copy constructor, we're pointer based X509List(const X509List& that) = delete; + X509List& operator=(const X509List& that) = delete; private: size_t _count; diff --git a/libraries/ESP8266WiFi/src/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/ESP8266WiFiAP.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiAP.cpp index d046a33841..cab599b6a8 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiAP.cpp +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiAP.cpp @@ -26,6 +26,8 @@ #include "ESP8266WiFiGeneric.h" #include "ESP8266WiFiAP.h" +#include + extern "C" { #include "c_types.h" #include "ets_sys.h" @@ -37,7 +39,6 @@ extern "C" { } #include "debug.h" -#include "LwipDhcpServer.h" // ----------------------------------------------------------------------------------------------------------------------- // ---------------------------------------------------- Private functions ------------------------------------------------ @@ -93,8 +94,9 @@ static bool softap_config_equal(const softap_config& lhs, const softap_config& r * @param channel WiFi channel number, 1 - 13. * @param ssid_hidden Network cloaking (0 = broadcast SSID, 1 = hide SSID) * @param max_connection Max simultaneous connected clients, 0 - 8. https://bbs.espressif.com/viewtopic.php?f=46&t=481&p=1832&hilit=max_connection#p1832 + * @param beacon_interval set arbitrary beacon interval (influences DTIM) */ -bool ESP8266WiFiAPClass::softAP(const char* ssid, const char* psk, int channel, int ssid_hidden, int max_connection) { +bool ESP8266WiFiAPClass::softAP(const char* ssid, const char* psk, int channel, int ssid_hidden, int max_connection, int beacon_interval) { if(!WiFi.enableAP(true)) { // enable AP failed @@ -104,13 +106,13 @@ bool ESP8266WiFiAPClass::softAP(const char* ssid, const char* psk, int channel, size_t ssid_len = ssid ? strlen(ssid) : 0; if(ssid_len == 0 || ssid_len > 32) { - DEBUG_WIFI("[AP] SSID length %u, too long or missing!\n", ssid_len); + DEBUG_WIFI("[AP] SSID length %zu, too long or missing!\n", ssid_len); return false; } size_t psk_len = psk ? strlen(psk) : 0; if(psk_len > 0 && (psk_len > 64 || psk_len < 8)) { - DEBUG_WIFI("[AP] fail psk length %u, too long or short!\n", psk_len); + DEBUG_WIFI("[AP] fail psk length %zu, too long or short!\n", psk_len); return false; } @@ -137,7 +139,7 @@ bool ESP8266WiFiAPClass::softAP(const char* ssid, const char* psk, int channel, conf.channel = channel; conf.ssid_hidden = ssid_hidden; conf.max_connection = max_connection; - conf.beacon_interval = 100; + conf.beacon_interval = beacon_interval; struct softap_config conf_compare; if(WiFi._persistent){ @@ -166,33 +168,30 @@ bool ESP8266WiFiAPClass::softAP(const char* ssid, const char* psk, int channel, DEBUG_WIFI("[AP] softap config unchanged\n"); } - dhcpSoftAP.end(); + wifi_softap_dhcps_stop(); // check IP config struct ip_info ip; if(wifi_get_ip_info(SOFTAP_IF, &ip)) { if(ip.ip.addr == 0x00000000) { - // Invalid config DEBUG_WIFI("[AP] IP config Invalid resetting...\n"); - //192.168.4.1 , 192.168.4.1 , 255.255.255.0 - ret = softAPConfig(0x0104A8C0, 0x0104A8C0, 0x00FFFFFF); - if(!ret) { - DEBUG_WIFI("[AP] softAPConfig failed!\n"); - ret = false; - } + ret = softAPConfig( + IPAddress(192, 168, 4, 1), + IPAddress(192, 168, 4, 1), + IPAddress(255, 255, 255, 0)); } } else { DEBUG_WIFI("[AP] wifi_get_ip_info failed!\n"); ret = false; } - dhcpSoftAP.begin(&ip); + wifi_softap_dhcps_start(); return ret; } -bool ESP8266WiFiAPClass::softAP(const String& ssid, const String& psk, int channel, int ssid_hidden, int max_connection) { - return softAP(ssid.c_str(), psk.c_str(), channel, ssid_hidden, max_connection); +bool ESP8266WiFiAPClass::softAP(const String& ssid, const String& psk, int channel, int ssid_hidden, int max_connection, int beacon_interval) { + return softAP(ssid.c_str(), psk.c_str(), channel, ssid_hidden, max_connection, beacon_interval); } /** @@ -224,10 +223,10 @@ bool ESP8266WiFiAPClass::softAPConfig(IPAddress local_ip, IPAddress gateway, IPA info.gw.addr = gateway.v4(); info.netmask.addr = subnet.v4(); - if(!wifi_softap_dhcps_stop()) { - DEBUG_WIFI("[APConfig] wifi_softap_dhcps_stop failed!\n"); - } - + // use SDK function for dhcps, not just server.begin() + // setting info with static IPs will fail otherwise + // (TODO: dhcps_flag seems to store 'SDK' DHCPs status) + wifi_softap_dhcps_stop(); if(!wifi_set_ip_info(SOFTAP_IF, &info)) { DEBUG_WIFI("[APConfig] wifi_set_ip_info failed!\n"); ret = false; @@ -244,30 +243,17 @@ bool ESP8266WiFiAPClass::softAPConfig(IPAddress local_ip, IPAddress gateway, IPA dhcp_lease.end_ip.addr = ip.v4(); DEBUG_WIFI("[APConfig] DHCP IP end: %s\n", ip.toString().c_str()); - if(!dhcpSoftAP.set_dhcps_lease(&dhcp_lease)) + auto& server = softAPDhcpServer(); + if(!server.set_dhcps_lease(&dhcp_lease)) { - DEBUG_WIFI("[APConfig] wifi_set_ip_info failed!\n"); + DEBUG_WIFI("[APConfig] server set_dhcps_lease failed!\n"); ret = false; } - // set lease time to 720min --> 12h - if(!dhcpSoftAP.set_dhcps_lease_time(720)) - { - DEBUG_WIFI("[APConfig] wifi_softap_set_dhcps_lease_time failed!\n"); - ret = false; - } + // send ROUTER option with netif's gateway IP + server.setRouter(true); - uint8 mode = info.gw.addr ? 1 : 0; - if(!dhcpSoftAP.set_dhcps_offer_option(OFFER_ROUTER, &mode)) - { - DEBUG_WIFI("[APConfig] wifi_softap_set_dhcps_offer_option failed!\n"); - ret = false; - } - - if(!wifi_softap_dhcps_start()) { - DEBUG_WIFI("[APConfig] wifi_softap_dhcps_start failed!\n"); - ret = false; - } + wifi_softap_dhcps_start(); // check config if(wifi_get_ip_info(SOFTAP_IF, &info)) { @@ -291,7 +277,7 @@ bool ESP8266WiFiAPClass::softAPConfig(IPAddress local_ip, IPAddress gateway, IPA /** * Disconnect from the network (close AP) * @param wifioff disable mode? - * @return one value of wl_status_t enum + * @return operation success */ bool ESP8266WiFiAPClass::softAPdisconnect(bool wifioff) { bool ret; @@ -337,6 +323,17 @@ IPAddress ESP8266WiFiAPClass::softAPIP() { return IPAddress(ip.ip.addr); } +/** + * Get the softAP broadcast ip address. + * @return IPAddress softAP broadcast IP + */ +IPAddress ESP8266WiFiAPClass::softAPbroadcastIP() +{ + struct ip_info ip; + wifi_get_ip_info(SOFTAP_IF, &ip); + return IPAddress(ip.ip.addr | ~(ip.netmask.addr)); +} + /** * Get the softAP interface MAC address. @@ -389,3 +386,11 @@ String ESP8266WiFiAPClass::softAPPSK() const { return psk; } + +/** + * Get the static DHCP server instance attached to the softAP interface + * @return DhcpServer instance. + */ +DhcpServer& ESP8266WiFiAPClass::softAPDhcpServer() { + return getNonOSDhcpServer(); +} diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiAP.h b/libraries/ESP8266WiFi/src/ESP8266WiFiAP.h index b8b3b8fb12..1bbc3a6d89 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiAP.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiAP.h @@ -27,6 +27,7 @@ #include "ESP8266WiFiType.h" #include "ESP8266WiFiGeneric.h" +#include class ESP8266WiFiAPClass { @@ -36,20 +37,23 @@ class ESP8266WiFiAPClass { public: - bool softAP(const char* ssid, const char* psk = NULL, int channel = 1, int ssid_hidden = 0, int max_connection = 4); - bool softAP(const String& ssid,const String& psk = emptyString,int channel = 1,int ssid_hidden = 0,int max_connection = 4); + bool softAP(const char* ssid, const char* psk = NULL, int channel = 1, int ssid_hidden = 0, int max_connection = 4, int beacon_interval = 100); + bool softAP(const String& ssid,const String& psk = emptyString,int channel = 1,int ssid_hidden = 0,int max_connection = 4,int beacon_interval = 100); bool softAPConfig(IPAddress local_ip, IPAddress gateway, IPAddress subnet); bool softAPdisconnect(bool wifioff = false); uint8_t softAPgetStationNum(); IPAddress softAPIP(); + IPAddress softAPbroadcastIP(); uint8_t* softAPmacAddress(uint8_t* mac); String softAPmacAddress(void); - String softAPSSID() const; - String softAPPSK() const; + String softAPSSID() const; + String softAPPSK() const; + + static DhcpServer& softAPDhcpServer(); protected: diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.cpp index 19da592c76..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 ----------------------------------------------- @@ -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) { @@ -415,7 +425,10 @@ bool ESP8266WiFiGenericClass::mode(WiFiMode_t m) { return true; } + char backup_hostname [33] { 0 }; // hostname is 32 chars long (RFC) + if (m != WIFI_OFF && wifi_fpm_get_sleep_type() != NONE_SLEEP_T) { + memcpy(backup_hostname, wifi_station_hostname, sizeof(backup_hostname)); // wifi starts asleep by default wifi_fpm_do_wakeup(); wifi_fpm_close(); @@ -438,10 +451,8 @@ bool ESP8266WiFiGenericClass::mode(WiFiMode_t m) { //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) { @@ -449,6 +460,9 @@ bool ESP8266WiFiGenericClass::mode(WiFiMode_t m) { } } + if (backup_hostname[0]) + memcpy(wifi_station_hostname, backup_hostname, sizeof(backup_hostname)); + return ret; } @@ -518,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) { @@ -559,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 } @@ -571,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 } @@ -583,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 @@ -696,16 +702,20 @@ 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) diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h index 3d3ceb3262..e0ad560d0e 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiGeneric.h @@ -24,6 +24,10 @@ #define ESP8266WIFIGENERIC_H_ #include "ESP8266WiFiType.h" + +#include +#include + #include #include @@ -44,12 +48,15 @@ typedef void (*WiFiEventCb)(WiFiEvent_t); enum class DNSResolveType: uint8_t { - DNS_AddrType_IPv4 = 0, // LWIP_DNS_ADDRTYPE_IPV4 = 0 - DNS_AddrType_IPv6, // LWIP_DNS_ADDRTYPE_IPV6 = 1 - DNS_AddrType_IPv4_IPv6, // LWIP_DNS_ADDRTYPE_IPV4_IPV6 = 2 - DNS_AddrType_IPv6_IPv4 // LWIP_DNS_ADDRTYPE_IPV6_IPV4 = 3 + DNS_AddrType_IPv4 = LWIP_DNS_ADDRTYPE_IPV4, + DNS_AddrType_IPv6 = LWIP_DNS_ADDRTYPE_IPV6, + DNS_AddrType_IPv4_IPv6 = LWIP_DNS_ADDRTYPE_IPV4_IPV6, + DNS_AddrType_IPv6_IPv4 = LWIP_DNS_ADDRTYPE_IPV6_IPV4, }; +inline constexpr auto DNSDefaultTimeoutMs = 10000; +inline constexpr auto DNSResolveTypeDefault = static_cast(LWIP_DNS_ADDRTYPE_DEFAULT); + struct WiFiState; class ESP8266WiFiGenericClass { @@ -64,15 +71,15 @@ class ESP8266WiFiGenericClass { void onEvent(WiFiEventCb cb, WiFiEvent_t event = WIFI_EVENT_ANY) __attribute__((deprecated)); // Subscribe to specific event and get event information as an argument to the callback - WiFiEventHandler onStationModeConnected(std::function); - WiFiEventHandler onStationModeDisconnected(std::function); - WiFiEventHandler onStationModeAuthModeChanged(std::function); - WiFiEventHandler onStationModeGotIP(std::function); - WiFiEventHandler onStationModeDHCPTimeout(std::function); - WiFiEventHandler onSoftAPModeStationConnected(std::function); - WiFiEventHandler onSoftAPModeStationDisconnected(std::function); - WiFiEventHandler onSoftAPModeProbeRequestReceived(std::function); - WiFiEventHandler onWiFiModeChange(std::function); + [[nodiscard]] WiFiEventHandler onStationModeConnected(std::function); + [[nodiscard]] WiFiEventHandler onStationModeDisconnected(std::function); + [[nodiscard]] WiFiEventHandler onStationModeAuthModeChanged(std::function); + [[nodiscard]] WiFiEventHandler onStationModeGotIP(std::function); + [[nodiscard]] WiFiEventHandler onStationModeDHCPTimeout(std::function); + [[nodiscard]] WiFiEventHandler onSoftAPModeStationConnected(std::function); + [[nodiscard]] WiFiEventHandler onSoftAPModeStationDisconnected(std::function); + [[nodiscard]] WiFiEventHandler onSoftAPModeProbeRequestReceived(std::function); + [[nodiscard]] WiFiEventHandler onWiFiModeChange(std::function); uint8_t channel(void); diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiMulti.cpp index 8d74f687cb..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(); @@ -340,7 +327,7 @@ wl_status_t ESP8266WiFiMulti::connectWiFiMulti(uint32_t connectTimeoutMs) // 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); @@ -373,7 +360,7 @@ wl_status_t ESP8266WiFiMulti::connectWiFiMulti(uint32_t connectTimeoutMs) } } - DEBUG_WIFI_MULTI("[WIFIM] Could not connect\n", ssid); + DEBUG_WIFI_MULTI("[WIFIM] Could not connect\n"); // Could not connect to any WiFi network return WL_CONNECT_FAILED; @@ -535,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 dd910295dd..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); } @@ -576,6 +646,18 @@ uint8_t* ESP8266WiFiSTAClass::BSSID(void) { return reinterpret_cast(conf.bssid); } +/** + * Fill the current bssid / mac associated with the network if configured + * @param bssid pointer to uint8_t array with length WL_MAC_ADDR_LENGTH + * @return bssid uint8_t * + */ +uint8_t* ESP8266WiFiSTAClass::BSSID(uint8_t* bssid) { + struct station_config conf; + wifi_station_get_config(&conf); + memcpy(bssid, conf.bssid, WL_MAC_ADDR_LENGTH); + return bssid; +} + /** * Return the current bssid / mac associated with the network if configured * @return String bssid mac diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.h b/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.h index 32813a5377..ad1b655a55 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFiSTA.h @@ -45,10 +45,19 @@ class ESP8266WiFiSTAClass: public LwipIntf { //The argument order for ESP is not the same as for Arduino. However, there is compatibility code under the hood //to detect Arduino arg order, and handle it correctly. Be aware that the Arduino default value handling doesn't //work here (see Arduino docs for gway/subnet defaults). In other words: at least 3 args must always be given. - bool config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1 = (uint32_t)0x00000000, IPAddress dns2 = (uint32_t)0x00000000); + bool config(IPAddress local_ip, IPAddress gateway, IPAddress subnet, IPAddress dns1 = INADDR_ANY, IPAddress dns2 = INADDR_ANY); + + // two and one parameter version. 2nd parameter is DNS like in Arduino + // IPv4 only + [[deprecated("It is discouraged to use this 1 or 2 parameters network configuration legacy function config(ip[,dns]) as chosen defaults may not match the local network configuration")]] + bool config(IPAddress local_ip, IPAddress dns = INADDR_ANY); + + bool setDNS(IPAddress dns1, IPAddress dns2 = INADDR_ANY); bool reconnect(); + bool disconnect(bool wifioff = false); + bool disconnect(bool wifioff, bool eraseCredentials); bool isConnected(); @@ -77,6 +86,7 @@ class ESP8266WiFiSTAClass: public LwipIntf { String psk() const; uint8_t * BSSID(); + uint8_t * BSSID(uint8_t* bssid); String BSSIDstr(); int8_t RSSI(); diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFiScan.cpp b/libraries/ESP8266WiFi/src/ESP8266WiFiScan.cpp index 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/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 decc7d7ac0..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" @@ -101,6 +99,10 @@ WiFiClient::~WiFiClient() _client->unref(); } +std::unique_ptr WiFiClient::clone() const { + return std::make_unique(*this); +} + WiFiClient::WiFiClient(const WiFiClient& other) { _client = other._client; @@ -374,6 +376,17 @@ uint16_t WiFiClient::localPort() return _client->getLocalPort(); } +// Api for heap saving. Optional use instead of WiFiClient::stop to systematically retreive some heap memory +// and avoiding server crashes in case of frequent clients connections. +void WiFiClient::abort() +{ + if (!_client) + return; + + flush(0); // Flush output buffer. Don't make any use of return boolean. + _client->abort(); // Wich in turn calls tcp_abort which calls tcp_abandon(). +} + void WiFiClient::stopAll() { for (WiFiClient* it = _s_first; it; it = it->_next) { diff --git a/libraries/ESP8266WiFi/src/WiFiClient.h b/libraries/ESP8266WiFi/src/WiFiClient.h index 038e8032df..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,7 +71,8 @@ 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); - size_t write(Stream& stream) [[ deprecated("use stream.sendHow(client...)") ]]; + [[ deprecated("use stream.sendHow(client...)") ]] + size_t write(Stream& stream); virtual int available() override; virtual int read() override; @@ -71,17 +84,17 @@ class WiFiClient : public Client, public SList { 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; } @@ -90,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 @@ -135,6 +148,10 @@ class WiFiClient : public Client, public SList { virtual bool outputCanTimeout () override { return connected(); } virtual bool inputCanTimeout () override { return connected(); } + // Immediately stops this client instance. + // Unlike stop(), does not wait to gracefuly shutdown the connection. + void abort(); + protected: static int8_t _s_connected(void* arg, void* tpcb, int8_t err); diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp index 6a82ff3b96..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 @@ -253,19 +250,32 @@ void WiFiClientSecureCtx::_freeSSL() { } bool WiFiClientSecureCtx::_clientConnected() { - return (_client && _client->state() == ESTABLISHED); + if (!_client || (_client->state() == CLOSED)) { + return false; + } + + return _client->state() == ESTABLISHED; +} + +bool WiFiClientSecureCtx::_engineConnected() { + return _clientConnected() && _handshake_done && _eng && (br_ssl_engine_current_state(_eng) != BR_SSL_CLOSED); } uint8_t WiFiClientSecureCtx::connected() { - if (available() || (_clientConnected() && _handshake_done && (br_ssl_engine_current_state(_eng) != BR_SSL_CLOSED))) { + if (!_engineConnected()) { + return false; + } + + if (_pollRecvBuffer() > 0) { return true; } - return false; + + return _engineConnected(); } int WiFiClientSecureCtx::availableForWrite () { - // code taken from ::_write() - if (!connected() || !_handshake_done) { + // Can't write things when there's no connection or br_ssl engine is closed + if (!_engineConnected()) { return 0; } // Get BearSSL to a state where we can send @@ -287,7 +297,7 @@ int WiFiClientSecureCtx::availableForWrite () { size_t WiFiClientSecureCtx::_write(const uint8_t *buf, size_t size, bool pmem) { size_t sent_bytes = 0; - if (!connected() || !size || !_handshake_done) { + if (!size || !_engineConnected()) { return 0; } @@ -334,10 +344,11 @@ size_t WiFiClientSecureCtx::write_P(PGM_P buf, size_t size) { } size_t WiFiClientSecureCtx::write(Stream& stream) { - if (!connected() || !_handshake_done) { - DEBUG_BSSL("write: Connect/handshake not completed yet\n"); + if (!_engineConnected()) { + DEBUG_BSSL("write: no br_ssl engine to work with\n"); return 0; } + return stream.sendAll(this); } @@ -346,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; } @@ -366,10 +385,11 @@ int WiFiClientSecureCtx::read(uint8_t *buf, size_t size) { return to_copy; } - if (!conn) { + if (!engine) { DEBUG_BSSL("read: Not connected\n"); return -1; } + return 0; // If we're connected, no error but no read. } @@ -394,11 +414,10 @@ int WiFiClientSecureCtx::read() { if (1 == read(&c, 1)) { return c; } - DEBUG_BSSL("read: failed\n"); return -1; } -int WiFiClientSecureCtx::available() { +int WiFiClientSecureCtx::_pollRecvBuffer() { if (_recvapp_buf) { return _recvapp_len; // Anything from last call? } @@ -419,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; } @@ -439,7 +462,7 @@ size_t WiFiClientSecureCtx::peekBytes(uint8_t *buffer, size_t length) { } _startMillis = millis(); - while ((available() < (int) length) && ((millis() - _startMillis) < 5000)) { + while ((_pollRecvBuffer() < (int) length) && ((millis() - _startMillis) < 5000)) { yield(); } diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h index 73dc9e7337..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; @@ -188,7 +195,13 @@ class WiFiClientSecureCtx : public WiFiClient { unsigned char *_recvapp_buf; size_t _recvapp_len; + int _pollRecvBuffer(); // If there's a buffer with some pending data, return it's length + // If there's no buffer, poll the engine and store any received data there and return the length + // (which also may change the internal state, e.g. make us disconnected) + bool _clientConnected(); // Is the underlying socket alive? + bool _engineConnected(); // Are both socket and the bearssl engine alive? + std::shared_ptr _alloc_iobuf(size_t sz); void _freeSSL(); int _run_until(unsigned target, bool blocking = true); @@ -231,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()) { _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); } @@ -261,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); } @@ -329,6 +357,21 @@ class WiFiClientSecure : public WiFiClient { // consume bytes after use (see peekBuffer) virtual void peekConsume (size_t consume) override { return _ctx->peekConsume(consume); } + + void keepAlive(uint16_t idle_sec = TCP_DEFAULT_KEEPALIVE_IDLE_SEC, uint16_t intv_sec = TCP_DEFAULT_KEEPALIVE_INTERVAL_SEC, uint8_t count = TCP_DEFAULT_KEEPALIVE_COUNT) override + { + _ctx->keepAlive(idle_sec, intv_sec, count); + } + + bool isKeepAliveEnabled() const override { return _ctx->isKeepAliveEnabled(); }; + + uint16_t getKeepAliveIdle() const override { return _ctx->getKeepAliveIdle(); }; + + uint16_t getKeepAliveInterval() const override { return _ctx->getKeepAliveInterval(); }; + + uint8_t getKeepAliveCount() const override { return _ctx->getKeepAliveCount(); }; + + void disableKeepAlive() override { _ctx->disableKeepAlive(); }; private: std::shared_ptr _ctx; diff --git a/libraries/ESP8266WiFi/src/WiFiServer.cpp b/libraries/ESP8266WiFi/src/WiFiServer.cpp index 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 f061938385..b0f51b02f0 100644 --- a/libraries/ESP8266WiFi/src/WiFiServer.h +++ b/libraries/ESP8266WiFi/src/WiFiServer.h @@ -23,13 +23,14 @@ #define wifiserver_h extern "C" { - #include "wl_definitions.h" + #include struct tcp_pcb; } -#include "Server.h" -#include "IPAddress.h" +#include +#include +#include // lwIP-v2 backlog facility allows to keep memory safe by limiting the // maximum number of incoming *pending clients*. Default number of possibly @@ -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 3dba2f6cd2..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,6 +75,13 @@ 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, _tls_min, _tls_max); diff --git a/libraries/ESP8266WiFi/src/WiFiServerSecureBearSSL.h b/libraries/ESP8266WiFi/src/WiFiServerSecureBearSSL.h index fa89a1e696..25d0d380d9 100644 --- a/libraries/ESP8266WiFi/src/WiFiServerSecureBearSSL.h +++ b/libraries/ESP8266WiFi/src/WiFiServerSecureBearSSL.h @@ -65,7 +65,8 @@ class WiFiServerSecure : public WiFiServer { 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; 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 9e72eb66ca..fdf82217bc 100644 --- a/libraries/ESP8266WiFi/src/WiFiUdp.h +++ b/libraries/ESP8266WiFi/src/WiFiUdp.h @@ -47,6 +47,8 @@ class WiFiUDP : public UDP, public SList { // Finish with the UDP connection void stop() override; // join a multicast group and listen on the given port + virtual uint8_t beginMulticast(IPAddress interfaceAddr, uint16_t port); + // join a multicast group and listen on the given port, using a specific interface address uint8_t beginMulticast(IPAddress interfaceAddr, IPAddress multicast, uint16_t port); // Sending UDP packets @@ -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 index 5eb62eec15..a8589c4d25 100644 --- a/libraries/ESP8266WiFi/src/enable_wifi_at_boot_time.cpp +++ b/libraries/ESP8266WiFi/src/enable_wifi_at_boot_time.cpp @@ -21,7 +21,7 @@ extern "C" void __disableWiFiAtBootTime() // Does (almost) nothing: WiFi is enabled by default in nonos-sdk // ... but restores legacy WiFi credentials persistence to true at boot time - // (can be still overrisden by user before setting up WiFi, like before) + // (can be still overridden by user before setting up WiFi, like before) // (note: c++ ctors not called yet at this point) ESP8266WiFiClass::persistent(true); diff --git a/libraries/ESP8266WiFi/src/include/ClientContext.h b/libraries/ESP8266WiFi/src/include/ClientContext.h index a994b5cb1d..5c579c8848 100644 --- a/libraries/ESP8266WiFi/src/include/ClientContext.h +++ b/libraries/ESP8266WiFi/src/include/ClientContext.h @@ -26,11 +26,9 @@ class WiFiClient; typedef void (*discard_cb_t)(void*, ClientContext*); -extern "C" void esp_yield(); -extern "C" void esp_schedule(); - #include #include +#include bool getDefaultPrivateGlobalSyncValue (); @@ -145,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"); @@ -352,7 +347,7 @@ 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 @@ -458,9 +453,10 @@ 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(); } } @@ -487,11 +483,8 @@ class ClientContext } _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); @@ -561,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(); } } @@ -649,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; } diff --git a/libraries/ESP8266WiFi/src/include/UdpContext.h b/libraries/ESP8266WiFi/src/include/UdpContext.h index 1d3c6e418c..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; } @@ -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/examples/HelloEspnow/HelloEspnow.ino b/libraries/ESP8266WiFiMesh/examples/HelloEspnow/HelloEspnow.ino index 443d52e2bb..427084e312 100644 --- a/libraries/ESP8266WiFiMesh/examples/HelloEspnow/HelloEspnow.ino +++ b/libraries/ESP8266WiFiMesh/examples/HelloEspnow/HelloEspnow.ino @@ -1,4 +1,4 @@ -#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. +#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 @@ -19,26 +19,23 @@ namespace TypeCast = MeshTypeConversionFunctions; 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. +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 - }; +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 +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); @@ -61,7 +58,7 @@ String manageRequest(const String &request, MeshBackendBase &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. + (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!: ")); @@ -174,7 +171,7 @@ bool broadcastFilter(String &firstTransmission, EspnowMeshBackend &meshInstance) String targetMeshName = firstTransmission.substring(0, metadataEndIndex); if (!targetMeshName.isEmpty() && meshInstance.getMeshName() != targetMeshName) { - return false; // Broadcast is for another mesh network + 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. @@ -198,7 +195,7 @@ bool broadcastFilter(String &firstTransmission, EspnowMeshBackend &meshInstance) 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. + (void)meshInstance; // This is useful to remove a "unused parameter" compiler warning. Does nothing else. return true; } @@ -221,7 +218,7 @@ bool exampleTransmissionOutcomesUpdateHook(MeshBackendBase &meshInstance) { 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)transmissionSuccessful; // This is useful to remove a "unused parameter" compiler warning. Does nothing else. (void)response; (void)recipientMac; (void)responseIndex; @@ -281,8 +278,8 @@ void setup() { // 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); + // 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; @@ -293,10 +290,10 @@ void loop() { // 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. + // 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. + 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(); @@ -310,9 +307,7 @@ void loop() { espnowDelay(100); // One way to check how attemptTransmission worked out - if (espnowNode.latestTransmissionSuccessful()) { - Serial.println(F("Transmission successful.")); - } + if (espnowNode.latestTransmissionSuccessful()) { Serial.println(F("Transmission successful.")); } // Another way to check how attemptTransmission worked out if (espnowNode.latestTransmissionOutcomes().empty()) { @@ -343,20 +338,20 @@ void loop() { 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). + 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'}; + 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. + espnowDelay(100); // Wait for response. Serial.println(F("\nPerforming encrypted ESP-NOW transmissions.")); - uint8_t targetBSSID[6] {0}; + 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) { @@ -369,7 +364,7 @@ void loop() { 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. + 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. @@ -383,7 +378,7 @@ void loop() { 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. + 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! @@ -392,7 +387,7 @@ void loop() { 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. + espnowDelay(100); // Wait for response. Serial.println(); // If we want to remove the encrypted connection on both nodes, we can do it like this. @@ -403,7 +398,7 @@ void loop() { 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. + espnowDelay(100); // Wait for response. Serial.println(); @@ -423,7 +418,7 @@ void loop() { 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. + 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. @@ -432,9 +427,9 @@ void loop() { espnowMessage = F("This message is always encrypted, regardless of receiver."); Serial.println(String(F("\nTransmitting: ")) + espnowMessage); espnowNode.attemptAutoEncryptingTransmission(espnowMessage); - espnowDelay(100); // Wait for response. + espnowDelay(100); // Wait for response. } else { - Serial.println(String(F("Ooops! Encrypted connection removal failed. Status: ")) + String(static_cast(removalOutcome))); + 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 @@ -445,8 +440,7 @@ void loop() { } // 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('.')); + 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 3ae8567e16..4129882acf 100644 --- a/libraries/ESP8266WiFiMesh/examples/HelloMesh/HelloMesh.ino +++ b/libraries/ESP8266WiFiMesh/examples/HelloMesh/HelloMesh.ino @@ -8,7 +8,7 @@ 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. +#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 @@ -28,18 +28,16 @@ namespace TypeCast = MeshTypeConversionFunctions; 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. +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 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 - }; +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 }; bool meshMessageHandler(String &message, FloodingMesh &meshInstance); @@ -49,7 +47,7 @@ FloodingMesh floodingMesh = FloodingMesh(meshMessageHandler, FPSTR(exampleWiFiPa bool theOne = true; String theOneMac; -bool useLED = false; // Change this to true if you wish the onboard LED to mark The One. +bool useLED = false; // Change this to true if you wish the onboard LED to mark The One. /** Callback for when a message is received from the mesh network. @@ -78,7 +76,7 @@ bool meshMessageHandler(String &message, FloodingMesh &meshInstance) { if (useLED && !theOne) { bool ledState = message.charAt(1) == '1'; - digitalWrite(LED_BUILTIN, ledState); // Turn LED on/off (LED_BUILTIN is active low) + digitalWrite(LED_BUILTIN, ledState); // Turn LED on/off (LED_BUILTIN is active low) } return true; @@ -87,26 +85,22 @@ bool meshMessageHandler(String &message, FloodingMesh &meshInstance) { } } 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. + uint32_t totalBroadcasts = strtoul(message.c_str(), nullptr, 0); // strtoul stops reading input when an invalid character is discovered. // Static variables are only initialized once. static uint32_t firstBroadcast = totalBroadcasts; - 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. + 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++; - missedBroadcasts += totalBroadcasts - previousTotalBroadcasts - 1; // We expect an increment by 1. + missedBroadcasts += totalBroadcasts - previousTotalBroadcasts - 1; // We expect an increment by 1. previousTotalBroadcasts = totalBroadcasts; - 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)); - } + 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 { @@ -136,24 +130,24 @@ void setup() { Serial.println(F("Setting up mesh node...")); floodingMesh.begin(); - floodingMesh.activateAP(); // Required to receive messages + floodingMesh.activateAP(); // Required to receive messages - uint8_t apMacArray[6] {0}; + 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) + 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); + // 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 + floodingMeshDelay(5000); // Give some time for user to start the nodes } int32_t timeOfLastProclamation = -10000; @@ -177,7 +171,7 @@ void loop() { 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. + 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.")); @@ -187,7 +181,7 @@ void loop() { floodingMeshDelay(20); } - if (millis() - loopStart > 23000) { // Start benchmarking the mesh once three proclamations have been made + 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.")); diff --git a/libraries/ESP8266WiFiMesh/examples/HelloTcpIp/HelloTcpIp.ino b/libraries/ESP8266WiFiMesh/examples/HelloTcpIp/HelloTcpIp.ino index f68b81f181..f59ce2a753 100644 --- a/libraries/ESP8266WiFiMesh/examples/HelloTcpIp/HelloTcpIp.ino +++ b/libraries/ESP8266WiFiMesh/examples/HelloTcpIp/HelloTcpIp.ino @@ -1,4 +1,4 @@ -#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. +#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 @@ -20,7 +20,7 @@ namespace TypeCast = MeshTypeConversionFunctions; 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. +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; @@ -45,7 +45,7 @@ String manageRequest(const String &request, MeshBackendBase &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. + (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!: ")); @@ -145,8 +145,7 @@ bool exampleTransmissionOutcomesUpdateHook(MeshBackendBase &meshInstance) { 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('.')); + meshInstance.setMessage(String(F("Hello world request #")) + String(++requestNumber) + F(" from ") + meshInstance.getMeshName() + meshInstance.getNodeID() + String('.')); } } else { Serial.println(F("Invalid mesh backend!")); @@ -174,8 +173,8 @@ void setup() { /* 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. + 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. @@ -186,17 +185,15 @@ void setup() { 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. + 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.")); - } + if (tcpIpNode.latestTransmissionSuccessful()) { Serial.println(F("Transmission successful.")); } // Another way to check how attemptTransmission worked out if (tcpIpNode.latestTransmissionOutcomes().empty()) { diff --git a/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.cpp b/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.cpp index 5775fa3009..81cffe48eb 100644 --- a/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.cpp +++ b/libraries/ESP8266WiFiMesh/src/ESP8266WiFiMesh.cpp @@ -624,7 +624,7 @@ void ESP8266WiFiMesh::acceptRequest() if(_handler != NULL) { while (true) { - _client = _server.available(); + _client = _server.accept(); if (!_client) break; @@ -647,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; diff --git a/libraries/ESP8266WiFiMesh/src/TcpIpMeshBackend.cpp b/libraries/ESP8266WiFiMesh/src/TcpIpMeshBackend.cpp index 6aebe46ba0..1e3c285c17 100644 --- a/libraries/ESP8266WiFiMesh/src/TcpIpMeshBackend.cpp +++ b/libraries/ESP8266WiFiMesh/src/TcpIpMeshBackend.cpp @@ -533,7 +533,7 @@ void TcpIpMeshBackend::acceptRequests() } while (true) { - WiFiClient _client = _server.available(); + WiFiClient _client = _server.accept(); if (!_client) break; 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 f3d67b40ea..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) @@ -85,6 +83,16 @@ HTTPUpdateResult ESP8266HTTPUpdate::update(WiFiClient& client, const String& hos return handleUpdate(http, currentVersion, false); } +HTTPUpdateResult ESP8266HTTPUpdate::update(HTTPClient& httpClient, const String& currentVersion) +{ + return handleUpdate(httpClient, currentVersion, false); +} + +HTTPUpdateResult ESP8266HTTPUpdate::updateFS(HTTPClient& httpClient, const String& currentVersion) +{ + return handleUpdate(httpClient, currentVersion, true); +} + /** * return error code as int * @return int error code @@ -210,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() ); @@ -228,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; @@ -250,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(); @@ -286,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); @@ -297,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(); @@ -413,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 c825d8abab..28e90bad23 100755 --- a/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h +++ b/libraries/ESP8266httpUpdate/src/ESP8266httpUpdate.h @@ -43,16 +43,18 @@ #endif /// note we use HTTP client errors too so we start at 100 -//TODO - in v3.0.0 make this an enum -constexpr int HTTP_UE_TOO_LESS_SPACE = (-100); -constexpr int HTTP_UE_SERVER_NOT_REPORT_SIZE = (-101); -constexpr int HTTP_UE_SERVER_FILE_NOT_FOUND = (-102); -constexpr int HTTP_UE_SERVER_FORBIDDEN = (-103); -constexpr int HTTP_UE_SERVER_WRONG_HTTP_CODE = (-104); -constexpr int HTTP_UE_SERVER_FAULTY_MD5 = (-105); -constexpr int HTTP_UE_BIN_VERIFY_HEADER_FAILED = (-106); -constexpr int HTTP_UE_BIN_FOR_WRONG_FLASH = (-107); -constexpr int HTTP_UE_SERVER_UNAUTHORIZED = (-108); +enum HTTPUpdateError { + HTTP_UE_SERVER_NOT_REPORT_VERSION = -109, // server did not respond with a firmware version + HTTP_UE_SERVER_UNAUTHORIZED, // -108 + HTTP_UE_BIN_FOR_WRONG_FLASH, // -107 + HTTP_UE_BIN_VERIFY_HEADER_FAILED, // -106 + HTTP_UE_SERVER_FAULTY_MD5, // -105 + HTTP_UE_SERVER_WRONG_HTTP_CODE, // -104 + HTTP_UE_SERVER_FORBIDDEN, // -103 + HTTP_UE_SERVER_FILE_NOT_FOUND, // -102 + HTTP_UE_SERVER_NOT_REPORT_SIZE, // -101 + HTTP_UE_TOO_LESS_SPACE // -100 +}; enum HTTPUpdateResult { HTTP_UPDATE_FAILED, @@ -108,6 +110,11 @@ class ESP8266HTTPUpdate _ledOn = ledOn; } + void setMD5sum(const String &md5Sum) + { + _md5Sum = md5Sum; + } + void setAuthorization(const String& user, const String& password); void setAuthorization(const String& auth); @@ -115,6 +122,10 @@ class ESP8266HTTPUpdate t_httpUpdate_return update(WiFiClient& client, const String& host, uint16_t port, const String& uri = "/", const String& currentVersion = ""); t_httpUpdate_return updateFS(WiFiClient& client, const String& url, const String& currentVersion = ""); + t_httpUpdate_return update(HTTPClient& httpClient, const String& currentVersion = ""); + t_httpUpdate_return updateFS(HTTPClient& httpClient, const String& currentVersion = ""); + + t_httpUpdate_return getAvailableVersion(WiFiClient& client, const String& host, uint16_t port, const String& uri, const String& current_version, String& available_version); // Notification callbacks void onStart(HTTPUpdateStartCB cbOnStart) { _cbStart = cbOnStart; } @@ -125,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); @@ -142,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 f0a9de1225..6234e9f109 100644 --- a/libraries/ESP8266mDNS/examples/LEAmDNS/mDNS_Clock/mDNS_Clock.ino +++ b/libraries/ESP8266mDNS/examples/LEAmDNS/mDNS_Clock/mDNS_Clock.ino @@ -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; // 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 +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; } @@ -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 17b29ec57c..1773b590b0 100644 --- a/libraries/ESP8266mDNS/examples/LEAmDNS/mDNS_ServiceMonitor/mDNS_ServiceMonitor.ino +++ b/libraries/ESP8266mDNS/examples/LEAmDNS/mDNS_ServiceMonitor/mDNS_ServiceMonitor.ino @@ -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; // 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 +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 87d03900f3..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. @@ -81,9 +81,7 @@ bool loadConfig(String *ssid, String *pass) { 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/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 777a4cb4c3..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, thoroughly 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 information. - 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 4908a67e06..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,1345 +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(); -} - -/* - 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(); - } + /** + INTERFACE + */ - 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(); - _releaseServices(); - _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; + } - bool bResult = false; + /* + MDNSResponder::close - if (_setHostname(p_pcHostname)) + Ends the MDNS responder. + Announced services are unannounced (by multicasting a goodbye message) + + */ + 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 - hMDNSTxt hTxt = 0; - stcMDNSService* pService = _findService(p_hService); - if (pService) + Add a static service TXT item ('Key'='Value') to a service. + + */ + 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) + /* + 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 (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) -{ - - 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::setDynamicServiceTxtCallback (global) - - Set a global callback for dynamic service TXT items. The callback is called, whenever - service TXT items are needed. - -*/ -bool MDNSResponder::setDynamicServiceTxtCallback(MDNSResponder::MDNSDynamicServiceTxtCallbackFunc p_fnCallback) -{ - - m_fnServiceTxtCallback = p_fnCallback; - - return true; -} - -/* - 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. - -*/ -bool MDNSResponder::setDynamicServiceTxtCallback(MDNSResponder::hMDNSService p_hService, - MDNSResponder::MDNSDynamicServiceTxtCallbackFunc p_fnCallback) -{ - - bool bResult = false; - - stcMDNSService* pService = _findService(p_hService); - if (pService) + /* + MDNSResponder::addServiceTxt (LEGACY) + */ + bool MDNSResponder::addServiceTxt(const char* p_pcService, const char* p_pcProtocol, + const char* p_pcKey, const char* p_pcValue) { - pService->m_fnTxtCallback = p_fnCallback; - - bResult = true; + return (0 + != _addServiceTxt(_findService(m_pcHostname, p_pcService, p_pcProtocol), p_pcKey, + p_pcValue, false)); } - 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);); - - hMDNSTxt hTxt = 0; - - stcMDNSService* pService = _findService(p_hService); - if (pService) + /* + MDNSResponder::addServiceTxt (LEGACY) + */ + bool MDNSResponder::addServiceTxt(const String& p_strService, const String& p_strProtocol, + const String& p_strKey, const String& p_strValue) { - hTxt = _addServiceTxt(pService, p_pcKey, p_pcValue, true); + return (0 + != _addServiceTxt( + _findService(m_pcHostname, p_strService.c_str(), p_strProtocol.c_str()), + p_strKey.c_str(), p_strValue.c_str(), false)); } - 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) -{ - - char acBuffer[32]; *acBuffer = 0; - sprintf(acBuffer, "%u", p_u32Value); - - return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); -} - -/* - 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); - - 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) -{ - - char acBuffer[8]; *acBuffer = 0; - sprintf(acBuffer, "%hhu", p_u8Value); - - return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); -} - -/* - 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); + /* + MDNSResponder::setDynamicServiceTxtCallback (global) - return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); -} + Set a global callback for dynamic service TXT items. The callback is called, whenever + service TXT items are needed. -/* - 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); - - return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); -} - -/* - 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); -} - - -/** - 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) + */ + bool MDNSResponder::setDynamicServiceTxtCallback( + MDNSResponder::MDNSDynamicServiceTxtCallbackFunc p_fnCallback) { - // safeguard against misuse - return 0; + m_fnServiceTxtCallback = p_fnCallback; + + return true; } - DEBUG_EX_INFO(DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] queryService '%s.%s'\n"), p_pcService, p_pcProtocol);); + /* + MDNSResponder::setDynamicServiceTxtCallback (service specific) - uint32_t u32Result = 0; + Set a service specific callback for dynamic service TXT items. The callback is called, + whenever service TXT items are needed for the given service. - 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))) + */ + bool MDNSResponder::setDynamicServiceTxtCallback( + MDNSResponder::hMDNSService p_hService, + MDNSResponder::MDNSDynamicServiceTxtCallbackFunc p_fnCallback) { + bool bResult = false; - pServiceQuery->m_bLegacyQuery = true; - - if (_sendMDNSServiceQuery(*pServiceQuery)) + stcMDNSService* pService = _findService(p_hService); + if (pService) { - // 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); + pService->m_fnTxtCallback = p_fnCallback; - // 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); + bResult = true; } + DEBUG_EX_ERR(if (!bResult) { + DEBUG_OUTPUT.printf_P(PSTR("[MDNSResponder] setDynamicServiceTxtCallback: FAILED!\n")); + }); + return bResult; } - 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)) + /* + 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);); - char* pcHostDomain = pSQAnswer->allocHostDomain(pSQAnswer->m_HostDomain.c_strLength()); - if (pcHostDomain) + hMDNSTxt hTxt = 0; + + stcMDNSService* pService = _findService(p_hService); + if (pService) { - pSQAnswer->m_HostDomain.c_str(pcHostDomain); + 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; } - 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()); -} -#endif + /* + MDNSResponder::addDynamicServiceTxt (uint32_t) + */ + MDNSResponder::hMDNSTxt + MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, const char* p_pcKey, + uint32_t p_u32Value) + { + char acBuffer[32]; + *acBuffer = 0; + sprintf(acBuffer, "%u", p_u32Value); -/* - MDNSResponder::answerPort -*/ -uint16_t MDNSResponder::answerPort(const uint32_t p_u32AnswerIndex) -{ + return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); + } - const stcMDNSServiceQuery* pServiceQuery = _findLegacyServiceQuery(); - const stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - return (pSQAnswer ? pSQAnswer->m_u16Port : 0); -} + /* + 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); -/* - MDNSResponder::hostname (LEGACY) -*/ -String MDNSResponder::hostname(const uint32_t p_u32AnswerIndex) -{ + return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); + } - return String(answerHostname(p_u32AnswerIndex)); -} + /* + MDNSResponder::addDynamicServiceTxt (uint8_t) + */ + MDNSResponder::hMDNSTxt + MDNSResponder::addDynamicServiceTxt(MDNSResponder::hMDNSService p_hService, const char* p_pcKey, + uint8_t p_u8Value) + { + char acBuffer[8]; + *acBuffer = 0; + sprintf(acBuffer, "%hhu", p_u8Value); -/* - MDNSResponder::IP (LEGACY) -*/ -IPAddress MDNSResponder::IP(const uint32_t p_u32AnswerIndex) -{ + return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); + } - return answerIP(p_u32AnswerIndex); -} + /* + 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); -/* - MDNSResponder::port (LEGACY) -*/ -uint16_t MDNSResponder::port(const uint32_t p_u32AnswerIndex) -{ + return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); + } - return answerPort(p_u32AnswerIndex); -} + /* + 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); + return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); + } -/** - DYNAMIC SERVICE QUERY -*/ + /* + 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); -/* - MDNSResponder::installServiceQuery + return addDynamicServiceTxt(p_hService, p_pcKey, acBuffer); + } - 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 + /** + STATIC SERVICE QUERY (LEGACY) + */ -*/ -MDNSResponder::hMDNSServiceQuery MDNSResponder::installServiceQuery(const char* p_pcService, - const char* p_pcProtocol, - MDNSResponder::MDNSServiceQueryCallbackFunc p_fnCallback) -{ - hMDNSServiceQuery hResult = 0; + /* + MDNSResponder::queryService - 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))) + 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);); - pServiceQuery->m_fnCallback = p_fnCallback; - pServiceQuery->m_bLegacyQuery = false; + uint32_t u32Result = 0; - if (_sendMDNSServiceQuery(*pServiceQuery)) + 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_u8SentCount = 1; - pServiceQuery->m_ResendTimeout.reset(MDNS_DYNAMIC_QUERY_RESEND_DELAY); + pServiceQuery->m_bLegacyQuery = true; - hResult = (hMDNSServiceQuery)pServiceQuery; + 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 { - _removeServiceQuery(pServiceQuery); + DEBUG_EX_ERR(DEBUG_OUTPUT.printf_P( + PSTR("[MDNSResponder] queryService: INVALID input data!\n"));); } + return u32Result; } - 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 + /* + MDNSResponder::removeQuery - Remove a dynamic service query (and all collected answers) from the MDNS responder + Remove the last static service query (and all answers). -*/ -bool MDNSResponder::removeServiceQuery(MDNSResponder::hMDNSServiceQuery p_hServiceQuery) -{ + */ + bool MDNSResponder::removeQuery(void) + { + return _removeLegacyServiceQuery(); + } - 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::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::answerCount -*/ -uint32_t MDNSResponder::answerCount(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery) -{ + /* + MDNSResponder::answerHostname + */ + const char* MDNSResponder::answerHostname(const uint32_t p_u32AnswerIndex) + { + stcMDNSServiceQuery* pServiceQuery = _findLegacyServiceQuery(); + stcMDNSServiceQuery::stcAnswer* pSQAnswer + = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - return (pServiceQuery ? pServiceQuery->answerCount() : 0); -} + if ((pSQAnswer) && (pSQAnswer->m_HostDomain.m_u16NameLength) + && (!pSQAnswer->m_pcHostDomain)) + { + char* pcHostDomain = pSQAnswer->allocHostDomain(pSQAnswer->m_HostDomain.c_strLength()); + if (pcHostDomain) + { + pSQAnswer->m_HostDomain.c_str(pcHostDomain); + } + } + return (pSQAnswer ? pSQAnswer->m_pcHostDomain : 0); + } -std::vector MDNSResponder::answerInfo(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery) -{ - std::vector tempVector; - for (uint32_t i = 0; i < answerCount(p_hServiceQuery); i++) +#ifdef MDNS_IP4_SUPPORT + /* + MDNSResponder::answerIP + */ + IPAddress MDNSResponder::answerIP(const uint32_t p_u32AnswerIndex) { - tempVector.emplace_back(*this, p_hServiceQuery, i); + 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 tempVector; -} +#endif -/* - MDNSResponder::answerServiceDomain +#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()); + } +#endif - Returns the domain for the given service. - If not already existing, the string is allocated, filled and attached to the answer. + /* + 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); + } -*/ -const char* MDNSResponder::answerServiceDomain(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex) -{ + /* + MDNSResponder::hostname (LEGACY) + */ + String MDNSResponder::hostname(const uint32_t p_u32AnswerIndex) + { + return String(answerHostname(p_u32AnswerIndex)); + } - 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)) + /* + MDNSResponder::IP (LEGACY) + */ + IPAddress MDNSResponder::IP(const uint32_t p_u32AnswerIndex) { + return answerIP(p_u32AnswerIndex); + } - pSQAnswer->m_pcServiceDomain = pSQAnswer->allocServiceDomain(pSQAnswer->m_ServiceDomain.c_strLength()); - if (pSQAnswer->m_pcServiceDomain) - { - pSQAnswer->m_ServiceDomain.c_str(pSQAnswer->m_pcServiceDomain); - } + /* + MDNSResponder::port (LEGACY) + */ + uint16_t MDNSResponder::port(const uint32_t p_u32AnswerIndex) + { + return answerPort(p_u32AnswerIndex); } - return (pSQAnswer ? pSQAnswer->m_pcServiceDomain : 0); -} -/* - MDNSResponder::hasAnswerHostDomain -*/ -bool MDNSResponder::hasAnswerHostDomain(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex) -{ + /** + 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; - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - return ((pSQAnswer) && - (pSQAnswer->m_u32ContentFlags & ServiceQueryAnswerType_HostDomainAndPort)); -} + 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_fnCallback = p_fnCallback; + pServiceQuery->m_bLegacyQuery = false; -/* - MDNSResponder::answerHostDomain + 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; + } - Returns the host domain for the given service. - If not already existing, the string is allocated, filled and attached to the answer. + /* + MDNSResponder::removeServiceQuery -*/ -const char* MDNSResponder::answerHostDomain(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex) -{ + Remove a dynamic service query (and all collected answers) from the MDNS responder - 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)) + */ + 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) + { + stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); + return (pServiceQuery ? pServiceQuery->answerCount() : 0); + } - pSQAnswer->m_pcHostDomain = pSQAnswer->allocHostDomain(pSQAnswer->m_HostDomain.c_strLength()); - if (pSQAnswer->m_pcHostDomain) + std::vector + MDNSResponder::answerInfo(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery) + { + std::vector tempVector; + for (uint32_t i = 0; i < answerCount(p_hServiceQuery); i++) { - pSQAnswer->m_HostDomain.c_str(pSQAnswer->m_pcHostDomain); + tempVector.emplace_back(*this, p_hServiceQuery, i); } + return tempVector; } - 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) -{ + /* + MDNSResponder::answerServiceDomain - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - return (pSQAnswer ? pSQAnswer->IP4AddressCount() : 0); -} + Returns the domain for the given service. + If not already existing, the string is allocated, filled and attached to the answer. -/* - MDNSResponder::answerIP4Address -*/ -IPAddress MDNSResponder::answerIP4Address(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex, - const uint32_t p_u32AddressIndex) -{ + */ + const char* + MDNSResponder::answerServiceDomain(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex) + { + 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_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); + } - 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 + /* + 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)); + } -#ifdef MDNS_IP6_SUPPORT -/* - MDNSResponder::hasAnswerIP6Address -*/ -bool MDNSResponder::hasAnswerIP6Address(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex) -{ + /* + MDNSResponder::answerHostDomain - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - return ((pSQAnswer) && - (pSQAnswer->m_u32ContentFlags & ServiceQueryAnswerType_HostIP6Address)); -} + Returns the host domain for the given service. + If not already existing, the string is allocated, filled and attached to the answer. -/* - MDNSResponder::answerIP6AddressCount -*/ -uint32_t MDNSResponder::answerIP6AddressCount(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex) -{ + */ + const char* + MDNSResponder::answerHostDomain(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, + const uint32_t p_u32AnswerIndex) + { + 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_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); + } - stcMDNSServiceQuery* pServiceQuery = _findServiceQuery(p_hServiceQuery); - stcMDNSServiceQuery::stcAnswer* pSQAnswer = (pServiceQuery ? pServiceQuery->answerAtIndex(p_u32AnswerIndex) : 0); - return (pSQAnswer ? pSQAnswer->IP6AddressCount() : 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::answerIP6Address -*/ -IPAddress MDNSResponder::answerIP6Address(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::stcIP6Address* pIP6Address = (pSQAnswer ? pSQAnswer->IP6AddressAtIndex(p_u32AddressIndex) : 0); - return (pIP6Address ? pIP6Address->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 -/* - 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)); -} +#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::answerPort -*/ -uint16_t MDNSResponder::answerPort(const MDNSResponder::hMDNSServiceQuery p_hServiceQuery, - const uint32_t p_u32AnswerIndex) -{ + /* + 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); - return (pSQAnswer ? pSQAnswer->m_u16Port : 0); -} + /* + 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::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 allocated, 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) - 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. + /* + PROBING + */ -*/ -bool MDNSResponder::setHostProbeResultCallback(MDNSResponder::MDNSHostProbeFn p_fnCallback) -{ + /* + MDNSResponder::setProbeResultCallback - m_HostProbeInformation.m_fnHostProbeResultCallback = p_fnCallback; + 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. - return true; -} - -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 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. + 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; + + stcMDNSService* pService = _findService(p_hService); + if (pService) + { + pService->m_ProbeInformation.m_fnServiceProbeResultCallback = p_fnCallback; - bResult = true; + 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 1c099c68ab..3fc4dd98da 100644 --- a/libraries/ESP8266mDNS/src/LEAmDNS.h +++ b/libraries/ESP8266mDNS/src/LEAmDNS.h @@ -11,15 +11,19 @@ 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 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); - } +#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 compatibility - 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: 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); - - 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 6fc4e3cd01..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 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 + // 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 ourselves -> 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 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 + // 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 ourselves -> 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 authoritative 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 authoritative 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 60087c16d8..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 predecessor + 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 existence) + /* + 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 predecessor + 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 49fc5bb608..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 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 */ @@ -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 85845d8faa..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. - Comparison ((all A TXT items in B and equal) AND (all B TXT items in A and equal)) is supported. - -*/ - -/* - MDNSResponder::stcMDNSServiceTxts::stcMDNSServiceTxts constructor -*/ -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 predecessor, 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 header. - -*/ - -/* - 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 - reset 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 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. - - 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 predecessor, 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 predecessor, 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 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(); -} + /* + 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 predecessor, 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 0398d9a6ee..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 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. + /* + 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 authoritative 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 known 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)) // Everything is 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 (might 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/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; + /* + 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))) && // 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; + /* + 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/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 index 713440d73f..18f1cad1f9 100644 --- a/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino +++ b/libraries/I2S/examples/InputSerialPlotter/InputSerialPlotter.ino @@ -15,13 +15,13 @@ void setup() { // on non-native USB ports Serial.begin(115200); while (!Serial) { - ; // wait for serial port to connect. Needed for native USB port only + ; // 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 + while (1); // do nothing } } diff --git a/libraries/I2S/examples/SimpleTone/SimpleTone.ino b/libraries/I2S/examples/SimpleTone/SimpleTone.ino index 6959ae74ba..e2dcb7b3f6 100644 --- a/libraries/I2S/examples/SimpleTone/SimpleTone.ino +++ b/libraries/I2S/examples/SimpleTone/SimpleTone.ino @@ -10,13 +10,13 @@ #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 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 +const int halfWavelength = (sampleRate / frequency); // half wavelength of square wave -short sample = amplitude; // current sample value +short sample = amplitude; // current sample value int count = 0; void setup() { @@ -26,7 +26,7 @@ void setup() { // start I2S at the sample rate with 16-bits per sample if (!I2S.begin(I2S_PHILIPS_MODE, sampleRate, 16)) { Serial.println("Failed to initialize I2S!"); - while (1); // do nothing + while (1); // do nothing } } @@ -43,4 +43,3 @@ void loop() { // increment the counter for the next sample count++; } - 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/src/LittleFS.h b/libraries/LittleFS/src/LittleFS.h index 65d70aac32..d5203ae6e0 100644 --- a/libraries/LittleFS/src/LittleFS.h +++ b/libraries/LittleFS/src/LittleFS.h @@ -167,6 +167,14 @@ class LittleFSImpl : public FSImpl return false; } int rc = lfs_mkdir(&_lfs, path); + if ((rc == 0) && _timeCallback) { + time_t now = _timeCallback(); + // Add metadata with creation time to the directory marker + int rc = lfs_setattr(&_lfs, path, 'c', (const void *)&now, sizeof(now)); + if (rc < 0) { + DEBUGV("Unable to set creation time on '%s' to %ld\n", path, (long)now); + } + } return (rc==0); } @@ -241,7 +249,7 @@ class LittleFSImpl : public FSImpl DEBUGV("lfs_format, lfs_setattr 't': rc=%d\n", rc); return false; } - + lfs_unmount(&_lfs); _mounted = false; } @@ -372,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; @@ -467,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/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/src/Netdump.cpp b/libraries/Netdump/src/Netdump.cpp index 3fcf79298b..6cee79a646 100644 --- a/libraries/Netdump/src/Netdump.cpp +++ b/libraries/Netdump/src/Netdump.cpp @@ -23,7 +23,6 @@ #include #include "Schedule.h" - namespace NetCapture { @@ -69,24 +68,26 @@ void Netdump::reset() void Netdump::printDump(Print& out, Packet::PacketDetail ndd, const Filter nf) { out.printf_P(PSTR("netDump starting\r\n")); - setCallback([&out, ndd, this](const Packet & ndp) - { - printDumpProcess(out, ndd, ndp); - }, nf); + 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); } -bool Netdump::tcpDump(WiFiServer &tcpDumpServer, const Filter nf) +bool Netdump::tcpDump(WiFiServer& tcpDumpServer, const Filter nf) { - if (!packetBuffer) { packetBuffer = new (std::nothrow) char[tcpBufferSize]; @@ -98,10 +99,11 @@ bool Netdump::tcpDump(WiFiServer &tcpDumpServer, const Filter nf) } bufferIndex = 0; - schedule_function([&tcpDumpServer, this, nf]() - { - tcpDumpLoop(tcpDumpServer, nf); - }); + schedule_function( + [&tcpDumpServer, this, nf]() + { + tcpDumpLoop(tcpDumpServer, nf); + }); return true; } @@ -109,7 +111,8 @@ void Netdump::capture(int netif_idx, const char* data, size_t len, int out, int { 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. } } @@ -118,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; } @@ -131,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); @@ -145,7 +148,7 @@ void Netdump::printDumpProcess(Print& out, Packet::PacketDetail ndd, const Packe 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; @@ -154,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); } @@ -168,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; } @@ -189,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()) { @@ -216,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 0e8b6cbb00..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); - bool 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; - int 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 56936c744a..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; @@ -253,7 +252,7 @@ size_t NetdumpIP::printTo(Print& p) { 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 bb4e09920c..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) { @@ -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,20 +248,36 @@ 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_P(PSTR("\r\n")); @@ -260,18 +287,42 @@ 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/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 eb9372b2a8..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 MS-DOS 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 2bc1dc972c..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"); @@ -156,7 +153,7 @@ class SDFSImpl : public FSImpl 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()); @@ -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/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/src/Servo.cpp b/libraries/Servo/src/Servo.cpp index 69ed476d84..1d19f62683 100644 --- a/libraries/Servo/src/Servo.cpp +++ b/libraries/Servo/src/Servo.cpp @@ -58,7 +58,7 @@ Servo::~Servo() { uint8_t Servo::attach(int pin) { - return attach(pin, DEFAULT_MIN_PULSE_WIDTH, DEFAULT_MAX_PULSE_WIDTH); + return attach(pin, _minUs, _maxUs); } uint8_t Servo::attach(int pin, uint16_t minUs, uint16_t maxUs) @@ -94,7 +94,6 @@ void Servo::detach() delay(REFRESH_INTERVAL / 1000); // long enough to complete active period under all circumstances. stopWaveform(_pin); _attached = false; - _valueUs = DEFAULT_NEUTRAL_PULSE_WIDTH; } } diff --git a/libraries/Servo/src/Servo.h b/libraries/Servo/src/Servo.h index 9efb7b2c0d..d40939c2aa 100644 --- a/libraries/Servo/src/Servo.h +++ b/libraries/Servo/src/Servo.h @@ -29,7 +29,8 @@ // // attach(pin) - Attaches a servo motor to an i/o pin. // attach(pin, min, max) - Attaches to a pin setting min and max values in microseconds -// default min is 1000, max is 2000 +// attach(pin, min, max, value) - Attaches to a pin setting min, max, and current values in microseconds +// default min is 1000, max is 2000, and value is 1500. // // write() - Sets the servo angle in degrees. (invalid angle that is valid as pulse in microseconds is treated as microseconds) // writeMicroseconds() - Sets the servo pulse width in microseconds diff --git a/libraries/SoftwareSerial b/libraries/SoftwareSerial index 566809f3de..bcfd6d10e6 160000 --- a/libraries/SoftwareSerial +++ b/libraries/SoftwareSerial @@ -1 +1 @@ -Subproject commit 566809f3de9de551ce1f2dedd3f7417c8b2b7e79 +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 dc13088b9f..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 coordinates + // 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 49d39fee3b..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 height -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) { + if (!bmpFile) { Serial.println("didn't find image"); while (1); } - if (! bmpReadHeader(bmpFile)) { + if (!bmpReadHeader(bmpFile)) { Serial.println("bad bmp"); return; } @@ -80,7 +81,6 @@ void loop() { delay(1000); } - } /*********************************************/ @@ -90,15 +90,15 @@ void loop() { // 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 03423a4ef5..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 height -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!"); @@ -153,12 +138,12 @@ void loop() { bmpFile = SD.open("pfvm_1.bmp"); - if (! bmpFile) { + if (!bmpFile) { Serial.println("didn't find image"); while (1); } - if (! bmpReadHeader(bmpFile)) { + if (!bmpReadHeader(bmpFile)) { Serial.println("bad bmp"); return; } @@ -176,12 +161,12 @@ void loop() { // 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 8263c0bb56..0c49eaf4ff 100644 --- a/libraries/Ticker/examples/TickerParameter/TickerParameter.ino +++ b/libraries/Ticker/examples/TickerParameter/TickerParameter.ino @@ -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 fa520722f6..f4bdfb022d 100644 --- a/libraries/esp8266/examples/CheckFlashCRC/CheckFlashCRC.ino +++ b/libraries/esp8266/examples/CheckFlashCRC/CheckFlashCRC.ino @@ -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 index 1f192dd01c..5ad731db3c 100644 --- a/libraries/esp8266/examples/FadePolledTimeout/FadePolledTimeout.ino +++ b/libraries/esp8266/examples/FadePolledTimeout/FadePolledTimeout.ino @@ -39,17 +39,15 @@ void setup() { // PWM-Locked generator: https://github.com/esp8266/Arduino/pull/7231 enablePhaseLockedWaveform(); - pinMode(LED_BUILTIN, OUTPUT); // Initialize the LED_BUILTIN pin as an output + 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 + 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 + digitalWrite(LED_BUILTIN, LOW); // Turn the LED on (Note that LOW is the voltage level oneShotMs timeoutOn(2000); - while (!timeoutOn) { - yield(); - } + while (!timeoutOn) { yield(); } stepPeriod.reset(); } 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 index ff1c41e93b..f9c692ccf0 100644 --- a/libraries/esp8266/examples/HwdtStackDump/HwdtStackDump.ino +++ b/libraries/esp8266/examples/HwdtStackDump/HwdtStackDump.ino @@ -19,12 +19,12 @@ #include #include #include -#include // g_pcont - only needed for this debug demo +#include // g_pcont - only needed for this debug demo #include #ifndef STASSID #define STASSID "your-ssid" -#define STAPSK "your-password" +#define STAPSK "your-password" #endif const char* ssid = STASSID; @@ -39,7 +39,7 @@ extern "C" { #define thunk_ets_uart_printf ets_uart_printf #else - int thunk_ets_uart_printf(const char *format, ...) __attribute__((format(printf, 1, 2))); + 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 @@ -47,25 +47,27 @@ extern "C" { //////////////////////////////////////////////////////////////////// void setup(void) { - WiFi.persistent(false); // w/o this a flash write occurs at every boot - WiFi.mode(WIFI_OFF); Serial.begin(115200); - delay(20); // This delay helps when using the 'Modified Serial monitor' otherwise it is not needed. + 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 @@ -77,9 +79,7 @@ void setup(void) { 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); + 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) 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 index 7d182bf377..4c98899cf4 100644 --- a/libraries/esp8266/examples/HwdtStackDump/ProcessKey.ino +++ b/libraries/esp8266/examples/HwdtStackDump/ProcessKey.ino @@ -1,5 +1,5 @@ #include -void crashMeIfYouCan(void)__attribute__((weak)); +void crashMeIfYouCan(void) __attribute__((weak)); int divideA_B(int a, int b); int* nullPointer = NULL; @@ -14,7 +14,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 +33,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(); @@ -74,10 +77,8 @@ void processKey(Print& out, int hotKey) { 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/I2SInput/I2SInput.ino b/libraries/esp8266/examples/I2SInput/I2SInput.ino index 836da2685b..6e2e11b07b 100644 --- a/libraries/esp8266/examples/I2SInput/I2SInput.ino +++ b/libraries/esp8266/examples/I2SInput/I2SInput.ino @@ -35,7 +35,7 @@ void setup() { 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 2eaab265d2..229e34fcd0 100644 --- a/libraries/esp8266/examples/I2STransmit/I2STransmit.ino +++ b/libraries/esp8266/examples/I2STransmit/I2STransmit.ino @@ -14,7 +14,7 @@ #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 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("%" PRIu32 "\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 7d182bf377..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(); @@ -74,10 +78,8 @@ void processKey(Print& out, int hotKey) { 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 2c22416e0d..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")); } @@ -201,10 +199,10 @@ void runTest3() { // 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.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.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.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.forceSleepBegin(); // 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,18 +387,18 @@ 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.resumeFromShutdown(nv->wss))) { // couldn't resume, or no valid saved WiFi state yet @@ -411,7 +407,7 @@ void initWiFi() { 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 f6c2df08fc..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; @@ -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 b06b38e307..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,64 +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); - } + 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"); @@ -103,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)); @@ -166,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 index c9c6b46025..73512cf2af 100644 --- a/libraries/esp8266/examples/StreamString/StreamString.ino +++ b/libraries/esp8266/examples/StreamString/StreamString.ino @@ -21,7 +21,7 @@ void checksketch(const char* what, const char* res1, const char* res2) { #endif void testStringPtrProgmem() { - static const char inProgmem [] PROGMEM = "I am in progmem"; + static const char inProgmem[] PROGMEM = "I am in progmem"; auto inProgmem2 = F("I am too in progmem"); int heap = (int)ESP.getFreeHeap(); @@ -54,7 +54,7 @@ void testStreamString() { { // We use a a lighter StreamConstPtr(input) to make a read-only Stream out of // a String that obviously should not be modified during the time the - // StreamConstPtr instance is used. It is used as a source to be sent to + // StreamConstPtr instance is used. It is used as a read-only source to be sent to // 'result'. result.clear(); @@ -77,7 +77,7 @@ void testStreamString() { // Now inputString is made into a Stream using S2Stream, // and set in non-consume mode (using ::resetPointer()). - // Then, after that input is read once, it won't be anymore readable + // Then, after input is read once, it won't be anymore readable // until the pointer is reset. S2Stream input(inputString); @@ -87,7 +87,7 @@ void testStreamString() { input.sendAll(result); input.sendAll(result); check("S2Stream.sendAll(StreamString)", result.c_str(), "hello"); - check("unmodified String given to S2Stream", inputString.c_str(), "hello"); + check("String given to S2Stream is unmodified", inputString.c_str(), "hello"); } { @@ -103,6 +103,17 @@ void testStreamString() { check("S2Stream.resetPointer(2):", result.c_str(), "llo"); } + { + // Streaming to a regular String + + String someSource{ F("hello") }; + String someDestString; + + StreamConstPtr(someSource).sendAll(S2Stream(someDestString)); + StreamConstPtr(someSource).sendAll(S2Stream(someDestString)); + check("StreamConstPtr(someSource).sendAll(S2Stream(someDestString))", someDestString.c_str(), "hellohello"); + } + { // inputString made into a Stream // reading the Stream consumes the String @@ -110,7 +121,7 @@ void testStreamString() { result.clear(); S2Stream input(inputString); // reading stream will consume the string - input.setConsume(); // can be omitted, this is the default + input.setConsume(); // can be omitted, this is the default input.sendSize(result, 1); input.sendSize(result, 2); @@ -181,8 +192,8 @@ void setup() { testStreamString(); - Serial.printf("sizeof: String:%d Stream:%d StreamString:%d SStream:%d\n", - (int)sizeof(String), (int)sizeof(Stream), (int)sizeof(StreamString), (int)sizeof(S2Stream)); + Serial.printf("sizeof: String:%zu Stream:%zu StreamString:%zu S2Stream:%zu StreamConstPtr:%zu\n", + sizeof(String), sizeof(Stream), sizeof(StreamString), sizeof(S2Stream), sizeof(StreamConstPtr)); } #endif diff --git a/libraries/esp8266/examples/TestEspApi/TestEspApi.ino b/libraries/esp8266/examples/TestEspApi/TestEspApi.ino index 49ff525898..38e443f3ca 100644 --- a/libraries/esp8266/examples/TestEspApi/TestEspApi.ino +++ b/libraries/esp8266/examples/TestEspApi/TestEspApi.ino @@ -15,8 +15,8 @@ extern "C" { } #endif -// Set up output serial port (could be a SoftwareSerial -// if really wanted). +// Set up output on the first serial port +// (can be any Stream, if needed) Stream& ehConsolePort(Serial); @@ -28,60 +28,19 @@ Stream& ehConsolePort(Serial); // UNLESS: You swap the TX pin using the alternate pinout. const uint8_t LED_PIN = 1; -const char * const RST_REASONS[] = { - "REASON_DEFAULT_RST", - "REASON_WDT_RST", - "REASON_EXCEPTION_RST", - "REASON_SOFT_WDT_RST", - "REASON_SOFT_RESTART", - "REASON_DEEP_SLEEP_AWAKE", - "REASON_EXT_SYS_RST" -}; +const char* const RST_REASONS[] = { "REASON_DEFAULT_RST", "REASON_WDT_RST", "REASON_EXCEPTION_RST", "REASON_SOFT_WDT_RST", "REASON_SOFT_RESTART", "REASON_DEEP_SLEEP_AWAKE", "REASON_EXT_SYS_RST" }; -const char * const FLASH_SIZE_MAP_NAMES[] = { - "FLASH_SIZE_4M_MAP_256_256", - "FLASH_SIZE_2M", - "FLASH_SIZE_8M_MAP_512_512", - "FLASH_SIZE_16M_MAP_512_512", - "FLASH_SIZE_32M_MAP_512_512", - "FLASH_SIZE_16M_MAP_1024_1024", - "FLASH_SIZE_32M_MAP_1024_1024" -}; +const char* const FLASH_SIZE_MAP_NAMES[] = { "FLASH_SIZE_4M_MAP_256_256", "FLASH_SIZE_2M", "FLASH_SIZE_8M_MAP_512_512", "FLASH_SIZE_16M_MAP_512_512", "FLASH_SIZE_32M_MAP_512_512", "FLASH_SIZE_16M_MAP_1024_1024", "FLASH_SIZE_32M_MAP_1024_1024" }; -const char * const OP_MODE_NAMES[] { - "NULL_MODE", - "STATION_MODE", - "SOFTAP_MODE", - "STATIONAP_MODE" -}; +const char* const OP_MODE_NAMES[]{ "NULL_MODE", "STATION_MODE", "SOFTAP_MODE", "STATIONAP_MODE" }; -const char * const AUTH_MODE_NAMES[] { - "AUTH_OPEN", - "AUTH_WEP", - "AUTH_WPA_PSK", - "AUTH_WPA2_PSK", - "AUTH_WPA_WPA2_PSK", - "AUTH_MAX" -}; +const char* const AUTH_MODE_NAMES[]{ "AUTH_OPEN", "AUTH_WEP", "AUTH_WPA_PSK", "AUTH_WPA2_PSK", "AUTH_WPA_WPA2_PSK", "AUTH_MAX" }; -const char * const PHY_MODE_NAMES[] { - "", - "PHY_MODE_11B", - "PHY_MODE_11G", - "PHY_MODE_11N" -}; +const char* const PHY_MODE_NAMES[]{ "", "PHY_MODE_11B", "PHY_MODE_11G", "PHY_MODE_11N" }; -const char * const EVENT_NAMES[] { - "EVENT_STAMODE_CONNECTED", - "EVENT_STAMODE_DISCONNECTED", - "EVENT_STAMODE_AUTHMODE_CHANGE", - "EVENT_STAMODE_GOT_IP", - "EVENT_SOFTAPMODE_STACONNECTED", - "EVENT_SOFTAPMODE_STADISCONNECTED", - "EVENT_MAX" -}; +const char* const EVENT_NAMES[]{ "EVENT_STAMODE_CONNECTED", "EVENT_STAMODE_DISCONNECTED", "EVENT_STAMODE_AUTHMODE_CHANGE", "EVENT_STAMODE_GOT_IP", "EVENT_SOFTAPMODE_STACONNECTED", "EVENT_SOFTAPMODE_STADISCONNECTED", "EVENT_MAX" }; -const char * const EVENT_REASONS[] { +const char* const EVENT_REASONS[]{ "", "REASON_UNSPECIFIED", "REASON_AUTH_EXPIRE", @@ -108,27 +67,21 @@ const char * const EVENT_REASONS[] { "REASON_CIPHER_SUITE_REJECTED", }; -const char * const EVENT_REASONS_200[] { - "REASON_BEACON_TIMEOUT", - "REASON_NO_AP_FOUND" -}; +const char* const EVENT_REASONS_200[]{ "REASON_BEACON_TIMEOUT", "REASON_NO_AP_FOUND" }; -void wifi_event_handler_cb(System_Event_t * event) { +void wifi_event_handler_cb(System_Event_t* event) { ehConsolePort.print(EVENT_NAMES[event->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 index cf581c8890..30cacb1127 100644 --- a/libraries/esp8266/examples/UartDownload/UartDownload.ino +++ b/libraries/esp8266/examples/UartDownload/UartDownload.ino @@ -59,11 +59,10 @@ constexpr char slipFrameMarker = '\xC0'; // <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"; +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 +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 @@ -71,9 +70,7 @@ constexpr size_t syncPktSz = sizeof(syncPkt) - 1; // Don't compare zero terminat // the slipFrameMarker. // void proxyEspSync() { - if (!uartDownloadEnable) { - return; - } + if (!uartDownloadEnable) { return; } byte buf[pktBufSz]; @@ -90,7 +87,7 @@ void proxyEspSync() { } // Assume RX FIFO data is garbled and flush all RX data. - while (0 <= Serial.read()) {} // Clear FIFO + while (0 <= Serial.read()) {} // Clear FIFO // If your Serial requirements need a specific timeout value, you would // restore those here. @@ -111,8 +108,7 @@ void setup() { // stub, then both devices shift their UART speeds to the command line value. Serial.begin(115200); - Serial.println(F( - "\r\n\r\n" + 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" @@ -143,20 +139,17 @@ void cmdLoop(Print& oStream, int key) { 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")); - } + 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; + default: break; } oStream.println(); 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 169d53e05e..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; } @@ -57,9 +231,7 @@ uint32_t cyclesToWrite_nKx16(int n, unsigned short *x) { uint32_t cyclesToRead_nKxs16(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 += *(x++); - } + for (int i = 0; i < n * 1024; i++) { sum += *(x++); } *res = sum; return ESP.getCycleCount() - b; } @@ -74,17 +246,15 @@ uint32_t cyclesToWrite_nKxs16(int n, short *x) { return ESP.getCycleCount() - b; } -uint32_t cyclesToRead_nKx8(int n, unsigned char*x, uint32_t *res) { +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++); - } + 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++) { @@ -95,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; @@ -121,7 +290,7 @@ 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++); + sum += mmu_get_int16(x++); //*(x++); } *res = sum; return ESP.getCycleCount() - b; @@ -138,17 +307,17 @@ uint32_t cyclesToWrite_nKxs16_viaInline(int n, short *x) { return ESP.getCycleCount() - b; } -uint32_t cyclesToRead_nKx8_viaInline(int n, unsigned char*x, uint32_t *res) { +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++) { @@ -159,25 +328,27 @@ uint32_t cyclesToWrite_nKx8_viaInline(int n, unsigned char*x) { return ESP.getCycleCount() - b; } + 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("\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); + 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); + 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); + 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); + 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); + 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"); @@ -186,9 +357,9 @@ bool perfTest_nK(int nK, uint32_t *mem, uint32_t *imem) { success = false; } - t = cyclesToWrite_nKxs16_viaInline(nK, (int16_t*)imem); + 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); + 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"); @@ -197,9 +368,9 @@ bool perfTest_nK(int nK, uint32_t *mem, uint32_t *imem) { success = false; } - t = cyclesToWrite_nKx16(nK, (uint16_t*)imem); + 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); + 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"); @@ -207,9 +378,9 @@ bool perfTest_nK(int nK, uint32_t *mem, uint32_t *imem) { Serial.printf("!= (sum %08x ) - failed\r\n", verify_res); success = false; } - t = cyclesToWrite_nKxs16(nK, (int16_t*)imem); + 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); + 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"); @@ -218,15 +389,16 @@ bool perfTest_nK(int nK, uint32_t *mem, uint32_t *imem) { 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("\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); + 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); + 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); + 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"); @@ -235,9 +407,9 @@ bool perfTest_nK(int nK, uint32_t *mem, uint32_t *imem) { success = false; } - t = cyclesToWrite_nKx8(nK, (uint8_t*)imem); + 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); + 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"); @@ -271,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 @@ -297,14 +467,13 @@ 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.printf("\r\nPerformance numbers for 32 bit access - no exception handler or inline macros needed.\r\n");; + 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: %7d cycles for %dK by uint32, %3d AVG cycles/transfer\r\n", t, nK, t / (nK * 1024)); t = cyclesToRead_nKx32(nK, mem, &res); @@ -317,7 +486,7 @@ void setup() { Serial.println(); - if (perfTest_nK(1, mem, imem)) { + if (perfTest_nK(1, mem, imem) && testPunning() && test4_32bit_loads()) { Serial.println(); } else { Serial.println("\r\n*******************************"); @@ -394,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); @@ -413,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()); @@ -440,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 index 47b9396cc7..1c4e3340fa 100644 --- a/libraries/esp8266/examples/virtualmem/virtualmem.ino +++ b/libraries/esp8266/examples/virtualmem/virtualmem.ino @@ -2,9 +2,7 @@ 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++); - } + for (int i = 0; i < 1024; i++) { sum += *(x++); } *res = sum; return ESP.getCycleCount() - b; } @@ -23,9 +21,7 @@ uint32_t cyclesToWrite1Kx32(unsigned int *x) { 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++); - } + for (int i = 0; i < 1024; i++) { sum += *(x++); } *res = sum; return ESP.getCycleCount() - b; } @@ -40,17 +36,15 @@ uint32_t cyclesToWrite1Kx16(unsigned short *x) { return ESP.getCycleCount() - b; } -uint32_t cyclesToRead1Kx8(unsigned char*x, uint32_t *res) { +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++); - } + for (int i = 0; i < 1024; i++) { sum += *(x++); } *res = sum; return ESP.getCycleCount() - b; } -uint32_t cyclesToWrite1Kx8(unsigned char*x) { +uint32_t cyclesToWrite1Kx8(unsigned char *x) { uint32_t b = ESP.getCycleCount(); uint32_t sum = 0; for (int i = 0; i < 1024; i++) { @@ -88,24 +82,24 @@ void setup() { t = cyclesToRead1Kx32(mem, &res); Serial.printf("Physical Memory Read: %d cycles for 4K (sum %08x)\n", t, res); - t = cyclesToWrite1Kx16((uint16_t*)vm); + t = cyclesToWrite1Kx16((uint16_t *)vm); Serial.printf("Virtual Memory Write: %d cycles for 2K by 16\n", t); - t = cyclesToWrite1Kx16((uint16_t*)mem); + t = cyclesToWrite1Kx16((uint16_t *)mem); Serial.printf("Physical Memory Write: %d cycles for 2K by 16\n", t); - t = cyclesToRead1Kx16((uint16_t*)vm, &res); + 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); + 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); + t = cyclesToWrite1Kx8((uint8_t *)vm); Serial.printf("Virtual Memory Write: %d cycles for 1K by 8\n", t); - t = cyclesToWrite1Kx8((uint8_t*)mem); + t = cyclesToWrite1Kx8((uint8_t *)mem); Serial.printf("Physical Memory Write: %d cycles for 1K by 8\n", t); - t = cyclesToRead1Kx8((uint8_t*)vm, &res); + 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); + 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 @@ -133,6 +127,4 @@ void setup() { ESP.resetHeap(); } -void loop() { - -} +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/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/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 07ec39e929..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; @@ -109,38 +122,39 @@ class ENC28J60 @return the length of the received frame 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 f0296a9265..b74c5ba56e 100644 --- a/libraries/lwIP_w5100/library.properties +++ b/libraries/lwIP_w5100/library.properties @@ -1,4 +1,4 @@ -name=lwIP_w5500 +name=lwIP_w5100 version=1 author=Nicholas Humfrey maintainer=esp8266/Arduino diff --git a/libraries/lwIP_w5100/src/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 43b9f0b9f3..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; @@ -110,28 +125,30 @@ class Wiznet5100 @return the length of the received frame 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 bdece2d88b..0000000000 --- a/libraries/lwIP_w5500/examples/TCPClient/TCPClient.ino +++ /dev/null @@ -1,95 +0,0 @@ -/* - This sketch establishes a TCP connection to a "quote of the day" service. - It sends a "hello" message, and then prints received data. -*/ - -#include -#include -//or #include -//or #include - -#include // WiFiClient (-> TCPClient) - -const char* host = "djxmmx.net"; -const uint16_t port = 17; - -using TCPClient = WiFiClient; - -#define CSPIN 16 // wemos/lolin/nodemcu D0 -Wiznet5500lwIP eth(CSPIN); - -void setup() { - Serial.begin(115200); - - SPI.begin(); - SPI.setClockDivider(SPI_CLOCK_DIV4); // 4 MHz? - SPI.setBitOrder(MSBFIRST); - SPI.setDataMode(SPI_MODE0); - eth.setDefault(); // use ethernet for default route - if (!eth.begin()) { - Serial.println("ethernet hardware not found ... sleeping"); - while (1) { - delay(1000); - } - } else { - Serial.print("connecting ethernet"); - while (!eth.connected()) { - Serial.print("."); - delay(1000); - } - } - Serial.println(); - Serial.print("ethernet IP address: "); - Serial.println(eth.localIP()); -} - -void loop() { - static bool wait = false; - - Serial.print("connecting to "); - Serial.print(host); - Serial.print(':'); - Serial.println(port); - - TCPClient client; - if (!client.connect(host, port)) { - Serial.println("connection failed"); - delay(5000); - return; - } - - // This will send a string to the server - Serial.println("sending data to server"); - if (client.connected()) { - client.println("hello from ESP8266"); - } - - // wait for data to be available - unsigned long timeout = millis(); - while (client.available() == 0) { - if (millis() - timeout > 5000) { - Serial.println(">>> Client Timeout !"); - client.stop(); - delay(60000); - return; - } - } - - // Read all the lines of the reply from server and print them to Serial - Serial.println("receiving from remote server"); - // not testing 'client.connected()' since we do not need to send data here - while (client.available()) { - char ch = static_cast(client.read()); - Serial.print(ch); - } - - // Close the connection - Serial.println(); - Serial.println("closing connection"); - client.stop(); - - if (wait) { - delay(300000); // execute once every 5 minutes, don't flood remote service - } - wait = true; -} diff --git a/libraries/lwIP_w5500/src/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 85b253a4bf..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; @@ -112,11 +125,9 @@ class Wiznet5500 @return the length of the received frame 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 CHECK + 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 cec683a941..d85d07b684 100644 --- a/package.json +++ b/package.json @@ -2,5 +2,5 @@ "name": "framework-arduinoespressif8266", "description": "Arduino Wiring-based Framework (ESP8266 Core)", "url": "https://github.com/esp8266/Arduino", - "version": "3.1.0-dev" + "version": "3.2.0-dev" } diff --git a/package/README.md b/package/README.md index dfad8f5d9d..b2d64ef76e 100644 --- a/package/README.md +++ b/package/README.md @@ -73,7 +73,7 @@ Here is a rough overview of the effective release process. See the section below * Since most changes are integrated into master using squash-rebase policy (i.e. one commit per PR), `git log --oneline` gives a good overview of changes in the release. - * Prepare release notes in Markdown format. + * Prepare release notes in Markdown format. Either use the `git log --oneline` output and sort through it manually, or use Github draft release and press 'Generate release notes' (see https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes) * For changes that are breaking, duplicate those changes and put the duplicate lines into a separate group called Breaking Changes. That group should go at the top of the Changelog. The original lines for the breaking changes should be marked by appending "(Breaking change)" to the line. Example: @@ -99,11 +99,11 @@ Here is a rough overview of the effective release process. See the section below - Documentation - Boards - * Not all commit descriptions which come from `git log` will explain changes well. Reword items as necessary, with the goal that a general user of this project should be able to understand what the change is related to. Preserve references to PRs or issues (`#XXXX`). + * Not all commit descriptions which come from `git log` or PR titles will explain changes well. Reword items as necessary, with the goal that a general user of this project should be able to understand what the change is related to. Preserve references to PRs or issues (`#XXXX`). * Aggregate minor fixes (e.g. typos, small documentation changes) in a few items. Focus on preparing a good overview of the release for the users, rather than mentioning every change. - * When done, put release notes into a private [Gist](https://gist.github.com) or [firepad](https://demo.firepad.io) and send the link to other maintainers for review. + * When done, put release notes into a private [Gist](https://gist.github.com) or [HedgeDoc note](https://hedgedoc.org/) and send the link to other maintainers for review. The following points assume work in a direct clone of the repository, and not in a personal fork. @@ -111,8 +111,8 @@ The following points assume work in a direct clone of the repository, and not in * [platform.txt](https://github.com/esp8266/Arduino/blob/master/platform.txt) and [package.json](https://github.com/esp8266/Arduino/blob/master/package.json): update `version` to the release E.g. `3.0.0`, - * [cores/esp8266/TZ.h](https://github.com/esp8266/Arduino/blob/master/cores/esp8266/TZ.h): import the latest database with the following shell command:\ - `$ cd tools; sh TZupdate.sh` + * [cores/esp8266/TZ.h](https://github.com/esp8266/Arduino/blob/master/cores/esp8266/TZ.h): import the latest database:\ + `$ pip install -U tzdata; python tools/format_tzdata.py --output cores/esp8266/TZ.h` * Update SSL/TLS certificates and public keys in examples:\ `$ cd tools; sh certsUpdate.sh` @@ -141,13 +141,13 @@ The following points assume work in a direct clone of the repository, and not in 8. Check that the new (draft) release has been created (no editing at this point!), see https://github.com/esp8266/Arduino/releases. -9. Check that the boards manager package .zip file has been successfully uploaded as a release artifact. +9. Check that the boards manager package .zip file has been successfully uploaded as a release asset. 10. Check that the package index downloaded from https://arduino.esp8266.com/stable/package_esp8266com_index.json contains an entry for the new version (it may not be the first one). 11. Navigate to release list in Github here https://github.com/esp8266/Arduino/releases, press "Edit" button to edit release description, paste release notes, and publish it. -12. In the issue tracker, remove "staged-for-release" label for all issues which have it, and close them. Close the milestone associated with the released version (the milestone should be empty per point 2 above) +12. Close the milestone associated with the released version (the milestone should be empty per point 1 above) 13. Check that https://arduino-esp8266.readthedocs.io/en/latest/ has a new doc build for the new tag, and that "stable" points to that build. If a new build did not trigger, log into readthedoc's home here https://readthedocs.org/ (account must have been added to project as maintainer) and trigger it manually. @@ -173,7 +173,7 @@ The following points assume work in a direct clone of the repository, and not in * [platform.txt](https://github.com/esp8266/Arduino/blob/master/platform.txt) * [package.json](https://github.com/esp8266/Arduino/blob/master/package.json) - * [TZ.h](https://github.com/esp8266/Arduino/blob/master/cores/esp8266/TZ.h) (<= `cd tools; sh ./TZupdate.sh`) + * [TZ.h](https://github.com/esp8266/Arduino/blob/master/cores/esp8266/TZ.h) (<= `pip install -U tzdata; python tools/format_tzdata.py --output cores/esp8266/TZ.h`) * Certificates (<= `cd tools; sh certsUpdate.sh`) - [ ] 5. Wait until the release notes have been checked by other maintainers (can be changed afterwards anyway) @@ -186,13 +186,13 @@ The following points assume work in a direct clone of the repository, and not in - [ ] 8. Check that the new (draft) release has been created (no editing at this point!), see https://github.com/esp8266/Arduino/releases. -- [ ] 9. Check that the boards manager package .zip file has been successfully uploaded as a release artifact. +- [ ] 9. Check that the boards manager package .zip file has been successfully uploaded as a release asset. - [ ] 10. Check that the package index downloaded from https://arduino.esp8266.com/stable/package_esp8266com_index.json contains an entry for the new version (it may not be the first one). - [ ] 11. Navigate to [release list in Github](https://github.com/esp8266/Arduino/releases), press "Edit" button to edit release description, paste release notes, and publish it. -- [ ] 12. In the issue tracker, remove "staged-for-release" label for all issues which have it, and close them. Close the milestone associated with the released version (the milestone should be empty per point 1 above) +- [ ] 12. Close the milestone associated with the released version (the milestone should be empty per point 1 above) - [ ] 13. Check that https://arduino-esp8266.readthedocs.io/en/latest/ has a new doc build for the new tag, and that "stable" points to that build. If a new build did not trigger, log into readthedoc's home here https://readthedocs.org/ (account must have been added to project as maintainer) and trigger it manually. @@ -202,3 +202,35 @@ The following points assume work in a direct clone of the repository, and not in * In main README.md go to "Latest release" section, change version number in the readthedocs link to the version which was just released, and verify that all links work. --------------COPY ABOVE THIS LINE-------------- ``` + +## Updating a SSH deploy key + +A SSH private/public key pair is required to update the master JSON (the final step of the release process). Sometimes GitHub will expire one side or the other of that key, and a new one will need to be regenerated and installed in the https://github.com/esp8266/esp8266.github.io (JSON) and https://github.com/esp8266/Arduino (core) repos. + +1. Generate a new public/private SSH key pair with an empty passphrase: +```console +$ ssh-keygen -f deploy_key -t ed25519 -N '' -C earlephilhower@yahoo.com (**replace with your GH user account email**) +Generating public/private ed25519 key pair. +Your identification has been saved in deploy_key +Your public key has been saved in deploy_key.pub +The key fingerprint is: +... +``` + +2. Copy the contents of `deploy_key.pub` to the clipboard: +```console +$ cat deploy_key.pub +ssh-ed25519 AAA..... earlephilhower@yahoo.com +``` + +3. Install the deploy key for esp8266.github.io repository. Go to https://github.com/esp8266/esp8266.github.io and the `Settings->Deploy Keys` and `Add deploy key`. Paste the (public key) string into the box and select `Allow writes` and hit OK. + +4. Convert the `deploy_key` private key to a 1-line base64 representation and copy it to the clipboard. +```console +$ base64 -w 0 < deploy_key && echo "" +yEvYm..... (**note this must be one single long line, hence the "-w 0"**) +``` + +5. Install the private key to the Core repo. Go to https://github.com/esp8266/Arduino and select `Settings->Secrets->Actions` and add or update a `Repository secret` called `GHCI_DEPLOY_KEY`. Paste the 1-line base64 contents of your clipboard to the box and hit OK. + +6. If the release failed in the `Update master JSON file` action, from the GitHub web interface run the `Actions->Release XXX->Re-run failed jobs` to re-run it and check its output. diff --git a/package/build_boards_manager_package.sh b/package/build_boards_manager_package.sh index e5d4e58aa0..40050f4437 100755 --- a/package/build_boards_manager_package.sh +++ b/package/build_boards_manager_package.sh @@ -1,6 +1,5 @@ #!/bin/bash - if [ ! -z "${manualversion}" ]; then # manual-made release based on $manualversion @@ -72,13 +71,16 @@ mkdir -p ${outdir} # Some files should be excluded from the package cat << EOF > exclude.txt .git +.git-blame-ignore-revs +.github .gitignore .gitmodules -.travis.yml -package +ISSUE_TEMPLATE.md doc +package EOF # Also include all files which are ignored by git +# TODO: .gitattributes helper for the above? git ls-files --other --directory >> exclude.txt # Now copy files to $outdir rsync -a --exclude-from 'exclude.txt' ${srcdir}/ ${outdir}/ diff --git a/package/package_esp8266com_index.template.json b/package/package_esp8266com_index.template.json index cb1bdab0c9..3c8d1bed39 100644 --- a/package/package_esp8266com_index.template.json +++ b/package/package_esp8266com_index.template.json @@ -47,6 +47,9 @@ { "name": "ESPresso Lite 2.0" }, + { + "name": "Mercury 1.0" + }, { "name": "Phoenix 1.0" }, @@ -77,6 +80,9 @@ { "name": "LOLIN(WEMOS) D1 R2 & mini" }, + { + "name": "LOLIN(WEMOS) D1 ESP-WROOM-02" + }, { "name": "LOLIN(WEMOS) D1 mini (clone)" }, @@ -132,22 +138,22 @@ "toolsDependencies": [ { "packager": "esp8266", - "version": "3.0.4-gcc10.3-1757bed", + "version": "3.2.0-gcc10.3-c791b74", "name": "xtensa-lx106-elf-gcc" }, { "packager": "esp8266", - "version": "3.0.4-gcc10.3-1757bed", + "version": "3.2.0-gcc10.3-c791b74", "name": "mkspiffs" }, { "packager": "esp8266", - "version": "3.0.4-gcc10.3-1757bed", + "version": "3.2.0-gcc10.3-c791b74", "name": "mklittlefs" }, { "packager": "esp8266", - "version": "3.7.2-post1", + "version": "3.7.2-post2", "name": "python3" } ], @@ -158,222 +164,222 @@ ], "tools": [ { - "version": "3.7.2-post1", + "version": "3.7.2-post2", "name": "python3", "systems": [ { "host": "x86_64-mingw32", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/python3-3.7.2.post1-embed-win32v2a.zip", + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/python3-3.7.2.post1-embed-win32v2a.zip", "archiveFileName": "python3-3.7.2.post1-embed-win32v2a.zip", "checksum": "SHA-256:f57cb2daf86176d2929e7c58990c2ac32554e3219d454dcac10e464ddda35bf2", "size": "6428926" }, { "host": "i686-mingw32", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/python3-3.7.2.post1-embed-win32v2a.zip", + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/python3-3.7.2.post1-embed-win32v2a.zip", "archiveFileName": "python3-3.7.2.post1-embed-win32v2a.zip", "checksum": "SHA-256:f57cb2daf86176d2929e7c58990c2ac32554e3219d454dcac10e464ddda35bf2", "size": "6428926" }, { "host": "aarch64-linux-gnu", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/python3-via-env.tar.gz", + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/python3-via-env.tar.gz", "archiveFileName": "python3-via-env.tar.gz", - "checksum": "SHA-256:c9237bfe0f62842d7187a39495baa4a7e3ab8b87c0b433614294b023cf0bc0f3", - "size": "292" + "checksum": "SHA-256:a7a9905887703a0c862356918b7a9b9ca6968a696d53a15a7cdb7f1655cecf3f", + "size": "331" }, { "host": "arm-linux-gnueabihf", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/python3-via-env.tar.gz", + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/python3-via-env.tar.gz", "archiveFileName": "python3-via-env.tar.gz", - "checksum": "SHA-256:c9237bfe0f62842d7187a39495baa4a7e3ab8b87c0b433614294b023cf0bc0f3", - "size": "292" + "checksum": "SHA-256:a7a9905887703a0c862356918b7a9b9ca6968a696d53a15a7cdb7f1655cecf3f", + "size": "331" }, { "host": "i686-pc-linux-gnu", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/python3-via-env.tar.gz", + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/python3-via-env.tar.gz", "archiveFileName": "python3-via-env.tar.gz", - "checksum": "SHA-256:c9237bfe0f62842d7187a39495baa4a7e3ab8b87c0b433614294b023cf0bc0f3", - "size": "292" + "checksum": "SHA-256:a7a9905887703a0c862356918b7a9b9ca6968a696d53a15a7cdb7f1655cecf3f", + "size": "331" }, { "host": "x86_64-apple-darwin", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/python3-macosx-portable.tar.gz", + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/python3-macosx-portable.tar.gz", "archiveFileName": "python3-macosx-portable.tar.gz", "checksum": "SHA-256:01a5bf1fa264c6f04cfaadf4c6e9f6caaacb6833ef40104dfbe953fcdb9bca1c", "size": "25494144" }, { "host": "x86_64-pc-linux-gnu", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/2.5.0-4/python3-via-env.tar.gz", + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/python3-via-env.tar.gz", "archiveFileName": "python3-via-env.tar.gz", - "checksum": "SHA-256:c9237bfe0f62842d7187a39495baa4a7e3ab8b87c0b433614294b023cf0bc0f3", - "size": "292" + "checksum": "SHA-256:a7a9905887703a0c862356918b7a9b9ca6968a696d53a15a7cdb7f1655cecf3f", + "size": "331" } ] }, { - "version": "3.0.4-gcc10.3-1757bed", + "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.4-gcc10.3/aarch64-linux-gnu.xtensa-lx106-elf-1757bed.210717.tar.gz", - "archiveFileName": "aarch64-linux-gnu.xtensa-lx106-elf-1757bed.210717.tar.gz", - "checksum": "SHA-256:7fe2b5ad8551e30e6ed919ad291ef3446ff1892aa6de106d7223188ae74cbc4e", - "size": "71328229" + "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.4-gcc10.3/arm-linux-gnueabihf.xtensa-lx106-elf-1757bed.210717.tar.gz", - "archiveFileName": "arm-linux-gnueabihf.xtensa-lx106-elf-1757bed.210717.tar.gz", - "checksum": "SHA-256:44c085a4b38ea09e7dfdc0932c9ebbc4676e487db0e3c5b8bee03c8b0f1661ee", - "size": "67428183" + "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.4-gcc10.3/i686-linux-gnu.xtensa-lx106-elf-1757bed.210717.tar.gz", - "archiveFileName": "i686-linux-gnu.xtensa-lx106-elf-1757bed.210717.tar.gz", - "checksum": "SHA-256:421a268b39dcdf02f3df65cf163ec0f727d8bd29ebb8ab508412efb4d8db72ba", - "size": "74504828" + "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.4-gcc10.3/i686-w64-mingw32.xtensa-lx106-elf-1757bed.210717.zip", - "archiveFileName": "i686-w64-mingw32.xtensa-lx106-elf-1757bed.210717.zip", - "checksum": "SHA-256:353fab32fdd596385a004da9ca5c4661a6dc48bf6a1cca460a22f512df7d9995", - "size": "71729688" + "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.4-gcc10.3/x86_64-apple-darwin14.xtensa-lx106-elf-1757bed.210717.tar.gz", - "archiveFileName": "x86_64-apple-darwin14.xtensa-lx106-elf-1757bed.210717.tar.gz", - "checksum": "SHA-256:5a0b00ca67476f89847776330b6cf2c161092ce798c98e28aeac0f7e11d7116a", - "size": "75917944" + "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.4-gcc10.3/x86_64-linux-gnu.xtensa-lx106-elf-1757bed.210717.tar.gz", - "archiveFileName": "x86_64-linux-gnu.xtensa-lx106-elf-1757bed.210717.tar.gz", - "checksum": "SHA-256:0a8819d7e5753b77c395d4e5047ffa442945fe0d13073e8425b4c398b8315fa6", - "size": "75030450" + "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.4-gcc10.3/x86_64-w64-mingw32.xtensa-lx106-elf-1757bed.210717.zip", - "archiveFileName": "x86_64-w64-mingw32.xtensa-lx106-elf-1757bed.210717.zip", - "checksum": "SHA-256:036d6ebde43ca5371759793d82d1ab992cf6250e7e0fa5400d8b13146aeb55bd", - "size": "75702794" + "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.4-gcc10.3-1757bed", + "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.4-gcc10.3/aarch64-linux-gnu.mkspiffs-7fefeac.210717.tar.gz", - "archiveFileName": "aarch64-linux-gnu.mkspiffs-7fefeac.210717.tar.gz", - "checksum": "SHA-256:03a58331a921c54ceb7a2d3fab368dfde81d74b36074d7439bcafca85df7f9e3", - "size": "51256" + "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.4-gcc10.3/arm-linux-gnueabihf.mkspiffs-7fefeac.210717.tar.gz", - "archiveFileName": "arm-linux-gnueabihf.mkspiffs-7fefeac.210717.tar.gz", - "checksum": "SHA-256:87bcfdca5b63ef48c3b515e994b552b3093cc4f0a4e7ce18f2ea1e5da87d8a51", - "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.4-gcc10.3/i686-linux-gnu.mkspiffs-7fefeac.210717.tar.gz", - "archiveFileName": "i686-linux-gnu.mkspiffs-7fefeac.210717.tar.gz", - "checksum": "SHA-256:c8a1ae974622e698503de0e8f982411cd98a1ecc756f78bbd5ae6f672ebec6c2", - "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.4-gcc10.3/i686-w64-mingw32.mkspiffs-7fefeac.210717.zip", - "archiveFileName": "i686-w64-mingw32.mkspiffs-7fefeac.210717.zip", - "checksum": "SHA-256:be4177ff2efb80a1971c261d81c58ae16976f5dd110307eadae8726ef47448c6", - "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.4-gcc10.3/x86_64-apple-darwin14.mkspiffs-7fefeac.210717.tar.gz", - "archiveFileName": "x86_64-apple-darwin14.mkspiffs-7fefeac.210717.tar.gz", - "checksum": "SHA-256:86e94d75068381ca3f2c90e2897d8bc2144149f4e9b174feba56d0fdf2d62698", - "size": "368772" + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/x86_64-apple-darwin14.mkspiffs-7fefeac.230224.tar.gz", + "archiveFileName": "x86_64-apple-darwin14.mkspiffs-7fefeac.230224.tar.gz", + "checksum": "SHA-256:3d5dc573f46b726dc38d3971dfe70500e818b78230f7d531d4370b779fef3710", + "size": "380164" }, { "host": "x86_64-pc-linux-gnu", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.4-gcc10.3/x86_64-linux-gnu.mkspiffs-7fefeac.210717.tar.gz", - "archiveFileName": "x86_64-linux-gnu.mkspiffs-7fefeac.210717.tar.gz", - "checksum": "SHA-256:56b0615f9e36f8624c26f502f9bf97605c319194cadb465ad25d301e8ce04e54", - "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.4-gcc10.3/x86_64-w64-mingw32.mkspiffs-7fefeac.210717.zip", - "archiveFileName": "x86_64-w64-mingw32.mkspiffs-7fefeac.210717.zip", - "checksum": "SHA-256:b556d32393583f3402473b4a15883869f706a3a4fe1dec596542314039bed07e", - "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.4-gcc10.3-1757bed", + "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.4-gcc10.3/aarch64-linux-gnu.mklittlefs-943d2f7.210717.tar.gz", - "archiveFileName": "aarch64-linux-gnu.mklittlefs-943d2f7.210717.tar.gz", - "checksum": "SHA-256:8de409e54df316b67ba89c630ac6d22333dfa1f65a8d603573ae048ca95480c5", - "size": "44966" + "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.4-gcc10.3/arm-linux-gnueabihf.mklittlefs-943d2f7.210717.tar.gz", - "archiveFileName": "arm-linux-gnueabihf.mklittlefs-943d2f7.210717.tar.gz", - "checksum": "SHA-256:4c1b0a5f6464110adc9de39ae930d0371b491f702d554a70211dfe0eea6717c7", - "size": "37463" + "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.4-gcc10.3/i686-linux-gnu.mklittlefs-943d2f7.210717.tar.gz", - "archiveFileName": "i686-linux-gnu.mklittlefs-943d2f7.210717.tar.gz", - "checksum": "SHA-256:0a2653d8bff693e904e09ea28922c29d8ebf92a55d91dad8ad5c3b70c032b4ae", - "size": "48408" + "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.4-gcc10.3/i686-w64-mingw32.mklittlefs-943d2f7.210717.zip", - "archiveFileName": "i686-w64-mingw32.mklittlefs-943d2f7.210717.zip", - "checksum": "SHA-256:df7eb4050cd87f4c8960c987465dfb3c30a8a996842313f3e00badf65e099664", - "size": "332965" + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/i686-w64-mingw32.mklittlefs-4aca452.230224.zip", + "archiveFileName": "i686-w64-mingw32.mklittlefs-4aca452.230224.zip", + "checksum": "SHA-256:7d3701f5e89dad12459b95359b7e5b668cba64661669e8c1cdc384b83f985714", + "size": "347321" }, { "host": "x86_64-apple-darwin", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.4-gcc10.3/x86_64-apple-darwin14.mklittlefs-943d2f7.210717.tar.gz", - "archiveFileName": "x86_64-apple-darwin14.mklittlefs-943d2f7.210717.tar.gz", - "checksum": "SHA-256:c2b70b824f460590002f8dc24c4fafef611c09d362f2307f816687a9deebfdaf", - "size": "362750" + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/x86_64-apple-darwin14.mklittlefs-4aca452.230224.tar.gz", + "archiveFileName": "x86_64-apple-darwin14.mklittlefs-4aca452.230224.tar.gz", + "checksum": "SHA-256:13048f6ae246b00ea1902156542a832c767ba43d839fc62b2f6668e8821bd899", + "size": "378772" }, { "host": "x86_64-pc-linux-gnu", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.4-gcc10.3/x86_64-linux-gnu.mklittlefs-943d2f7.210717.tar.gz", - "archiveFileName": "x86_64-linux-gnu.mklittlefs-943d2f7.210717.tar.gz", - "checksum": "SHA-256:c00fc8d9771c33e60873ac4dfa6147e00d3f9a8a92a563afff4605a0f8a05acc", - "size": "47059" + "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.4-gcc10.3/x86_64-w64-mingw32.mklittlefs-943d2f7.210717.zip", - "archiveFileName": "x86_64-w64-mingw32.mklittlefs-943d2f7.210717.zip", - "checksum": "SHA-256:6fcfc3c9a6aaad34ab41c62d2355a2e9536687b0f4108959262924937b8798f0", - "size": "345411" + "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.2.0-gcc10.3/x86_64-w64-mingw32.mklittlefs-4aca452.230224.zip", + "archiveFileName": "x86_64-w64-mingw32.mklittlefs-4aca452.230224.zip", + "checksum": "SHA-256:bedb416db3f30b34884b5ad37ea358452dbd1e6a951a31c7b7e6d3d2f467ea68", + "size": "359007" } ] } diff --git a/platform.txt b/platform.txt index 9f823d2c99..3ca4ffae29 100644 --- a/platform.txt +++ b/platform.txt @@ -5,8 +5,8 @@ # For more info: # https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5-3rd-party-Hardware-specification -name=ESP8266 Boards (3.1.0-dev) -version=3.1.0-dev +name=ESP8266 Boards (3.2.0-dev) +version=3.2.0-dev # These will be removed by the packager script when doing a JSON release runtime.tools.xtensa-lx106-elf-gcc.path={runtime.platform.path}/tools/xtensa-lx106-elf @@ -17,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 -Werror=return-type -compiler.warning_flags.none=-w -Werror=return-type -compiler.warning_flags.default=-Werror=return-type -compiler.warning_flags.more=-Wall -Werror=return-type -compiler.warning_flags.all=-Wall -Wextra -Werror=return-type +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__ -D_GNU_SOURCE "-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.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.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 @@ -83,7 +106,7 @@ compiler.elf2hex.flags= compiler.size.cmd=xtensa-lx106-elf-size # This can be overridden in boards.txt -build.extra_flags=-DESP8266 +build.extra_flags= # These can be overridden in platform.local.txt compiler.c.extra_flags= @@ -98,28 +121,32 @@ compiler.elf2hex.extra_flags= ## needs git recipe.hooks.sketch.prebuild.pattern="{runtime.tools.python3.path}/python3" -I "{runtime.tools.signing}" --mode header --publickey "{build.source.path}/public.key" --out "{build.path}/core/Updater_Signing.h" # This is quite a working hack. This form of prebuild hook, while intuitive, is not explicitly documented. -recipe.hooks.prebuild.pattern="{runtime.tools.python3.path}/python3" -I "{runtime.tools.makecorever}" --build_path "{build.path}" --platform_path "{runtime.platform.path}" --version "{version}" +recipe.hooks.prebuild.1.pattern="{runtime.tools.python3.path}/python3" -I "{runtime.tools.makecorever}" --build_path "{build.path}" --platform_path "{runtime.platform.path}" --version "{version}" + +# Handle processing sketch global options +recipe.hooks.prebuild.2.pattern="{runtime.tools.python3.path}/python3" -I "{runtime.tools.mkbuildoptglobals}" "{runtime.ide.path}" {runtime.ide.version} "{build.path}" "{build.opt.fqfn}" "{globals.h.source.fqfn}" "{commonhfile.fqfn}" {mkbuildoptglobals.extra_flags} + ## Build the app.ld linker file recipe.hooks.linking.prelink.1.pattern="{runtime.tools.python3.path}/python3" -I "{runtime.tools.mkdir}" -p "{build.path}/ld_h/" recipe.hooks.linking.prelink.2.pattern="{runtime.tools.python3.path}/python3" -I "{runtime.tools.cp}" "{runtime.platform.path}/tools/sdk/ld/{build.flash_ld}" "{build.path}/ld_h/local.eagle.flash.ld.h" recipe.hooks.linking.prelink.3.pattern="{compiler.path}{compiler.c.cmd}" -CC -E -P {build.vtable_flags} {build.mmuflags} "{build.path}/ld_h/local.eagle.flash.ld.h" -o "{build.path}/local.eagle.flash.ld" -recipe.hooks.linking.prelink.4.pattern="{compiler.path}{compiler.c.cmd}" -CC -E -P {build.vtable_flags} {build.mmuflags} "{runtime.platform.path}/tools/sdk/ld/eagle.app.v6.common.ld.h" -o "{build.path}/local.eagle.app.v6.common.ld" +recipe.hooks.linking.prelink.4.pattern="{compiler.path}{compiler.c.cmd}" {build.iramfloat} -CC -E -P {build.vtable_flags} {build.mmuflags} "{runtime.platform.path}/tools/sdk/ld/eagle.app.v6.common.ld.h" -o "{build.path}/local.eagle.app.v6.common.ld" ## Compile c files -recipe.c.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.cpreprocessor.flags} {compiler.c.flags} -D{build.sdk}=1 -DF_CPU={build.f_cpu} {build.lwip_flags} {build.debug_port} {build.debug_level} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" {build.led} {build.flash_flags} {compiler.c.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" +recipe.c.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.cpreprocessor.flags} {compiler.c.flags} -D{build.sdk}=1 -DF_CPU={build.f_cpu} {build.lwip_flags} {build.debug_port} {build.debug_level} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" -DARDUINO_BOARD_ID="{_id}" {build.led} {build.flash_flags} {compiler.c.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" ## Compile c++ files -recipe.cpp.o.pattern="{compiler.path}{compiler.cpp.cmd}" {compiler.cpreprocessor.flags} {compiler.cpp.flags} -D{build.sdk}=1 -DF_CPU={build.f_cpu} {build.lwip_flags} {build.debug_port} {build.debug_level} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" {build.led} {build.flash_flags} {compiler.cpp.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" +recipe.cpp.o.pattern="{compiler.path}{compiler.cpp.cmd}" {compiler.cpreprocessor.flags} {compiler.cpp.flags} -D{build.sdk}=1 -DF_CPU={build.f_cpu} {build.lwip_flags} {build.debug_port} {build.debug_level} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" -DARDUINO_BOARD_ID="{_id}" {build.led} {build.flash_flags} {compiler.cpp.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" ## Compile S files -recipe.S.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.cpreprocessor.flags} {compiler.S.flags} -D{build.sdk}=1 -DF_CPU={build.f_cpu} {build.lwip_flags} {build.debug_port} {build.debug_level} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" {build.led} {build.flash_flags} {compiler.S.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" +recipe.S.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.cpreprocessor.flags} {compiler.S.flags} -D{build.sdk}=1 -DF_CPU={build.f_cpu} {build.lwip_flags} {build.debug_port} {build.debug_level} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} -DARDUINO_BOARD="{build.board}" -DARDUINO_BOARD_ID="{_id}" {build.led} {build.flash_flags} {compiler.S.extra_flags} {build.extra_flags} {includes} "{source_file}" -o "{object_file}" ## Create archives recipe.ar.pattern="{compiler.path}{compiler.ar.cmd}" {compiler.ar.flags} {compiler.ar.extra_flags} "{archive_file_path}" "{object_file}" ## 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= @@ -127,7 +154,7 @@ recipe.objcopy.eep.pattern= ## Create hex recipe.objcopy.hex.1.pattern="{runtime.tools.python3.path}/python3" -I "{runtime.tools.elf2bin}" --eboot "{runtime.tools.eboot}" --app "{build.path}/{build.project_name}.elf" --flash_mode {build.flash_mode} --flash_freq {build.flash_freq} --flash_size {build.flash_size} --path "{runtime.tools.xtensa-lx106-elf-gcc.path}/bin" --out "{build.path}/{build.project_name}.bin" recipe.objcopy.hex.2.pattern="{runtime.tools.python3.path}/python3" -I "{runtime.tools.signing}" --mode sign --privatekey "{build.source.path}/private.key" --bin "{build.path}/{build.project_name}.bin" --out "{build.path}/{build.project_name}.bin.signed" --legacy "{build.path}/{build.project_name}.bin.legacy_sig" -recipe.objcopy.hex.3.pattern="{runtime.tools.python3.path}/python3" -I "{runtime.tools.sizes}" --elf "{build.path}/{build.project_name}.elf" --path "{runtime.tools.xtensa-lx106-elf-gcc.path}/bin" --mmu "{build.mmuflags}" +recipe.objcopy.hex.3.pattern="{runtime.tools.python3.path}/python3" -X utf8 -I "{runtime.tools.sizes}" --elf "{build.path}/{build.project_name}.elf" --path "{runtime.tools.xtensa-lx106-elf-gcc.path}/bin" --mmu "{build.mmuflags}" ## Save hex recipe.output.tmp_file={build.project_name}.bin diff --git a/tests/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 index 0450b03353..7efb6bbd60 100755 --- a/tests/ci/eboot_test.sh +++ b/tests/ci/eboot_test.sh @@ -1,13 +1,14 @@ #!/bin/bash -READELF="$TRAVIS_BUILD_DIR/tools/xtensa-lx106-elf/bin/xtensa-lx106-elf-readelf" - set -ev -cd $TRAVIS_BUILD_DIR/tools +root=$(git rev-parse --show-toplevel) +READELF="$root/tools/xtensa-lx106-elf/bin/xtensa-lx106-elf-readelf" + +cd $root/tools python3 get.py -q -cd $TRAVIS_BUILD_DIR/bootloaders/eboot +cd $root/bootloaders/eboot "$READELF" -x .data -x .text eboot.elf > git.txt make clean diff --git a/tests/ci/host_test.sh b/tests/ci/host_test.sh index ef143f90e1..9ce2ecf0e8 100755 --- a/tests/ci/host_test.sh +++ b/tests/ci/host_test.sh @@ -4,8 +4,8 @@ set -ev -cd $TRAVIS_BUILD_DIR/tests/host - +root=$(git rev-parse --show-toplevel) +cd $root/tests/host make -j2 FORCE32=0 ssl for i in ../../libraries/ESP8266WiFi/examples/WiFiClient/WiFiClient \ @@ -13,7 +13,8 @@ for i in ../../libraries/ESP8266WiFi/examples/WiFiClient/WiFiClient \ ../../libraries/ESP8266WebServer/examples/HelloServer/HelloServer \ ../../libraries/SD/examples/Files/Files \ ../../libraries/LittleFS/examples/LittleFS_Timestamp/LittleFS_Timestamp \ - ../../libraries/LittleFS/examples/SpeedTest/SpeedTest ; do + ../../libraries/LittleFS/examples/SpeedTest/SpeedTest \ + ../../libraries/DNSServer/examples/DNSServer/DNSServer ; do make -j2 D=1 FORCE32=0 $i valgrind --leak-check=full --track-origins=yes --error-limit=no --show-leak-kinds=all --error-exitcode=999 bin/$(basename $i)/$(basename $i) -1 done diff --git a/tests/ci/pkgrefs_test.sh b/tests/ci/pkgrefs_test.sh index ea4f1c235e..a189e1a594 100755 --- a/tests/ci/pkgrefs_test.sh +++ b/tests/ci/pkgrefs_test.sh @@ -2,8 +2,10 @@ set -ev +root=$(git rev-parse --show-toplevel) + fail=0 -for i in $(cat "$TRAVIS_BUILD_DIR/package/package_esp8266com_index.template.json" | jq '.packages[0]."tools" | .[] | .systems[] | "\(.url) \(.checksum)"' | sort -u | sed 's/ /@/'); do +for i in $(cat "$root/package/package_esp8266com_index.template.json" | jq '.packages[0]."tools" | .[] | .systems[] | "\(.url) \(.checksum)"' | sort -u | sed 's/ /@/'); do url=$(echo $i | sed 's/@/ /' | cut -f2 -d\" | cut -f1 -d' ') sha=$(echo $i | sed 's/@/ /' | cut -f2 -d\" | cut -f2 -d' ' | cut -f2 -d:) echo "INFO: Checking $url" diff --git a/tests/ci/style_check.sh b/tests/ci/style_check.sh index 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 a99ff6a9c0..f05eb6b37a 100755 --- a/tests/common.sh +++ b/tests/common.sh @@ -1,256 +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 core_path=$1 + local ide_path=$2 + local hardware_path=$3 + local library_path=$4 + local build_mod=$5 + local build_rem=$6 + local lwip=$7 + + local build_dir="$cache_dir"/build + mkdir -p "$build_dir" + + local build_cache="$cache_dir"/cache + mkdir -p "$build_cache" + + local build_cmd + build_cmd="python3 tools/build.py"\ +" --build_cache $build_cache"\ +" --build_path $build_dir"\ +" --hardware_path $hardware_path"\ +" --ide_path $ide_path"\ +" --library_path $library_path"\ +" --lwIP $lwip"\ +" --board_name generic --verbose --warnings all"\ +" --flash_size 4M1M --keep" + + print_size_info_header >"$cache_dir"/size.log + + local mk_clean_core=1 local testcnt=0 - for sketch in $sketches; do + + for sketch in $ESP8266_ARDUINO_SKETCHES; do testcnt=$(( ($testcnt + 1) % $build_mod )) - if [ $testcnt -ne $build_rem ]; then + if [ $testcnt -ne "$build_rem" ]; then continue # Not ours to do fi + # mkbuildoptglobals.py is optimized around the Arduino IDE 1.x + # behaviour. One way the CI differs from the Arduino IDE is in the + # handling of core and caching core. With the Arduino IDE, each sketch + # has a private copy of core and contributes to a core cache. With the + # CI, there is one shared copy of core for all sketches. When global + # options are used, the shared copy of core and cache are removed before + # and after the build. + # + # Do we need a clean core build? $build_dir/core/* cannot be shared + # between sketches when global options are present. + if [ -s ${sketch}.globals.h ]; then + mk_clean_core=1 + fi + if [ $mk_clean_core -ne 0 ]; then + rm -rf "$build_dir"/core/* + else + # Remove sketch specific files from ./core/ between builds. + rm -rf "$build_dir/core/build.opt" "$build_dir"/core/*.ino.globals.h + fi + if [ -e $cache_dir/core/*.a ]; then # We need to preserve the build.options.json file and replace the last .ino # with this sketch's ino file, or builder will throw everything away. - jq '."sketchLocation" = "'$sketch'"' $build_dir/build.options.json > $build_dir/build.options.json.tmp - mv $build_dir/build.options.json.tmp $build_dir/build.options.json - # Set the time of the cached core.a file to the future so the GIT header - # we regen won't cause the builder to throw it out and rebuild from scratch. - touch -d 'now + 1 day' $cache_dir/core/*.a + jq '."sketchLocation" = "'$sketch'"' $build_dir/build.options.json \ + > "$build_dir"/build.options.json.tmp + mv "$build_dir"/build.options.json.tmp "$build_dir"/build.options.json + if [ $mk_clean_core -ne 0 ]; then + # Hack workaround for CI not handling core rebuild for global options + rm $cache_dir/core/*.a + fi + fi + + if [ -s ${sketch}.globals.h ]; then + # Set to cleanup core at the start of the next build. + mk_clean_core=1 + else + mk_clean_core=0 fi # Clear out the last built sketch, map, elf, bin files, but leave the compiled # objects in the core and libraries available for use so we don't need to rebuild # them each sketch. - rm -rf $build_dir/sketch $build_dir/*.bin $build_dir/*.map $build_dir/*.elf + rm -rf "$build_dir"/sketch \ + "$build_dir"/*.bin \ + "$build_dir"/*.map \ + "$build_dir"/*.elf - local sketchdir=$(dirname $sketch) - local sketchdirname=$(basename $sketchdir) - local sketchname=$(basename $sketch) - if [[ "${sketchdirname}.ino" != "$sketchname" ]]; then - echo "Skipping $sketch, because it is not the main sketch file"; - continue - fi; - if [[ -f "$sketchdir/.test.skip" ]]; then - echo -e "\n ------------ Skipping $sketch ------------ \n"; - continue - fi - if [[ $(skip_ino $sketch) = 1 ]]; then - echo -e "\n ------------ Skipping $sketch ------------ \n"; + local sketchdir + sketchdir=$(dirname $sketch) + + local sketchdirname + sketchdirname=$(basename $sketchdir) + + local sketchname + sketchname=$(basename $sketch) + + local skip + skip=$(skip_sketch "$sketch" "$sketchname" "$sketchdir" "$sketchdirname") + if [ -n "$skip" ]; then + echo "$skip" continue fi - echo -e "\n ------------ Building $sketch ------------ \n"; - # $arduino --verify $sketch; - if [ "$WINDOWS" == "1" ]; then - sketch=$(echo $sketch | sed 's/^\/c//') - # MINGW will try to be helpful and silently convert args that look like paths to point to a spot inside the MinGW dir. This breaks everything. - # http://www.mingw.org/wiki/Posix_path_conversion - # https://stackoverflow.com/questions/7250130/how-to-stop-mingw-and-msys-from-mangling-path-names-given-at-the-command-line#34386471 - export MSYS2_ARG_CONV_EXC="*" - export MSYS_NO_PATHCONV=1 - fi + + echo ::group::Building $sketch echo "$build_cmd $sketch" - time ($build_cmd $sketch >build.log) - local result=$? + + local result + time $build_cmd $sketch >"$cache_dir"/build.log \ + && result=0 || result=1 + if [ $result -ne 0 ]; then - echo "Build failed ($1)" - echo "Build log:" - cat build.log - set -e + echo ::error::Build failed for $sketch + cat "$cache_dir/build.log" + echo ::endgroup:: return $result else - local warns=$( grep -c warning: build.log ) - if [ $warns -ne 0 ]; then - echo "Warnings detected, log follows:" - cat build.log - fi + grep -s -c warning: "$cache_dir"/build.log \ + && step_summary "$sketch warnings" "$cache_dir/build.log" fi - rm build.log - print_size_info $build_dir/*.elf >>size.log + + print_size_info "$core_path"/tools/xtensa-lx106-elf/bin/xtensa-lx106-elf-size \ + $build_dir/*.elf >>$cache_dir/size.log + + echo ::endgroup:: done - set -e +} + +function check_hash() +{ + local file=$1 + local hash=$2 + + local shasum + case ${RUNNER_OS-} in + "macOS") + shasum="shasum -a 512" + ;; + *) + shasum="sha512sum" + ;; + esac + + echo "$hash $file" | $shasum -c - +} + +function fetch_and_unpack() +{ + local archive=$1 + local hash=$2 + local url=$3 + + test -r "$archive" \ + && check_hash "$archive" "$hash" \ + || { pushd "$cache_dir" + curl --output "$archive" --location "$url"; + check_hash "$archive" "$hash"; + popd; + mv "$cache_dir/$archive" ./"$archive"; } + + case $archive in + *".zip") + unzip -q "$archive" + ;; + *) + tar xf "$archive" + ;; + esac +} + +function install_library() +{ + local lib_path=$1 + local name=$2 + local archive=$3 + local hash=$4 + local url=$5 + + fetch_and_unpack "$archive" "$hash" "$url" + mkdir -p "$lib_path" + rm -rf "$lib_path/$name" + mv "$name" "$lib_path/$name" } function install_libraries() { - mkdir -p $HOME/Arduino/libraries - pushd $HOME/Arduino/libraries + local core_path=$1 + local lib_path=$2 - # install ArduinoJson library - { test -r ArduinoJson-v6.11.0.zip || curl --output ArduinoJson-v6.11.0.zip -L https://github.com/bblanchon/ArduinoJson/releases/download/v6.11.0/ArduinoJson-v6.11.0.zip; } && unzip -q ArduinoJson-v6.11.0.zip + mkdir -p "$core_path"/tools/dist + pushd "$core_path"/tools/dist + + install_library "$lib_path" \ + "ArduinoJson" \ + "ArduinoJson-v6.11.5.zip" \ + "8b836c862e69e60c4357a5ed7cbcf1310a3bb1c6bd284fe028faaa3d9d7eed319d10febc8a6a3e06040d1c73aaba5ca487aeffe87ae9388dc4ae1677a64d602c" \ + "https://github.com/bblanchon/ArduinoJson/releases/download/v6.11.5/ArduinoJson-v6.11.5.zip" popd } function install_ide() { - local idever='nightly' - local ideurl='https://www.arduino.cc/download.php?f=/arduino-nightly' + # TODO replace ide distribution + arduino-builder with arduino-cli + local idever='1.8.19' + local ideurl="https://downloads.arduino.cc/arduino-$idever" - #local idever='1.8.10' - #local ideurl="https://downloads.arduino.cc/arduino-$idever" + echo "Arduino IDE ${idever}" - echo "using Arduino IDE distribution ${idever}" + local core_path=$1 + local ide_path=$2 - local ide_path=$1 - local core_path=$2 - local debug=$3 mkdir -p ${core_path}/tools/dist - if [ "$WINDOWS" = "1" ]; then - test -r ${core_path}/tools/dist/arduino-windows.zip || curl --output ${core_path}/tools/dist/arduino-windows.zip -L "${ideurl}-windows.zip" - unzip -q ${core_path}/tools/dist/arduino-windows.zip - mv arduino-${idever} arduino-distrib - elif [ "$MACOSX" = "1" ]; then - # MACOS only has next-to-obsolete Python2 installed. Install Python 3 from python.org - wget -q https://www.python.org/ftp/python/3.7.4/python-3.7.4-macosx10.9.pkg - sudo installer -pkg python-3.7.4-macosx10.9.pkg -target / - # Install the Python3 certificates, because SSL connections fail w/o them and of course they aren't installed by default. - ( cd "/Applications/Python 3.7/" && sudo "./Install Certificates.command" ) + pushd ${core_path}/tools/dist + + if [ "${RUNNER_OS-}" = "Windows" ]; then + fetch_and_unpack "arduino-windows.zip" \ + "c4072d808aea3848bceff5772f9d1e56a0fde02366b5aa523d10975c54eee2ca8def25ee466abbc88995aa323d475065ad8eb30bf35a2aaf07f9473f9168e2da" \ + "${ideurl}-windows.zip" + mv arduino-$idever arduino-distrib + elif [ "${RUNNER_OS-}" = "macOS" ]; then + fetch_and_unpack "arduino-macos.zip" \ + "053b0c1e70da9176680264e40fcb9502f45ca5a879aeb8b6f71282b38bfdb87c63ebc6b88e35ea70a73720ad439d828cc8cb110e4c6ab07357126a36ee396325" \ + "${ideurl}-macosx.zip" # Hack to place arduino-builder in the same spot as sane OSes - test -r ${core_path}/tools/dist/arduino-macos.zip || wget -q -O ${core_path}/tools/dist/arduino-macos.zip "${ideurl}-macosx.zip" - unzip -q ${core_path}/tools/dist/arduino-macos.zip mv Arduino.app arduino-distrib mv arduino-distrib/Contents/Java/* arduino-distrib/. else - test -r ${core_path}/tools/dist/arduino-linux.tar.xz || wget -q -O ${core_path}/tools/dist/arduino-linux.tar.xz "${ideurl}-linux64.tar.xz" - tar xf ${core_path}/tools/dist/arduino-linux.tar.xz - mv arduino-${idever} arduino-distrib - fi - mv arduino-distrib $ide_path - cd $ide_path/hardware - mkdir esp8266com - cd esp8266com - if [ "$WINDOWS" = "1" ]; then - cp -a $core_path esp8266 - else - ln -s $core_path esp8266 + fetch_and_unpack "arduino-linux.tar.xz" \ + "9328abf8778200019ed40d4fc0e6afb03a4cee8baaffbcea7dd3626477e14243f779eaa946c809fb153a542bf2ed60cf11a5f135c91ecccb1243c1387be95328" \ + "${ideurl}-linux64.tar.xz" + mv arduino-$idever arduino-distrib fi + + mv arduino-distrib "$ide_path" + popd +} + +function install_core() +{ + local core_path=$1 + local hardware_core_path=$2 + local debug=$3 + + pushd "${core_path}" + local debug_flags="" if [ "$debug" = "debug" ]; then - debug_flags="-DDEBUG_ESP_PORT=Serial -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM" + debug_flags="-DDEBUG_ESP_PORT=Serial -DDEBUG_ESP_SSL -DDEBUG_ESP_TLS_MEM"\ +" -DDEBUG_ESP_HTTP_CLIENT -DDEBUG_ESP_HTTP_SERVER -DDEBUG_ESP_CORE -DDEBUG_ESP_WIFI"\ +" -DDEBUG_ESP_HTTP_UPDATE -DDEBUG_ESP_UPDATER -DDEBUG_ESP_OTA -DDEBUG_ESP_OOM" fi - # Set custom warnings for all builds (i.e. could add -Wextra at some point) - echo "compiler.c.extra_flags=-Wall -Wextra -Werror $debug_flags" > esp8266/platform.local.txt - echo "compiler.cpp.extra_flags=-Wall -Wextra -Werror $debug_flags" >> esp8266/platform.local.txt + + # Set our custom warnings for all builds + { echo "compiler.c.extra_flags=-Wall -Wextra -Werror $debug_flags"; + echo "compiler.cpp.extra_flags=-Wall -Wextra -Werror $debug_flags"; + echo "mkbuildoptglobals.extra_flags=--ci --cache_core"; } \ + > platform.local.txt echo -e "\n----platform.local.txt----" - cat esp8266/platform.local.txt + cat platform.local.txt echo -e "\n----\n" - cd esp8266/tools + + pushd tools python3 get.py -q - if [ "$WINDOWS" = "1" ]; then - # Because the symlinks don't work well under Win32, we need to add the path to this copy, not the original... - relbin=$(realpath $PWD/xtensa-lx106-elf/bin) - export PATH="$ide_path:$relbin:$PATH" + + popd + popd + + local core_dir + core_dir=$(dirname "$hardware_core_path") + mkdir -p "$core_dir" + + if [ "${RUNNER_OS-}" = "Windows" ]; then + cp -a "$core_path" "${core_dir}/esp8266" else - export PATH="$ide_path:$core_path/tools/xtensa-lx106-elf/bin:$PATH" + ln -s "$core_path" "$hardware_core_path" fi } function install_arduino() { + echo ::group::Install arduino local debug=$1 - # Install Arduino IDE and required libraries - echo -e "travis_fold:start:sketch_test_env_prepare" - cd $TRAVIS_BUILD_DIR - install_ide $HOME/arduino_ide $TRAVIS_BUILD_DIR $debug - cd $TRAVIS_BUILD_DIR - install_libraries - echo -e "travis_fold:end:sketch_test_env_prepare" + + test -d "$ESP8266_ARDUINO_IDE" \ + || install_ide "$ESP8266_ARDUINO_BUILD_DIR" "$ESP8266_ARDUINO_IDE" + + local hardware_core_path="$ESP8266_ARDUINO_HARDWARE/esp8266com/esp8266" + test -d "$hardware_core_path" \ + || install_core "$ESP8266_ARDUINO_BUILD_DIR" "$hardware_core_path" "$debug" + + install_libraries "$ESP8266_ARDUINO_BUILD_DIR" "$ESP8266_ARDUINO_LIBRARIES" + + echo ::endgroup:: +} + +function arduino_lwip_menu_option() +{ + case $1 in + "default") + echo "lm2f" + ;; + "IPv6") + echo "lm6f" + ;; + esac } function build_sketches_with_arduino() { local build_mod=$1 local build_rem=$2 - local lwip=$3 - # Compile sketches - echo -e "travis_fold:start:sketch_test" - build_sketches $HOME/arduino_ide $TRAVIS_BUILD_DIR/libraries "-l $HOME/Arduino/libraries" $build_mod $build_rem $lwip - echo -e "travis_fold:end:sketch_test" + local lwip + lwip=$(arduino_lwip_menu_option $3) - # Generate size report - echo -e "travis_fold:start:size_report" - cat size.log - echo -e "travis_fold:end:size_report" + build_sketches "$ESP8266_ARDUINO_BUILD_DIR" \ + "$ESP8266_ARDUINO_IDE" \ + "$ESP8266_ARDUINO_HARDWARE" \ + "$ESP8266_ARDUINO_LIBRARIES" \ + "$build_mod" "$build_rem" "$lwip" + step_summary "Size report" "$cache_dir/size.log" } +function install_platformio() +{ + echo ::group::Install PlatformIO -set -e + local board=$1 -if [ -z "$TRAVIS_BUILD_DIR" ]; then - echo "TRAVIS_BUILD_DIR is not set, trying to guess:" - pushd $(dirname $0)/../ > /dev/null - TRAVIS_BUILD_DIR=$PWD - popd > /dev/null - echo "TRAVIS_BUILD_DIR=$TRAVIS_BUILD_DIR" -fi + pushd $ESP8266_ARDUINO_BUILD_DIR/tools + python3 get.py -q + popd + + # we should reference our up-to-date build tools + # ref. https://docs.platformio.org/en/latest/core/userguide/pkg/cmd_install.html + pio pkg install --global --skip-dependencies --platform "https://github.com/platformio/platform-espressif8266.git" + + local framework_symlink="framework-arduinoespressif8266 @ symlink://${ESP8266_ARDUINO_BUILD_DIR}" + local toolchain_symlink="toolchain-xtensa @ symlink://${ESP8266_ARDUINO_BUILD_DIR}/tools/xtensa-lx106-elf/" + + # pre-generate config; pio-ci with multiple '-O' replace each other instead of appending to the same named list + # (and, it is much nicer to write this instead of a multi-line cmdline with several large strings) + cat < $cache_dir/platformio.ini +[env:$board] +platform = espressif8266 +board = $board +framework = arduino +platform_packages = + ${framework_symlink} + ${toolchain_symlink} +EOF + + # Install dependencies: + # - esp8266/examples/ConfigFile + pio pkg install --global --library "ArduinoJson@^6.11.0" + + echo ::endgroup:: +} + +function build_sketches_with_platformio() +{ + local build_mod=$1 + local build_rem=$2 + local testcnt=0 + + for sketch in $ESP8266_ARDUINO_SKETCHES; do + testcnt=$(( ($testcnt + 1) % $build_mod )) + if [ $testcnt -ne $build_rem ]; then + continue # Not ours to do + fi + + local sketchdir + sketchdir=$(dirname $sketch) + + local sketchdirname + sketchdirname=$(basename $sketchdir) + + local sketchname + sketchname=$(basename $sketch) + + local skip + skip=$(skip_sketch "$sketch" "$sketchname" "$sketchdir" "$sketchdirname") + if [ -n "$skip" ]; then + echo "$skip" + continue + fi + + echo ::group::Building $sketch + + local result + time pio ci \ + --verbose \ + --project-conf $cache_dir/platformio.ini \ + $sketchdir >$cache_dir/build.log 2>&1 \ + && result=0 || result=1 + if [ $result -ne 0 ]; then + echo ::error::Build failed for $sketch + cat "$cache_dir/build.log" + echo ::endgroup:: + return $result + fi + + echo ::endgroup:: + done +} + +if [ -z "${ESP8266_ARDUINO_BUILD_DIR-}" ]; then + ESP8266_ARDUINO_BUILD_DIR=$(git rev-parse --show-toplevel) + echo "Using ESP8266_ARDUINO_BUILD_DIR=$ESP8266_ARDUINO_BUILD_DIR" +fi diff --git a/tests/debug.sh b/tests/debug.sh deleted file mode 100755 index bbca35ff53..0000000000 --- a/tests/debug.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash - -cache_dir=$(mktemp -d) - -source "$TRAVIS_BUILD_DIR"/tests/common.sh - -if [ "$BUILD_PARITY" = "even" ]; then - mod=2 - rem=0 -elif [ "$BUILD_PARITY" = "odd" ]; then - mod=2 - rem=1 -fi - -install_arduino debug -build_sketches_with_arduino "$mod" "$rem" lm2f - -rm -rf "$cache_dir" diff --git a/tests/debug6.sh b/tests/debug6.sh deleted file mode 100644 index b0a1aeaaba..0000000000 --- a/tests/debug6.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bash - -cache_dir=$(mktemp -d) - -source "$TRAVIS_BUILD_DIR"/tests/common.sh - -if [ -z "$BUILD_PARITY" ]; then - mod=1 - rem=0 -elif [ "$BUILD_PARITY" = "even" ]; then - mod=2 - rem=0 -elif [ "$BUILD_PARITY" = "odd" ]; then - mod=2 - rem=1 -fi - -install_arduino debug -build_sketches_with_arduino "$mod" "$rem" lm6f - -rm -rf "$cache_dir" diff --git a/tests/device/Makefile b/tests/device/Makefile index 8bc145a532..cbbd34728a 100644 --- a/tests/device/Makefile +++ b/tests/device/Makefile @@ -65,8 +65,8 @@ $(TEST_LIST): ifeq ("$(MOCK)", "1") @echo Compiling $(notdir $@) (cd ../host; make D=$(V) ULIBDIRS=../device/libraries/BSTest ../device/$(@:%.ino=%)) - $(SILENT)source $(BS_DIR)/virtualenv/bin/activate && \ - $(PYTHON) $(BS_DIR)/runner.py \ + $(SILENT)$(BS_DIR)/virtualenv/bin/python \ + $(BS_DIR)/runner.py \ $(RUNNER_DEBUG_FLAG) \ -e "$(ESP8266_CORE_PATH)/tests/host/bin/$(@:%.ino=%)" \ -n $(basename $(notdir $@)) \ @@ -120,8 +120,8 @@ ifneq ("$(NO_RUN)","1") --port $(UPLOAD_PORT) \ --baud $(UPLOAD_BAUD) \ read_flash_status $(REDIR) # reset - $(SILENT)source $(BS_DIR)/virtualenv/bin/activate && \ - $(PYTHON) $(BS_DIR)/runner.py \ + $(SILENT)$(BS_DIR)/virtualenv/bin/python \ + $(BS_DIR)/runner.py \ $(RUNNER_DEBUG_FLAG) \ -p $(UPLOAD_PORT) \ -n $(basename $(notdir $@)) \ diff --git a/tests/device/libraries/BSTest/Makefile b/tests/device/libraries/BSTest/Makefile index 93c181f5fb..78e7a19a02 100644 --- a/tests/device/libraries/BSTest/Makefile +++ b/tests/device/libraries/BSTest/Makefile @@ -11,11 +11,11 @@ clean: rm -rf $(TEST_EXECUTABLE) $(PYTHON_ENV_DIR): - virtualenv --python=$(PYTHON) --no-site-packages $(PYTHON_ENV_DIR) - . $(PYTHON_ENV_DIR)/bin/activate && pip install -r requirements.txt + $(PYTHON) -mvenv $(PYTHON_ENV_DIR) + $(PYTHON_ENV_DIR)/bin/pip install -r requirements.txt test: $(TEST_EXECUTABLE) $(PYTHON_ENV_DIR) - . $(PYTHON_ENV_DIR)/bin/activate && $(PYTHON) runner.py -e $(TEST_EXECUTABLE) -m test/test.py + $(PYTHON_ENV_DIR)/bin/python runner.py -e $(TEST_EXECUTABLE) -m test/test.py $(TEST_EXECUTABLE): test/test.cpp g++ -std=c++11 -Isrc -o $@ test/test.cpp diff --git a/tests/device/libraries/BSTest/runner.py b/tests/device/libraries/BSTest/runner.py index 97849a5e32..5c9dddfef9 100644 --- a/tests/device/libraries/BSTest/runner.py +++ b/tests/device/libraries/BSTest/runner.py @@ -8,7 +8,9 @@ import argparse import serial import subprocess -import imp + +from importlib.machinery import SourceFileLoader + try: from configparser import ConfigParser except: @@ -278,12 +280,12 @@ def main(): if args.env_file is not None: cfg = ConfigParser() cfg.optionxform = str - with args.env_file as fp: - cfg.readfp(fp) + with args.env_file as env: + cfg.read_file(env) env_vars = cfg.items('global') mocks = {} if args.mock is not None: - mocks_mod = imp.load_source('mocks', args.mock) + mocks_mod = SourceFileLoader('mocks', args.mock).load_module() mocks = mock_decorators.env with spawn_func(spawn_arg) as sp: ts = run_tests(sp, name, mocks, env_vars) diff --git a/tests/device/libraries/BSTest/src/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 2485af0923..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, reproducible 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 7c1883a085..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, 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) + /* 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_spi_flash/test_spi_flash.ino b/tests/device/test_spi_flash/test_spi_flash.ino index b1e18241f5..16fa4a9750 100644 --- a/tests/device/test_spi_flash/test_spi_flash.ino +++ b/tests/device/test_spi_flash/test_spi_flash.ino @@ -1,5 +1,4 @@ #include -#include BS_ENV_DECLARE(); @@ -176,6 +175,11 @@ TEST_CASE("Unaligned page cross only", "[spi_flash]") CHECK(testFlash(0xa0000 + 255, 1, 2)); } +TEST_CASE("Unaligned page cross with unaligned size (#8372, #8588, #8605)", "[spi_flash]") +{ + CHECK(testFlash(0xa00b, 0, 202)); +} + void loop () { } diff --git a/tests/host/Makefile b/tests/host/Makefile index 8cb9d89297..97a4c671fb 100644 --- a/tests/host/Makefile +++ b/tests/host/Makefile @@ -12,31 +12,25 @@ RANLIB ?= ranlib MAKEFILE = $(word 1, $(MAKEFILE_LIST)) -CXX = $(shell for i in g++-10 g++-9 g++-8 g++; do which $$i > /dev/null && { echo $$i; break; } done) -CC = $(shell for i in gcc-10 gcc-9 gcc-8 gcc; do which $$i > /dev/null && { echo $$i; break; } done) -GCOV = $(shell for i in gcov-10 gcov-9 gcov-8 gcov; do which $$i > /dev/null && { echo $$i; break; } done) -$(warning using $(CXX)) -ifeq ($(CXX),g++) -CXXFLAGS += -std=gnu++11 -else -CXXFLAGS += -std=gnu++17 -endif -ifeq ($(CC),gcc) -CFLAGS += -std=gnu11 -else -CFLAGS += -std=gnu17 -endif +# Prefer named GCC (and, specifically, GCC10), same as platform.txt / platformio_build.py +find_tool = $(shell for name in $(1) $(2); do which $$name && break; done 2>/dev/null) +CXX = $(call find_tool,g++-10,g++) +CC = $(call find_tool,gcc-10,gcc) +GCOV = $(call find_tool,gcov-10,gcov) -# I wasn't able to build with clang when -coverage flag is enabled, forcing GCC on OS X -ifeq ($(shell uname -s),Darwin) -CC ?= gcc -CXX ?= g++ -endif -GCOV ?= gcov +$(warning using $(CXX) and $(CC)) + +GCOV ?= gcov VALGRIND ?= valgrind -LCOV ?= lcov --gcov-tool $(GCOV) -GENHTML ?= genhtml +LCOV ?= lcov --gcov-tool $(GCOV) +GENHTML ?= genhtml + +# Board fild will be built with GCC10, but we have some limited ability to build with older versions +# *Always* push the standard used in the platform.txt +CXXFLAGS += -std=gnu++17 +CFLAGS += -std=gnu17 +# 32-bit mode is prefered, but not required ifeq ($(FORCE32),1) SIZEOFLONG = $(shell echo 'int main(){return sizeof(long);}'|$(CXX) -m32 -x c++ - -o sizeoflong 2>/dev/null && ./sizeoflong; echo $$?; rm -f sizeoflong;) ifneq ($(SIZEOFLONG),4) @@ -57,6 +51,7 @@ endif OUTPUT_BINARY := $(BINDIR)/host_tests LCOV_DIRECTORY := $(BINDIR)/../lcov +# Hide full build commands by default ifeq ($(V), 0) VERBC = @echo "C $@"; VERBCXX = @echo "C++ $@"; @@ -73,6 +68,30 @@ endif $(shell mkdir -p $(BINDIR)) +# Core files sometimes override libc functions, check when necessary to hide them +# TODO proper configure script / other build system? +ifeq (,$(wildcard $(BINDIR)/.have_strlcpy)) +$(shell printf '#include \nint main(){char a[4]{}; char b[4]{}; strlcpy(&a[0], &b[0], sizeof(a)); return 0;}\n' | \ + $(CXX) -x c++ - -o $(BINDIR)/.have_strlcpy 2>/dev/null || ( printf '#!/bin/sh\nexit 1\n' > $(BINDIR)/.have_strlcpy ; chmod +x $(BINDIR)/.have_strlcpy; )) +endif + +$(shell $(BINDIR)/.have_strlcpy) +ifneq ($(.SHELLSTATUS), 0) +FLAGS += -DSTRLCPY_MISSING +endif + +ifeq (,$(wildcard $(BINDIR)/.have_strlcat)) +$(shell printf '#include \nint main(){char a[4]{}; strlcat(&a[0], "test", sizeof(a)); return 0;}\n' | \ + $(CXX) -x c++ - -o $(BINDIR)/.have_strlcat 2>/dev/null || ( printf '#!/bin/sh\nexit 1\n' > $(BINDIR)/.have_strlcat ; chmod +x $(BINDIR)/.have_strlcat; )) +endif + +$(shell $(BINDIR)/.have_strlcat) +ifneq ($(.SHELLSTATUS), 0) +FLAGS += -DSTRLCAT_MISSING +endif + +# Actual build recipes + CORE_CPP_FILES := \ $(addprefix $(abspath $(CORE_PATH))/,\ debug.cpp \ @@ -97,6 +116,7 @@ CORE_CPP_FILES := \ HardwareSerial.cpp \ crc32.cpp \ Updater.cpp \ + time.cpp \ ) \ $(addprefix $(abspath $(LIBRARIES_PATH)/ESP8266SdFat/src)/, \ FatLib/FatFile.cpp \ @@ -104,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) \ @@ -131,7 +155,7 @@ MOCK_CPP_FILES_COMMON := \ MockUART.cpp \ MockTools.cpp \ MocklwIP.cpp \ - MockDigital.cpp \ + HostWiring.cpp \ ) MOCK_CPP_FILES := $(MOCK_CPP_FILES_COMMON) \ @@ -145,6 +169,7 @@ MOCK_CPP_FILES_EMU := $(MOCK_CPP_FILES_COMMON) \ ArduinoMainUdp.cpp \ ArduinoMainSpiffs.cpp \ ArduinoMainLittlefs.cpp \ + DhcpServer.cpp \ user_interface.cpp \ ) @@ -169,6 +194,8 @@ INC_PATHS += \ ../../tools/sdk/lwip2/include \ ) +TEST_ARGS ?= + TEST_CPP_FILES := \ fs/test_fs.cpp \ core/test_pgmspace.cpp \ @@ -231,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 @@ -341,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 := \ @@ -350,7 +378,6 @@ MOCK_ARDUINO_LIBS := \ MockWiFiServerSocket.cpp \ MockWiFiServer.cpp \ UdpContextSocket.cpp \ - HostWiring.cpp \ MockEsp.cpp \ MockEEPROM.cpp \ MockSPI.cpp \ @@ -371,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) @@ -391,7 +419,7 @@ $(BINDIR)/fullcore.a: $(FULLCORE_OBJECTS_ISOLATED) ifeq ($(INO),) %: - make INO=$@.ino $(BINDIR)/$(abspath $@) + $(MAKE) INO=$@.ino $(BINDIR)/$(abspath $@) else diff --git a/tests/host/common/Arduino.cpp b/tests/host/common/Arduino.cpp index 780c4adc14..4b1d7070de 100644 --- a/tests/host/common/Arduino.cpp +++ b/tests/host/common/Arduino.cpp @@ -1,23 +1,26 @@ /* 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() @@ -38,9 +41,15 @@ extern "C" unsigned long micros() 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() @@ -48,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; @@ -67,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) @@ -76,7 +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 051ca0dbc1..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,279 +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); - - // 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(); - 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 2d1d7c6e83..a0bc7610d8 100644 --- a/tests/host/common/ClientContextSocket.cpp +++ b/tests/host/common/ClientContextSocket.cpp @@ -39,168 +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 - // 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)); + 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; - } + return -1; + } + ret = 0; + } - ccinbufsize += ret; - return ret; + 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) { // 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 > 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 (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); - } - } + 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 - 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 dc6514ae40..cce3648c58 100644 --- a/tests/host/common/MockUART.cpp +++ b/tests/host/common/MockUART.cpp @@ -28,475 +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); + } -bool -uart_swap(uart_t* uart, int tx_pin) -{ - (void) uart; - (void) tx_pin; - return true; -} + bool uart_swap(uart_t* uart, int tx_pin) + { + (void)uart; + (void)tx_pin; + return true; + } -bool -uart_set_tx(uart_t* uart, int tx_pin) -{ - (void) uart; - (void) tx_pin; - return true; -} + bool uart_set_tx(uart_t* uart, int tx_pin) + { + (void)uart; + (void)tx_pin; + return true; + } -bool -uart_set_pins(uart_t* uart, int tx, int rx) -{ - (void) uart; - (void) tx; - (void) rx; - return true; -} + 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() + int uart_get_debug() + { + return s_uart_debug_nr; + } + + void uart_start_detect_baudrate(int uart_nr) + { + (void)uart_nr; + } + + 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; } - -}; - - -size_t uart_peek_available (uart_t* uart) { return 0; } -const char* uart_peek_buffer (uart_t* uart) { return nullptr; } -void uart_peek_consume (uart_t* uart, size_t consume) { (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 a4c784c7fb..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,9 +94,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 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__) "\""))) +#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 IRAM_ATTR @@ -108,10 +115,9 @@ typedef enum { #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/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 744d7872d4..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 -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) @@ -219,60 +217,62 @@ 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 char* data, size_t size) { - ssize_t ret = mockWrite(_sock, (const uint8_t*)data, size, _timeout_ms); - if (ret < 0) - { - abort(); - return 0; - } - return ret; + ssize_t ret = mockWrite(_sock, (const uint8_t*)data, size, _timeout_ms); + if (ret < 0) + { + 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; @@ -280,13 +280,13 @@ class ClientContext // return a pointer to available data buffer (size = peekAvailable()) // semantic forbids any kind of read() before calling peekConsume() - const char* peekBuffer () + const char* peekBuffer() { return _inbuf; } // return number of byte accessible by peekBuffer() - size_t peekAvailable () + size_t peekAvailable() { ssize_t ret = mockPeekBytes(_sock, nullptr, 0, 0, _inbuf, _inbufsize); if (ret < 0) @@ -298,7 +298,7 @@ class ClientContext } // consume bytes after use (see peekBuffer) - void peekConsume (size_t consume) + void peekConsume(size_t consume) { assert(consume <= _inbufsize); memmove(_inbuf, _inbuf + consume, _inbufsize - consume); @@ -306,22 +306,21 @@ class ClientContext } private: + discard_cb_t _discard_cb = nullptr; + void* _discard_cb_arg = nullptr; - 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 0194271d9a..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,24 +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); } + 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 } @@ -130,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(); // @@ -172,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 cd844545d0..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"); @@ -135,13 +139,21 @@ TEST_CASE("String concantenation", "[core][String]") 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"); + REQUIRE(str + == "abcdeabcde9872147483647-2147483648691969-123321-1.011.01-9223372036854775808-" + "9223372036854775808"); str += LLONG_MAX; - REQUIRE(str == "abcdeabcde9872147483647-2147483648691969-123321-1.011.01-9223372036854775808-92233720368547758089223372036854775807"); + REQUIRE(str + == "abcdeabcde9872147483647-2147483648691969-123321-1.011.01-9223372036854775808-" + "92233720368547758089223372036854775807"); str += ULLONG_MAX; - REQUIRE(str == "abcdeabcde9872147483647-2147483648691969-123321-1.011.01-9223372036854775808-9223372036854775808922337203685477580718446744073709551615"); + 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"); + REQUIRE(str + == "abcdeabcde9872147483647-2147483648691969-123321-1.011.01-9223372036854775808-" + "9223372036854775808922337203685477580718446744073709551615ffffffffffffffff"); str = "clean"; REQUIRE(str.concat(str) == true); REQUIRE(str == "cleanclean"); @@ -156,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"); } @@ -211,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(); @@ -222,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]") @@ -246,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'); @@ -268,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); @@ -299,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 == "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 == "0123456789a"); - REQUIRE(s.length() == 11); - s += "bcde"; + REQUIRE(s == "01234567"); + REQUIRE(s.length() == 8); + s += "8"; 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 == "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 @@ -426,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; @@ -502,105 +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)); - } + 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); + 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/sys/pgmspace.h b/tests/host/sys/pgmspace.h index ecc4558351..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 ea454069c5..3bc90e52f0 100755 --- a/tests/restyle.sh +++ b/tests/restyle.sh @@ -1,50 +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/debug* -cores/esp8266/core_esp8266_si2c.cpp -cores/esp8266/StreamString.* -cores/esp8266/StreamSend.* -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 673f8d8068..0ccfa8f4d9 100755 --- a/tools/boards.txt.py +++ b/tools/boards.txt.py @@ -112,25 +112,30 @@ '+-----------------+------------+------------------+', '| GND | | GND |', '+-----------------+------------+------------------+', - '| TX or GPIO2\* | | RX |', + '| TX or GPIO2 | | |', + '| [#tx_or_gpio2]_ | RX | |', '+-----------------+------------+------------------+', '| RX | | TX |', '+-----------------+------------+------------------+', '| GPIO0 | PullUp | DTR |', '+-----------------+------------+------------------+', - '| Reset\* | PullUp | RTS |', + '| Reset | | |', + '| [#reset]_ | PullUp | RTS |', '+-----------------+------------+------------------+', - '| GPIO15\* | PullDown | |', + '| GPIO15 | | |', + '| [#gpio15]_ | PullDown | |', '+-----------------+------------+------------------+', - '| CH\_PD | PullUp | |', + '| CH\\_PD | | |', + '| [#ch_pd]_ | PullUp | |', '+-----------------+------------+------------------+', '', - '- Note', - '- GPIO15 is also named MTDO', - '- Reset is also named RSBT or REST (adding PullUp improves the', + '.. rubric:: Notes', + '', + '.. [#tx_or_gpio2] GPIO15 is also named MTDO', + '.. [#reset] Reset is also named RSBT or REST (adding PullUp improves the', ' stability of the module)', - '- GPIO2 is alternative TX for the boot loader mode', - '- **Directly connecting a pin to VCC or GND is not a substitute for a', + '.. [#gpio15] GPIO2 is alternative TX for the boot loader mode', + '.. [#ch_pd] **Directly connecting a pin to VCC or GND is not a substitute for a', ' PullUp or PullDown resistor, doing this can break upload management', ' and the serial console, instability has also been noted in some', ' cases.**', @@ -161,15 +166,16 @@ '+---------------+------------+------------------+', '| GPIO0 | | GND |', '+---------------+------------+------------------+', - '| Reset | | RTS\* |', + '| Reset | | RTS [#rts]_ |', '+---------------+------------+------------------+', '| GPIO15 | PullDown | |', '+---------------+------------+------------------+', - '| CH\_PD | PullUp | |', + '| CH\\_PD | PullUp | |', '+---------------+------------+------------------+', '', - '- Note', - '- if no RTS is used a manual power toggle is needed', + '.. rubric:: Notes', + '', + '.. [#rts] if no RTS is used a manual power toggle is needed', '', 'Minimal Hardware Setup for Running only', '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', @@ -187,7 +193,7 @@ '+----------+------------+----------------+', '| GPIO15 | PullDown | |', '+----------+------------+----------------+', - '| CH\_PD | PullUp | |', + '| CH\\_PD | PullUp | |', '+----------+------------+----------------+', '', 'Minimal', @@ -249,7 +255,11 @@ 'boot mode', '~~~~~~~~~', '', - 'the first value respects the pin setup of the Pins 0, 2 and 15.', + 'the first value respects the pin setup of the Pins 0, 2 and 15', + '', + '.. code-block::', + '', + ' Number = (GPIO15 << 2) | (GPIO0 << 1) | GPIO2', '', '+----------+----------+---------+---------+-------------+', '| Number | GPIO15 | GPIO0 | GPIO2 | Mode |', @@ -271,7 +281,6 @@ '| 7 | 3.3V | 3.3V | 3.3V | SDIO |', '+----------+----------+---------+---------+-------------+', '', - 'note: - number = ((GPIO15 << 2) \| (GPIO0 << 1) \| GPIO2);', ], }), ( 'esp8285', { @@ -436,6 +445,23 @@ ], 'desc': [ 'ESPresso Lite 2.0 is an Arduino-compatible Wi-Fi development board based on an earlier V1 (beta version). Re-designed together with Cytron Technologies, the newly-revised ESPresso Lite V2.0 features the auto-load/auto-program function, eliminating the previous need to reset the board manually before flashing a new program. It also feature two user programmable side buttons and a reset button. The special distinctive features of on-board pads for I2C sensor and actuator is retained.', ] }), +( 'mercury1', { + 'name': 'Mercury 1.0', + 'opts': { + '.build.board': 'mercury', + '.build.variant': 'mercury_v1', + }, + 'macro': [ + 'resetmethod_nodemcu', + 'flashmode_dio', + 'flashfreq_40', + '4M', + ], + 'desc': [ 'Based on ESP8266, Mercury is board developed by Ralio Technologies. Board supports on motor drivers and direct-connect feature for various endpoints.', + '', + 'Product page: https://www.raliotech.com', + ], + }), ( 'phoenix_v1', { 'name': 'Phoenix 1.0', 'opts': { @@ -531,8 +557,9 @@ '.build.variant': 'modwifi', }, 'macro': [ - 'resetmethod_ck', - 'flashmode_qio', + 'resetmethod_menu', + 'resetmethod_menu_extra', + 'flashmode_menu', 'flashfreq_40', '2M', ], @@ -620,6 +647,21 @@ 'serial': '921', 'desc': [ 'Product page: https://www.wemos.cc/' ], }), + ( 'd1_wroom_02', { + 'name': 'LOLIN(WEMOS) D1 ESP-WROOM-02', + 'opts': { + '.build.board': 'ESP8266_WEMOS_D1WROOM02', + '.build.variant': 'd1_mini', + }, + 'macro': [ + 'resetmethod_nodemcu', + 'flashmode_dio', + 'flashfreq_26', + '2M', + ], + 'serial': '921', + 'desc': [ 'No real product pages. See: https://www.instructables.com/How-to-Use-Wemos-ESP-Wroom-02-D1-Mini-WiFi-Module-/ or https://www.arduino-tech.com/wemos-esp-wroom-02-mainboard-d1-mini-wifi-module-esp826618650-battery/ ' ], + }), ( 'd1_mini_clone', { 'name': 'LOLIN(WEMOS) D1 mini (clone)', 'opts': { @@ -740,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', { @@ -791,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', @@ -948,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', @@ -1030,6 +1067,7 @@ ( '.build.core', 'esp8266' ), ( '.build.variant', 'generic' ), ( '.build.spiffs_pagesize', '256' ), + ( '.build.debug_optim', '' ), ( '.build.debug_port', '' ), ( '.build.debug_level', '' ), ]), @@ -1071,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([ @@ -1085,6 +1123,10 @@ ( '.menu.FlashFreq.26.build.flash_freq', '26' ), ]), + 'flashfreq_26': collections.OrderedDict([ + ( '.build.flash_freq', '26' ), + ]), + 'flashfreq_40': collections.OrderedDict([ ( '.build.flash_freq', '40' ), ]), @@ -1269,10 +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 External 23LC1024' ), + ( '.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.ext1024k', '1M External 64 MBit PSRAM' ), - ( '.menu.mmu.ext1024k.build.mmuflags', '-DMMU_EXTERNAL_HEAP=256 -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 @@ -1341,6 +1383,12 @@ def all_debug (): ( '.menu.dbg.Serial1.build.debug_port', '-DDEBUG_ESP_PORT=Serial1' ), ( '.menu.lvl.None____', 'None' ), ( '.menu.lvl.None____.build.debug_level', '' ), + ( '.menu.optim.Smallest', 'None' ), + ( '.menu.optim.Smallest.build.debug_optim', '-Os' ), + ( '.menu.optim.Lite', 'Lite' ), + ( '.menu.optim.Lite.build.debug_optim', '-Os -fno-optimize-sibling-calls' ), + ( '.menu.optim.Full', 'Optimum' ), + ( '.menu.optim.Full.build.debug_optim', '-Og' ), ]) for optlist in options: @@ -1371,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 @@ -1402,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' @@ -1416,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 ), @@ -1429,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: @@ -1449,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 @@ -1468,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("") @@ -1497,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([]) + + # flash(KB) spiffs(KB) confname(C) - f1m.update( flash_map( 1024, 64 )) + 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, @@ -1550,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( @@ -1573,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'), ]) } @@ -1611,6 +1742,7 @@ def all_boards (): macros.update(led('led', led_default, range(0,led_max+1))) macros.update(led('led216', 2, { 16 })) macros.update(sdk()) + macros.update(float_in_iram()) if boardfilteropt or excludeboards: print('#') @@ -1649,12 +1781,14 @@ def all_boards (): print('menu.ResetMethod=Reset Method') print('menu.dbg=Debug port') print('menu.lvl=Debug Level') + print('menu.optim=Debug Optimization') print('menu.ip=lwIP Variant') print('menu.vt=VTables') print('menu.exception=C++ Exceptions') print('menu.stacksmash=Stack Protection') print('menu.wipe=Erase Flash') - print('menu.sdk=Espressif FW') + print('menu.sdk=NONOS SDK Version') + print('menu.iramfloat=Floating Point operations') print('menu.ssl=SSL Support') print('menu.mmu=MMU') print('menu.non32xfer=Non-32-Bit Access') @@ -1665,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('##############################################################') @@ -1691,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']): 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 index 7498d5ee62..f319ef0f30 100755 --- a/tools/cert.py +++ b/tools/cert.py @@ -41,8 +41,8 @@ def printData(data, showPub = True): name = re.sub('[^a-zA-Z0-9_]', '_', cn) print('// CN: {} => name: {}'.format(cn, name)) - print('// not valid before:', xcert.not_valid_before) - print('// not valid after: ', xcert.not_valid_after) + print('// not valid before:', xcert.not_valid_before_utc) + print('// not valid after: ', xcert.not_valid_after_utc) if showPub: @@ -114,7 +114,7 @@ def main(): print() print('// this file is autogenerated - any modification will be overwritten') print('// unused symbols will not be linked in the final binary') - print('// generated on {}'.format(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))) + print('// generated on {}'.format(datetime.datetime.now(datetime.UTC).strftime("%Y-%m-%d %H:%M:%S"))) print('// by {}'.format(sys.argv)) print() print('#pragma once') diff --git a/tools/decoder.py b/tools/decoder.py new file mode 100755 index 0000000000..b1f1706767 --- /dev/null +++ b/tools/decoder.py @@ -0,0 +1,222 @@ +#!/usr/bin/env python3 + +# Baseline code from https://github.com/me-no-dev/EspExceptionDecoder by Hristo Gochkov (@me-no-dev) +# - https://github.com/me-no-dev/EspExceptionDecoder/blob/master/src/EspExceptionDecoder.java +# Stack line detection from https://github.com/platformio/platform-espressif8266/ monitor exception filter by Vojtěch Boček (@Tasssadar) +# - https://github.com/platformio/platform-espressif8266/commits?author=Tasssadar + +import os +import argparse +import sys +import re +import subprocess +import shutil + +# https://github.com/me-no-dev/EspExceptionDecoder/blob/349d17e4c9896306e2c00b4932be3ba510cad208/src/EspExceptionDecoder.java#L59-L90 +EXCEPTION_CODES = ( + "Illegal instruction", + "SYSCALL instruction", + "InstructionFetchError: Processor internal physical address or data error during " + "instruction fetch", + "LoadStoreError: Processor internal physical address or data error during load or store", + "Level1Interrupt: Level-1 interrupt as indicated by set level-1 bits in " + "the INTERRUPT register", + "Alloca: MOVSP instruction, if caller's registers are not in the register file", + "IntegerDivideByZero: QUOS, QUOU, REMS, or REMU divisor operand is zero", + "reserved", + "Privileged: Attempt to execute a privileged operation when CRING ? 0", + "LoadStoreAlignmentCause: Load or store to an unaligned address", + "reserved", + "reserved", + "InstrPIFDataError: PIF data error during instruction fetch", + "LoadStorePIFDataError: Synchronous PIF data error during LoadStore access", + "InstrPIFAddrError: PIF address error during instruction fetch", + "LoadStorePIFAddrError: Synchronous PIF address error during LoadStore access", + "InstTLBMiss: Error during Instruction TLB refill", + "InstTLBMultiHit: Multiple instruction TLB entries matched", + "InstFetchPrivilege: An instruction fetch referenced a virtual address at a ring level " + "less than CRING", + "reserved", + "InstFetchProhibited: An instruction fetch referenced a page mapped with an attribute " + "that does not permit instruction fetch", + "reserved", + "reserved", + "reserved", + "LoadStoreTLBMiss: Error during TLB refill for a load or store", + "LoadStoreTLBMultiHit: Multiple TLB entries matched for a load or store", + "LoadStorePrivilege: A load or store referenced a virtual address at a ring level " + "less than CRING", + "reserved", + "LoadProhibited: A load referenced a page mapped with an attribute that does not " + "permit loads", + "StoreProhibited: A store referenced a page mapped with an attribute that does not " + "permit stores", +) + + +# similar to java version, which used `list` and re-formatted it +# instead, simply use an already short-format `info line` +# TODO `info symbol`? revert to `list`? +def addresses_gdb(gdb, elf, addresses): + cmd = [gdb, "--batch"] + for address in addresses: + if not address.startswith("0x"): + address = f"0x{address}" + cmd.extend(["--ex", f"info line *{address}"]) + cmd.append(elf) + + with subprocess.Popen(cmd, stdout=subprocess.PIPE, universal_newlines=True) as proc: + for line in proc.stdout.readlines(): + if "No line number" in line: + continue + yield line.strip() + + +# original approach using addr2line, which is pretty enough already +def addresses_addr2line(addr2line, elf, addresses): + cmd = [ + addr2line, + "--addresses", + "--inlines", + "--functions", + "--pretty-print", + "--demangle", + "--exe", + elf, + ] + + for address in addresses: + if not address.startswith("0x"): + address = f"0x{address}" + cmd.append(address) + + with subprocess.Popen(cmd, stdout=subprocess.PIPE, universal_newlines=True) as proc: + for line in proc.stdout.readlines(): + if "??:0" in line: + continue + yield line.strip() + + +def decode_lines(format_addresses, elf, lines): + ANY_ADDR_RE = re.compile(r"0x[0-9a-fA-F]{8}|[0-9a-fA-F]{8}") + HEX_ADDR_RE = re.compile(r"0x[0-9a-f]{8}") + + MEM_ERR_LINE_RE = re.compile(r"^(Stack|last failed alloc call)") + + STACK_LINE_RE = re.compile(r"^[0-9a-f]{8}:\s\s+") + + IGNORE_FIRMWARE_RE = re.compile(r"^(epc1=0x........, |Fatal exception )") + + CUT_HERE_STRING = "CUT HERE FOR EXCEPTION DECODER" + DECODE_IT = "DECODE IT" + EXCEPTION_STRING = "Exception (" + EPC_STRING = "epc1=" + + # either print everything as-is, or cache current string and dump after stack contents end + last_stack = None + stack_addresses = {} + + in_stack = False + + def print_all_addresses(addresses): + for ctx, addrs in addresses.items(): + print() + print(ctx) + for formatted in format_addresses(elf, addrs): + print(formatted) + return dict() + + def format_address(address): + return "\n".join(format_addresses(elf, [address])) + + for line in lines: + # ctx could happen multiple times. for the 2nd one, reset list + # ctx: bearssl *or* ctx: cont *or* ctx: sys *or* ctx: whatever + if in_stack and "ctx:" in line: + stack_addresses = print_all_addresses(stack_addresses) + last_stack = line.strip() + # 3fffffb0: feefeffe feefeffe 3ffe85d8 401004ed + elif IGNORE_FIRMWARE_RE.match(line): + continue + elif in_stack and STACK_LINE_RE.match(line): + _, addrs = line.split(":") + addrs = ANY_ADDR_RE.findall(addrs) + stack_addresses.setdefault(last_stack, []) + stack_addresses[last_stack].extend(addrs) + # epc1=0xfffefefe epc2=0xfefefefe epc3=0xefefefef excvaddr=0xfefefefe depc=0xfefefefe + elif EPC_STRING in line: + pairs = line.split() + for pair in pairs: + name, addr = pair.split("=") + if name in ["epc1", "excvaddr"]: + output = format_address(addr) + if output: + print(f"{name}={output}") + # Exception (123): + # Other reasons coming before the guard shown as-is + elif EXCEPTION_STRING in line: + number = line.strip()[len(EXCEPTION_STRING) : -2] + print(f"Exception ({number}) - {EXCEPTION_CODES[int(number)]}") + # stack smashing detected at + # last failed alloc call: ()[@] + elif MEM_ERR_LINE_RE.match(line): + for addr in ANY_ADDR_RE.findall(line): + line = line.replace(addr, format_address(addr)) + print() + print(line.strip()) + # postmortem guards our actual stack dump values with these + elif ">>>stack>>>" in line: + in_stack = True + # ignore + elif "<<> 24) & 255 return raw + def add_crc(out): with open(out, "rb") as binfile: raw = bytearray(binfile.read()) @@ -141,15 +185,16 @@ def add_crc(out): with open(out, "wb") as binfile: binfile.write(raw) + def gzip_bin(mode, out): import gzip firmware_path = out - gzip_path = firmware_path + '.gz' - orig_path = firmware_path + '.orig' + gzip_path = f"{firmware_path}.gz" + orig_path = f"{firmware_path}.orig" if os.path.exists(gzip_path): os.remove(gzip_path) - print('GZipping firmware ' + firmware_path) + print(f'GZipping firmware {firmware_path}') with open(firmware_path, 'rb') as firmware_file, \ gzip.open(gzip_path, 'wb') as dest: data = firmware_file.read() @@ -162,10 +207,11 @@ def gzip_bin(mode, out): if mode == "PIO": if os.path.exists(orig_path): os.remove(orig_path) - print('Moving original firmware to ' + orig_path) + print(f'Moving original firmware to {orig_path}') os.rename(firmware_path, orig_path) os.rename(gzip_path, firmware_path) + def main(): parser = argparse.ArgumentParser(description='Create a BIN file from eboot.elf and Arduino sketch.elf for upload by esptool.py') parser.add_argument('-e', '--eboot', action='store', required=True, help='Path to the Arduino eboot.elf bootloader') @@ -179,8 +225,7 @@ def main(): args = parser.parse_args() - print('Creating BIN file "{out}" using "{eboot}" and "{app}"'.format( - out=args.out, eboot=args.eboot, app=args.app)) + print(f'Creating BIN file "{args.out}" using "{args.eboot}" and "{args.app}"') with open(args.out, "wb") as out: def wrapper(**kwargs): diff --git a/tools/format_tzdata.py b/tools/format_tzdata.py new file mode 100755 index 0000000000..609a9218bf --- /dev/null +++ b/tools/format_tzdata.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python3 + +# this script refreshes world timezone definitions in +# cores/esp8266/TZ.h +# +# use the file output argument or stdout redirect to overwrite the target file + +import argparse +import contextlib +import datetime +import mmap +import os +import pathlib +import re +import sys +import pathlib + +from importlib import resources + +import tzdata # https://tzdata.readthedocs.io/en/latest/ + + +def known_alias(entry): + swaps = { + "Europe/Zaporozhye": "Europe/Zaporizhzhia", + "Europe/Uzhgorod": "Europe/Uzhhorod", + } + + return swaps.get(entry) + + +def fix_name(name): + swaps = [["-", "m"], ["+", "p"], ["/", "_"]] + + for lhs, rhs in swaps: + name = name.replace(lhs, rhs) + + return name + + +def utc_alias(zone): + return zone in ( + "Universal", + "UTC", + "UCT", + "Zulu", + "GMT", + "GMT+0", + "GMT-0", + "GMT0", + "Greenwich", + ) + + +def tzdata_resource_from_name(name): + pair = name.rsplit("/", 1) + if len(pair) == 1: + return resources.files("tzdata.zoneinfo") / pair[0] + + return resources.files(f'tzdata.zoneinfo.{pair[0].replace("/", ".")}') / pair[1] + + +def make_zones_list(f): + return [zone.strip() for zone in f.readlines()] + + +def make_zones(args): + out = [] + + for zone in make_zones_list(args.zones): + if args.root: + target = args.root / zone + else: + target = tzdata_resource_from_name(zone) + + with target.open("rb") as f: + magic = f.read(4) + if magic != b"TZif": + continue + + m = mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ) + newline = m.rfind(b"\n", 0, len(m) - 1) + if newline < 0: + continue + + m.seek(newline + 1) + tz = m.readline().strip() + tz = tz.decode("ascii") + + if alias := known_alias(zone): + out.append([alias, tz]) + + out.append([zone, tz]) + + out.sort(key=lambda x: x[0]) + return out + + +def markdown(zones): + utcs = [] + rows = [] + + for name, value in zones: + if utc_alias(name): + utcs.append(name) + continue + + rows.append(f"|{name}|`{value}`|") + + print("|Name|Value|") + print("|---|---|") + for name in utcs: + print(f"|{name}|UTC0|") + + last = "" + for row in rows: + prefix, _, _ = row.partition("/") + if last != prefix: + last = prefix + print("|||") + print(row) + print() + print("---") + print() + print(f"*Generated with *{tzdata.IANA_VERSION=} {tzdata.__version__=}*") + + +def header(zones): + print("// ! ! ! DO NOT EDIT, AUTOMATICALLY GENERATED ! ! !") + print(f"// File created {datetime.datetime.now(tz=datetime.timezone.utc)}") + print(f"// Based on IANA database {tzdata.IANA_VERSION}") + print(f"// Re-run /tools/{sys.argv[0]} to update") + print() + print("#pragma once") + print() + for name, value in zones: + print(f'#define TZ_{fix_name(name)}\tPSTR("{value}")') + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + parser.add_argument( + "--output", + type=argparse.FileType("w", encoding="utf-8"), + default=sys.stdout, + ) + parser.add_argument( + "--format", + default="header", + choices=["header", "markdown"], + ) + parser.add_argument( + "--zones", + type=argparse.FileType("r", encoding="utf-8"), + help="Zone names file, one per line", + default=os.path.join(os.path.dirname(tzdata.__file__), "zones"), + ) + parser.add_argument( + "--root", + help="Where do we get raw zoneinfo files from", + type=pathlib.Path, + ) + + args = parser.parse_args() + zones = make_zones(args) + + with contextlib.redirect_stdout(args.output): + if args.format == "markdown": + markdown(zones) + elif args.format == "header": + header(zones) diff --git a/tools/makecorever.py b/tools/makecorever.py index 5d44d7cfa0..d3f44f1742 100755 --- a/tools/makecorever.py +++ b/tools/makecorever.py @@ -70,7 +70,7 @@ def git(*args): 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 // developpment version\n" + text += "#define ARDUINO_ESP8266_DEV 1 // development version\n" try: with open(path, "r") as inp: 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/platformio-build.py b/tools/platformio-build.py index 8998fcde78..20fe8c77fb 100644 --- a/tools/platformio-build.py +++ b/tools/platformio-build.py @@ -59,7 +59,13 @@ def scons_patched_match_splitext(path, suffixes=None): gzip_switch = ["--gzip", "PIO"] env.Append( - ASFLAGS=["-x", "assembler-with-cpp"], + ASFLAGS=[ + "-mlongcalls", + "-mtext-section-literals", + ], + ASPPFLAGS=[ + "-x", "assembler-with-cpp", + ], # General options that are passed to the C compiler (C only; not C++) CFLAGS=[ @@ -78,7 +84,6 @@ def scons_patched_match_splitext(path, suffixes=None): "-mtext-section-literals", "-falign-functions=4", "-U__STRICT_ANSI__", - "-D_GNU_SOURCE", "-ffunction-sections", "-fdata-sections", "-Wall", @@ -119,8 +124,10 @@ def scons_patched_match_splitext(path, suffixes=None): ("F_CPU", "$BOARD_F_CPU"), "__ets__", "ICACHE_FLASH", + "_GNU_SOURCE", ("ARDUINO", 10805), ("ARDUINO_BOARD", '\\"PLATFORMIO_%s\\"' % env.BoardConfig().id.upper()), + ("ARDUINO_BOARD_ID", '\\"%s\\"' % env.BoardConfig().id), "FLASHMODE_${BOARD_FLASH_MODE.upper()}", "LWIP_OPEN_SRC" ], @@ -139,12 +146,8 @@ def scons_patched_match_splitext(path, suffixes=None): join(FRAMEWORK_DIR, "tools", "sdk", "ld") ], - # A list of one or more libraries that will be linked with any executable programs created by this environment - LIBS=[ - "hal", "phy", "pp", "net80211", "wpa", "crypto", "main", - "wps", "bearssl", "espnow", "smartconfig", "airkiss", "wpa2", - "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") @@ -170,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 @@ -266,33 +259,39 @@ def scons_patched_match_splitext(path, suffixes=None): # # Exceptions # +stdcpp_lib = None if "PIO_FRAMEWORK_ARDUINO_ENABLE_EXCEPTIONS" in flatten_cppdefines: env.Append( CXXFLAGS=["-fexceptions"], - LIBS=["stdc++-exc"] ) + stdcpp_lib = "stdc++-exc" else: env.Append( CXXFLAGS=["-fno-exceptions"], - LIBS=["stdc++"] ) - + 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 +if not current_fp: + current_fp = "FP_IN_IROM" + env.Append(CPPDEFINES=[current_fp]) +assert current_fp + # # MMU # @@ -337,7 +336,7 @@ def scons_patched_match_splitext(path, suffixes=None): for flag in env["CPPDEFINES"]: define = flag if isinstance(flag, (tuple, list)): - define, _ = flag + define, *_ = flag if define.startswith("MMU_"): mmu_flags.append(flag) # PIO_FRAMEWORK_ARDUINO_MMU_CACHE32_IRAM32 (default) @@ -355,15 +354,25 @@ def scons_patched_match_splitext(path, suffixes=None): 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 %s %s $SOURCE -o $TARGET" + "$CC -CC -E -P -D%s -D%s %s $SOURCE -o $TARGET" % ( current_vtables, + current_fp, # String representation of MMU flags " ".join( [ @@ -371,7 +380,6 @@ def scons_patched_match_splitext(path, suffixes=None): for f in mmu_flags ] ), - fp_in_irom, ), "Generating LD script $TARGET", ), diff --git a/tools/sdk/include/bearssl/bearssl.h b/tools/sdk/include/bearssl/bearssl.h index 4f4797cf79..310edb258d 100644 --- a/tools/sdk/include/bearssl/bearssl.h +++ b/tools/sdk/include/bearssl/bearssl.h @@ -137,6 +137,10 @@ #include "bearssl_x509.h" #include "bearssl_pem.h" +#ifdef __cplusplus +extern "C" { +#endif + /** \brief Type for a configuration option. * * A "configuration option" is a value that is selected when the BearSSL @@ -167,4 +171,13 @@ typedef struct { */ const br_config_option *br_get_config(void); +/* ======================================================================= */ + +/** \brief Version feature: support for time callback. */ +#define BR_FEATURE_X509_TIME_CALLBACK 1 + +#ifdef __cplusplus +} +#endif + #endif diff --git a/tools/sdk/include/bearssl/bearssl_git.h b/tools/sdk/include/bearssl/bearssl_git.h index 9ee1904139..37fe2a0d36 100644 --- a/tools/sdk/include/bearssl/bearssl_git.h +++ b/tools/sdk/include/bearssl/bearssl_git.h @@ -1,2 +1,2 @@ // Do not edit -- Automatically generated by tools/sdk/ssl/bearssl/Makefile -#define BEARSSL_GIT 6105635 +#define BEARSSL_GIT 5166f2b diff --git a/tools/sdk/include/bearssl/bearssl_x509.h b/tools/sdk/include/bearssl/bearssl_x509.h index f2f6e6f877..9a1e6593e0 100644 --- a/tools/sdk/include/bearssl/bearssl_x509.h +++ b/tools/sdk/include/bearssl/bearssl_x509.h @@ -625,6 +625,52 @@ typedef struct { } br_name_element; +/** + * \brief Callback for validity date checks. + * + * The function receives as parameter an arbitrary user-provided context, + * and the notBefore and notAfter dates specified in an X.509 certificate, + * both expressed as a number of days and a number of seconds: + * + * - Days are counted in a proleptic Gregorian calendar since + * January 1st, 0 AD. Year "0 AD" is the one that preceded "1 AD"; + * it is also traditionally known as "1 BC". + * + * - Seconds are counted since midnight, from 0 to 86400 (a count of + * 86400 is possible only if a leap second happened). + * + * Each date and time is understood in the UTC time zone. The "Unix + * Epoch" (January 1st, 1970, 00:00 UTC) corresponds to days=719528 and + * seconds=0; the "Windows Epoch" (January 1st, 1601, 00:00 UTC) is + * days=584754, seconds=0. + * + * This function must return -1 if the current date is strictly before + * the "notBefore" time, or +1 if the current date is strictly after the + * "notAfter" time. If neither condition holds, then the function returns + * 0, which means that the current date falls within the validity range of + * the certificate. If the function returns a value distinct from -1, 0 + * and +1, then this is interpreted as an unavailability of the current + * time, which normally ends the validation process with a + * `BR_ERR_X509_TIME_UNKNOWN` error. + * + * During path validation, this callback will be invoked for each + * considered X.509 certificate. Validation fails if any of the calls + * returns a non-zero value. + * + * The context value is an abritrary pointer set by the caller when + * configuring this callback. + * + * \param tctx context pointer. + * \param not_before_days notBefore date (days since Jan 1st, 0 AD). + * \param not_before_seconds notBefore time (seconds, at most 86400). + * \param not_after_days notAfter date (days since Jan 1st, 0 AD). + * \param not_after_seconds notAfter time (seconds, at most 86400). + * \return -1, 0 or +1. + */ +typedef int (*br_x509_time_check)(void *tctx, + uint32_t not_before_days, uint32_t not_before_seconds, + uint32_t not_after_days, uint32_t not_after_seconds); + /** * \brief The "minimal" X.509 engine structure. * @@ -647,8 +693,8 @@ typedef struct { uint32_t *rp; const unsigned char *ip; } cpu; - uint32_t dp_stack[32]; - uint32_t rp_stack[32]; + uint32_t dp_stack[31]; + uint32_t rp_stack[31]; int err; /* Server name to match with the SAN / CN of the EE certificate. */ @@ -730,6 +776,12 @@ typedef struct { br_name_element *name_elts; size_t num_name_elts; + /* + * Callback function (and context) to get the current date. + */ + void *itime_ctx; + br_x509_time_check itime; + /* * Public key cryptography implementations (signature verification). */ @@ -890,7 +942,10 @@ void br_x509_minimal_init_full(br_x509_minimal_context *ctx, * - Seconds are counted since midnight, from 0 to 86400 (a count of * 86400 is possible only if a leap second happened). * - * The validation date and time is understood in the UTC time zone. + * The validation date and time is understood in the UTC time zone. The + * "Unix Epoch" (January 1st, 1970, 00:00 UTC) corresponds to days=719528 + * and seconds=0; the "Windows Epoch" (January 1st, 1601, 00:00 UTC) is + * days=584754, seconds=0. * * If the validation date and time are not explicitly set, but BearSSL * was compiled with support for the system clock on the underlying @@ -908,6 +963,28 @@ br_x509_minimal_set_time(br_x509_minimal_context *ctx, { ctx->days = days; ctx->seconds = seconds; + ctx->itime = 0; +} + +/** + * \brief Set the validity range callback function for the X.509 + * "minimal" engine. + * + * The provided function will be invoked to check whether the validation + * date is within the validity range for a given X.509 certificate; a + * call will be issued for each considered certificate. The provided + * context pointer (itime_ctx) will be passed as first parameter to the + * callback. + * + * \param tctx context for callback invocation. + * \param cb callback function. + */ +static inline void +br_x509_minimal_set_time_callback(br_x509_minimal_context *ctx, + void *itime_ctx, br_x509_time_check itime) +{ + ctx->itime_ctx = itime_ctx; + ctx->itime = itime; } /** diff --git a/tools/sdk/include/ets_sys.h b/tools/sdk/include/ets_sys.h index 7908f127c3..a199469e0d 100644 --- a/tools/sdk/include/ets_sys.h +++ b/tools/sdk/include/ets_sys.h @@ -208,7 +208,6 @@ void *ets_memset(void *s, int c, size_t n); void ets_timer_arm_new(ETSTimer *a, int b, int c, int isMstimer); void ets_timer_setfn(ETSTimer *t, ETSTimerFunc *fn, void *parg); void ets_timer_disarm(ETSTimer *a); -int atoi(const char *nptr); int ets_strncmp(const char *s1, const char *s2, int len); int ets_strcmp(const char *s1, const char *s2); int ets_strlen(const char *s); diff --git a/tools/sdk/include/user_interface.h b/tools/sdk/include/user_interface.h index e69821b372..1d057417b8 100644 --- a/tools/sdk/include/user_interface.h +++ b/tools/sdk/include/user_interface.h @@ -25,6 +25,12 @@ #ifndef __USER_INTERFACE_H__ #define __USER_INTERFACE_H__ +#if defined(NONOSDK305) +#define NONOSDK (0x30500) +#else +#define NONOSDK (0x22100) +#endif + #include "os_type.h" #ifdef LWIP_OPEN_SRC @@ -249,13 +255,19 @@ typedef struct { struct station_config { uint8 ssid[32]; uint8 password[64]; +#if (NONOSDK >= (0x30200)) + uint8 channel; +#endif uint8 bssid_set; // Note: If bssid_set is 1, station will just connect to the router // with both ssid[] and bssid[] matched. Please check about this. uint8 bssid[6]; wifi_fast_scan_threshold_t threshold; -#ifdef NONOSDK3V0 +#if (NONOSDK >= (0x30000)) bool open_and_wep_mode_disable; // Can connect to open/wep router by default. #endif +#if (NONOSDK >= (0x30200)) + bool all_channel_scan; +#endif }; bool wifi_station_get_config(struct station_config *config); @@ -382,17 +394,17 @@ void wifi_softap_free_station_info(void); bool wifi_softap_dhcps_start(void); bool wifi_softap_dhcps_stop(void); -#if 1 // dhcp server -// these functions are open-source, in dhcp server, -// which is now moved to lwIPDhcpServer.cpp (lwip2) -// (but still there with lwip1) +// esp8266/Arduino notice: +// these dhcp functions are no longer provided by the lwip lib +// only way to include them is to build our NonOS LwipDhcpServer helpers +// (ref. libraries/ESP8266WiFi/src/ESP8266WiFiAP-DhcpServer.cpp) + bool wifi_softap_set_dhcps_lease(struct dhcps_lease *please); bool wifi_softap_get_dhcps_lease(struct dhcps_lease *please); uint32 wifi_softap_get_dhcps_lease_time(void); bool wifi_softap_set_dhcps_lease_time(uint32 minute); bool wifi_softap_reset_dhcps_lease_time(void); bool wifi_softap_add_dhcps_lease(uint8 *macaddr); // add static lease on the list, this will be the next available @ -#endif // dhcp server enum dhcp_status wifi_softap_dhcps_status(void); bool wifi_softap_set_dhcps_offer_option(uint8 level, void* optarg); @@ -438,7 +450,7 @@ typedef enum { MODEM_SLEEP_T } sleep_type_t; -#ifdef NONOSDK3V0 +#if (NONOSDK >= (0x30000)) typedef enum { MIN_SLEEP_T, @@ -771,6 +783,70 @@ bool wifi_set_country(wifi_country_t *country); */ bool wifi_get_country(wifi_country_t *country); +#if (NONOSDK >= (0x30000)) + +typedef enum { + SYSTEM_PARTITION_INVALID = 0, + SYSTEM_PARTITION_BOOTLOADER, /* user can't modify this partition address, but can modify size */ + SYSTEM_PARTITION_OTA_1, /* user can't modify this partition address, but can modify size */ + SYSTEM_PARTITION_OTA_2, /* user can't modify this partition address, but can modify size */ + SYSTEM_PARTITION_RF_CAL, /* user must define this partition */ + SYSTEM_PARTITION_PHY_DATA, /* user must define this partition */ + SYSTEM_PARTITION_SYSTEM_PARAMETER, /* user must define this partition */ + SYSTEM_PARTITION_AT_PARAMETER, + SYSTEM_PARTITION_SSL_CLIENT_CERT_PRIVKEY, + SYSTEM_PARTITION_SSL_CLIENT_CA, + SYSTEM_PARTITION_SSL_SERVER_CERT_PRIVKEY, + SYSTEM_PARTITION_SSL_SERVER_CA, + SYSTEM_PARTITION_WPA2_ENTERPRISE_CERT_PRIVKEY, + SYSTEM_PARTITION_WPA2_ENTERPRISE_CA, + + SYSTEM_PARTITION_CUSTOMER_BEGIN = 100, /* user can define partition after here */ + SYSTEM_PARTITION_MAX +} partition_type_t; + +typedef struct { + partition_type_t type; /* the partition type */ + uint32_t addr; /* the partition address */ + uint32_t size; /* the partition size */ +} partition_item_t; + +/** + * @brief regist partition table information, user MUST call it in user_pre_init() + * + * @param partition_table: the partition table + * @param partition_num: the partition number in partition table + * @param map: the flash map + * + * @return true : succeed + * @return false : fail + */ +bool system_partition_table_regist( + const partition_item_t* partition_table, + uint32_t partition_num, + uint32_t map + ); + +/** + * @brief get ota partition size + * + * @return the size of ota partition + */ +uint32_t system_partition_get_ota_partition_size(void); + +/** + * @brief get partition information + * + * @param type: the partition type + * @param partition_item: the point to store partition information + * + * @return true : succeed + * @return false : fail + */ +bool system_partition_get_item(partition_type_t type, partition_item_t* partition_item); + +#endif + #ifdef __cplusplus } #endif diff --git a/tools/sdk/ld/eagle.app.v6.common.ld.h b/tools/sdk/ld/eagle.app.v6.common.ld.h index 77c834ae19..e6b415c50b 100644 --- a/tools/sdk/ld/eagle.app.v6.common.ld.h +++ b/tools/sdk/ld/eagle.app.v6.common.ld.h @@ -138,10 +138,20 @@ SECTIONS *(.text.app_entry*) /* The main startup code */ - *(.text.gdbstub*, .text.gdb_init) /* Any GDB hooks */ + *(.text.gdbstub* .text.gdb_init) /* Any GDB hooks */ /* all functional callers are placed in IRAM (including SPI/IRQ callbacks/etc) here */ *(.text._ZNKSt8functionIF*EE*) /* std::function::operator()() const */ + +#ifdef FP_IN_IRAM + *libgcc.a:*f2.o(.literal .text) + *libgcc.a:*f3.o(.literal .text) + *libgcc.a:*fsi.o(.literal .text) + *libgcc.a:*fdi.o(.literal .text) + *libgcc.a:*ifs.o(.literal .text) + *libgcc.a:*idf.o(.literal .text) +#endif + } >iram1_0_seg :iram1_0_phdr .irom0.text : ALIGN(4) @@ -155,13 +165,31 @@ SECTIONS LONG(0x00000000); *(.ver_number) - *.c.o(.literal*, .text*) - *.cpp.o(EXCLUDE_FILE (umm_malloc.cpp.o) .literal*, EXCLUDE_FILE (umm_malloc.cpp.o) .text*) - *.cc.o(.literal*, .text*) + *.c.o(.literal* .text*) + *.cpp.o(EXCLUDE_FILE (umm_malloc.cpp.o) .literal* EXCLUDE_FILE (umm_malloc.cpp.o) .text*) + *.cc.o(.literal* .text*) #ifdef VTABLES_IN_FLASH *(.rodata._ZTV*) /* C++ vtables */ #endif + . = ALIGN(4); /* this table MUST be 4-byte aligned */ + /* C++ constructor and destructor tables, properly ordered: */ + __init_array_start = ABSOLUTE(.); + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + __init_array_end = ABSOLUTE(.); + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + . = ALIGN(4); /* this table MUST be 4-byte aligned */ + _bss_table_start = ABSOLUTE(.); + LONG(_bss_start) + LONG(_bss_end) + _bss_table_end = ABSOLUTE(.); + *libgcc.a:unwind-dw2.o(.literal .text .rodata .literal.* .text.* .rodata.*) *libgcc.a:unwind-dw2-fde.o(.literal .text .rodata .literal.* .text.* .rodata.*) @@ -220,7 +248,7 @@ SECTIONS *(.rodata._ZZ*__func__) /* std::* exception strings, in their own section to allow string coalescing */ - *(.irom.exceptiontext, .rodata.exceptiontext) + *(.irom.exceptiontext .rodata.exceptiontext) *(.rodata.*__exception_what__*) /* G++ seems to throw out templatized section attributes */ /* c++ typeof IDs, etc. */ diff --git a/tools/sdk/ld/eagle.app.v6.common.ld.vtables.h b/tools/sdk/ld/eagle.app.v6.common.ld.vtables.h index 6649e08a25..f837e12d41 100644 --- a/tools/sdk/ld/eagle.app.v6.common.ld.vtables.h +++ b/tools/sdk/ld/eagle.app.v6.common.ld.vtables.h @@ -12,18 +12,7 @@ *(.gnu.linkonce.e.*) *(.gnu.version_r) *(.eh_frame) - . = (. + 3) & ~ 3; - /* C++ constructor and destructor tables, properly ordered: */ - __init_array_start = ABSOLUTE(.); - KEEP (*crtbegin.o(.ctors)) - KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) - KEEP (*(SORT(.ctors.*))) - KEEP (*(.ctors)) - __init_array_end = ABSOLUTE(.); - KEEP (*crtbegin.o(.dtors)) - KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) - KEEP (*(SORT(.dtors.*))) - KEEP (*(.dtors)) + . = ALIGN(4); /* C++ exception handlers table: */ __XT_EXCEPTION_DESCS__ = ABSOLUTE(.); *(.xt_except_desc) @@ -32,11 +21,6 @@ *(.xt_except_desc_end) *(.dynamic) *(.gnu.version_d) - . = ALIGN(4); /* this table MUST be 4-byte aligned */ - _bss_table_start = ABSOLUTE(.); - LONG(_bss_start) - LONG(_bss_end) - _bss_table_end = ABSOLUTE(.); _rodata_end = ABSOLUTE(.); } >dram0_0_seg :dram0_0_phdr diff --git a/tools/sdk/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/lib/NONOSDK221/libwpa2.a b/tools/sdk/lib/NONOSDK221/libwpa2.a index 1f7aabb688..f2b2178fc0 100644 Binary files a/tools/sdk/lib/NONOSDK221/libwpa2.a and b/tools/sdk/lib/NONOSDK221/libwpa2.a differ diff --git a/tools/sdk/lib/NONOSDK22x_190313/libwpa2.a b/tools/sdk/lib/NONOSDK22x_190313/libwpa2.a index 07420c5bf0..fb08a7ee3e 100644 Binary files a/tools/sdk/lib/NONOSDK22x_190313/libwpa2.a and b/tools/sdk/lib/NONOSDK22x_190313/libwpa2.a differ diff --git a/tools/sdk/lib/NONOSDK22x_190703/libwpa2.a b/tools/sdk/lib/NONOSDK22x_190703/libwpa2.a index 07420c5bf0..fb08a7ee3e 100644 Binary files a/tools/sdk/lib/NONOSDK22x_190703/libwpa2.a and b/tools/sdk/lib/NONOSDK22x_190703/libwpa2.a differ diff --git a/tools/sdk/lib/NONOSDK22x_191024/libwpa2.a b/tools/sdk/lib/NONOSDK22x_191024/libwpa2.a index 07420c5bf0..fb08a7ee3e 100644 Binary files a/tools/sdk/lib/NONOSDK22x_191024/libwpa2.a and b/tools/sdk/lib/NONOSDK22x_191024/libwpa2.a differ diff --git a/tools/sdk/lib/NONOSDK22x_191105/libwpa2.a b/tools/sdk/lib/NONOSDK22x_191105/libwpa2.a index 07420c5bf0..fb08a7ee3e 100644 Binary files a/tools/sdk/lib/NONOSDK22x_191105/libwpa2.a and b/tools/sdk/lib/NONOSDK22x_191105/libwpa2.a differ diff --git a/tools/sdk/lib/NONOSDK22x_191122/libwpa2.a b/tools/sdk/lib/NONOSDK22x_191122/libwpa2.a index 07420c5bf0..fb08a7ee3e 100644 Binary files a/tools/sdk/lib/NONOSDK22x_191122/libwpa2.a and b/tools/sdk/lib/NONOSDK22x_191122/libwpa2.a differ diff --git a/tools/sdk/lib/NONOSDK305/commitlog.txt b/tools/sdk/lib/NONOSDK305/commitlog.txt new file mode 100644 index 0000000000..ebad3fedf1 --- /dev/null +++ b/tools/sdk/lib/NONOSDK305/commitlog.txt @@ -0,0 +1,147 @@ + feat: Update sdk version header file 3.0.5 + feat(core): update core version to b29dcd3 + feat: Update sdk version header file 3.0.4 + fix: It sometimes crashes after disable sntp + fix: Sometimes the parameter is missing because of power down when erasing or writing the flash + +commit 7b5b35da98ad9ee2de7afc63277d4933027ae91c +Merge: bd63c1f 26bedd0 +Author: Xu Chun Guang +Date: Fri Oct 15 11:40:39 2021 +0000 + + Merge branch 'feature/update_at_bin' into 'release/v3.0.5' + + feat: Update at bin to 1.7.5 + + See merge request sdk/ESP8266_NONOS_SDK!296 + +commit 26bedd0c7afd8a090670fb352f1f8a811b4f1839 +Author: Xu Chun Guang +Date: Fri Oct 15 18:06:49 2021 +0800 + + feat: Update at bin to 1.7.5 + +commit 38d251e1decafb56bae94e67f62ec425c6e5fe64 +Author: Xu Chun Guang +Date: Fri Oct 15 18:04:52 2021 +0800 + + feat: Update sdk version header file 3.0.5 + +commit bd63c1f81aae4bfb1b569e880a24fa73be81c713 +Merge: b1c14cd dae389b +Author: Xu Chun Guang +Date: Sat Oct 9 03:57:00 2021 +0000 + + Merge branch 'feature/update_core_v3.0.5' into 'release/v3.0.5' + + feat(core): update core version to b29dcd3 + + See merge request sdk/ESP8266_NONOS_SDK!293 + +commit dae389b804ad7c796d4dd62fab9ec346208507ac +Author: Chen Wu +Date: Sat Oct 9 11:37:28 2021 +0800 + + feat(core): update core version to b29dcd3 + +commit b1c14cdb08e2f39b654933f887b4f997b291982f +Merge: 5677e37 43dfa5a +Author: Xu Chun Guang +Date: Wed May 27 16:56:15 2020 +0800 + + Merge branch 'feature/update_at_bin' into 'master' + + feat: Update at bin to 1.7.4.0 + + See merge request sdk/ESP8266_NONOS_SDK!283 + +commit 43dfa5a7f919c3537929c1e36822118414da7d7f +Author: Xu Chun Guang +Date: Wed May 27 10:22:50 2020 +0800 + + feat: Update at bin to 1.7.4.0 + +commit 5677e379adf43032c8c05dde7816854409f9a8e8 +Merge: 8c0e419 9fbceb4 +Author: Xu Chun Guang +Date: Wed May 27 09:52:55 2020 +0800 + + Merge branch 'feature/update_sdk_version_h' into 'master' + + feat: Update sdk version header file 3.0.4 + + See merge request sdk/ESP8266_NONOS_SDK!282 + +commit 9fbceb4bc837521a09a790ee8db6dd1166e55498 +Author: Xu Chun Guang +Date: Wed May 27 09:50:30 2020 +0800 + + feat: Update sdk version header file 3.0.4 + +commit 8c0e419de1173347c4b9d073a2d891d3575c018a +Merge: f537843 13e436f +Author: Xu Chun Guang +Date: Tue May 26 15:35:24 2020 +0800 + + Merge branch 'bugfix/N8266-67' into 'master' + + fix: It sometimes crashes after disable sntp + + See merge request sdk/ESP8266_NONOS_SDK!281 + +commit 13e436f6214993e1f322c24f20a431fef8a123ca +Author: Xu Chun Guang +Date: Tue May 26 10:55:57 2020 +0800 + + feat: Update lwip lib to 5623f48f + +commit 5623f48f5e2dd1f45c25b9f2cad2314924a3cb00 +Author: Xu Chun Guang +Date: Tue May 26 10:52:30 2020 +0800 + + fix: It sometimes crashes after disable sntp + +commit f5378436fde39c0693df25efb51174b56f79dcc2 +Merge: c2ddeca da9767c +Author: Xu Chun Guang +Date: Fri May 22 17:45:10 2020 +0800 + + Merge branch 'bugfix/parameter_missing' into 'master' + + fix: Sometimes the parameter is missing because of power down when erasing or writing the flash + + See merge request sdk/ESP8266_NONOS_SDK!280 + +commit da9767c316e0e0dca9c00d91fbdac44ada62b29c +Author: Xu Chun Guang +Date: Fri May 22 17:26:39 2020 +0800 + + fix: Sometimes the parameter is missing because of power down when erasing or writing the flash + +commit c2ddeca67fe849a0a243ee50a28f8c81b8bba59a +Merge: b2831d5 613a042 +Author: Xu Chun Guang +Date: Tue May 12 11:17:23 2020 +0800 + + Merge branch 'feature/compatible_with_16M_512_512_in_at_nano' into 'master' + + feat: Compatible with 16M 512 512 in at nano + + See merge request sdk/ESP8266_NONOS_SDK!279 + +commit 613a042bc35823e7a48b0870a6562008f34009a9 +Author: Xu Chun Guang +Date: Tue May 12 10:06:16 2020 +0800 + + feat: Compatible with 16M 512 512 in at nano + +commit b2831d57b3c51e421bf35fc902d177ea57ba6e13 +Merge: b77cb8c 369b9bc +Author: Xu Chun Guang +Date: Mon May 11 20:04:36 2020 +0800 + + Merge branch 'bugfix/workaround_ota' into 'master' + + Bugfix/workaround ota + + See merge request sdk/ESP8266_NONOS_SDK!277 diff --git a/tools/sdk/lib/NONOSDK3V0/libairkiss.a b/tools/sdk/lib/NONOSDK305/libairkiss.a similarity index 72% rename from tools/sdk/lib/NONOSDK3V0/libairkiss.a rename to tools/sdk/lib/NONOSDK305/libairkiss.a index cfdcc84234..60415e5242 100644 Binary files a/tools/sdk/lib/NONOSDK3V0/libairkiss.a and b/tools/sdk/lib/NONOSDK305/libairkiss.a differ diff --git a/tools/sdk/lib/NONOSDK305/libcrypto.a b/tools/sdk/lib/NONOSDK305/libcrypto.a new file mode 100644 index 0000000000..cbb94e7552 Binary files /dev/null and b/tools/sdk/lib/NONOSDK305/libcrypto.a differ diff --git a/tools/sdk/lib/NONOSDK305/libespnow.a b/tools/sdk/lib/NONOSDK305/libespnow.a new file mode 100644 index 0000000000..2d3f7a8d2b Binary files /dev/null and b/tools/sdk/lib/NONOSDK305/libespnow.a differ diff --git a/tools/sdk/lib/NONOSDK305/libmain.a b/tools/sdk/lib/NONOSDK305/libmain.a new file mode 100644 index 0000000000..699b5b4b14 Binary files /dev/null and b/tools/sdk/lib/NONOSDK305/libmain.a differ diff --git a/tools/sdk/lib/NONOSDK305/libnet80211.a b/tools/sdk/lib/NONOSDK305/libnet80211.a new file mode 100644 index 0000000000..1edb9e5dda Binary files /dev/null and b/tools/sdk/lib/NONOSDK305/libnet80211.a differ diff --git a/tools/sdk/lib/NONOSDK305/libphy.a b/tools/sdk/lib/NONOSDK305/libphy.a new file mode 100644 index 0000000000..9bdb6d6448 Binary files /dev/null and b/tools/sdk/lib/NONOSDK305/libphy.a differ diff --git a/tools/sdk/lib/NONOSDK305/libpp.a b/tools/sdk/lib/NONOSDK305/libpp.a new file mode 100644 index 0000000000..e5c86d681c Binary files /dev/null and b/tools/sdk/lib/NONOSDK305/libpp.a differ diff --git a/tools/sdk/lib/NONOSDK305/libsmartconfig.a b/tools/sdk/lib/NONOSDK305/libsmartconfig.a new file mode 100644 index 0000000000..d6d593e87f Binary files /dev/null and b/tools/sdk/lib/NONOSDK305/libsmartconfig.a differ diff --git a/tools/sdk/lib/NONOSDK305/libwpa.a b/tools/sdk/lib/NONOSDK305/libwpa.a new file mode 100644 index 0000000000..18b5efc3f5 Binary files /dev/null and b/tools/sdk/lib/NONOSDK305/libwpa.a differ diff --git a/tools/sdk/lib/NONOSDK305/libwpa2.a b/tools/sdk/lib/NONOSDK305/libwpa2.a new file mode 100644 index 0000000000..a3a7a4210c Binary files /dev/null and b/tools/sdk/lib/NONOSDK305/libwpa2.a differ diff --git a/tools/sdk/lib/NONOSDK305/libwps.a b/tools/sdk/lib/NONOSDK305/libwps.a new file mode 100644 index 0000000000..fe821a418d Binary files /dev/null and b/tools/sdk/lib/NONOSDK305/libwps.a differ diff --git a/tools/sdk/lib/NONOSDK305/version b/tools/sdk/lib/NONOSDK305/version new file mode 100644 index 0000000000..7a5f16731d --- /dev/null +++ b/tools/sdk/lib/NONOSDK305/version @@ -0,0 +1 @@ +v3.0.5-g7b5b35d (shows as SDK:3.0.5(b29dcd3) in debug mode) \ No newline at end of file diff --git a/tools/sdk/lib/NONOSDK3V0/libcrypto.a b/tools/sdk/lib/NONOSDK3V0/libcrypto.a deleted file mode 100644 index d0aea338e8..0000000000 Binary files a/tools/sdk/lib/NONOSDK3V0/libcrypto.a and /dev/null differ diff --git a/tools/sdk/lib/NONOSDK3V0/libespnow.a b/tools/sdk/lib/NONOSDK3V0/libespnow.a deleted file mode 100644 index 2ed7994e1d..0000000000 Binary files a/tools/sdk/lib/NONOSDK3V0/libespnow.a and /dev/null differ diff --git a/tools/sdk/lib/NONOSDK3V0/libmain.a b/tools/sdk/lib/NONOSDK3V0/libmain.a deleted file mode 100644 index fb5242f1be..0000000000 Binary files a/tools/sdk/lib/NONOSDK3V0/libmain.a and /dev/null differ diff --git a/tools/sdk/lib/NONOSDK3V0/libnet80211.a b/tools/sdk/lib/NONOSDK3V0/libnet80211.a deleted file mode 100644 index 576304be26..0000000000 Binary files a/tools/sdk/lib/NONOSDK3V0/libnet80211.a and /dev/null differ diff --git a/tools/sdk/lib/NONOSDK3V0/libphy.a b/tools/sdk/lib/NONOSDK3V0/libphy.a deleted file mode 100644 index dfd469518e..0000000000 Binary files a/tools/sdk/lib/NONOSDK3V0/libphy.a and /dev/null differ diff --git a/tools/sdk/lib/NONOSDK3V0/libpp.a b/tools/sdk/lib/NONOSDK3V0/libpp.a deleted file mode 100644 index fbf3e85636..0000000000 Binary files a/tools/sdk/lib/NONOSDK3V0/libpp.a and /dev/null differ diff --git a/tools/sdk/lib/NONOSDK3V0/libsmartconfig.a b/tools/sdk/lib/NONOSDK3V0/libsmartconfig.a deleted file mode 100644 index 34598d36a6..0000000000 Binary files a/tools/sdk/lib/NONOSDK3V0/libsmartconfig.a and /dev/null differ diff --git a/tools/sdk/lib/NONOSDK3V0/libwpa.a b/tools/sdk/lib/NONOSDK3V0/libwpa.a deleted file mode 100644 index 3a58113a1a..0000000000 Binary files a/tools/sdk/lib/NONOSDK3V0/libwpa.a and /dev/null differ diff --git a/tools/sdk/lib/NONOSDK3V0/libwpa2.a b/tools/sdk/lib/NONOSDK3V0/libwpa2.a deleted file mode 100644 index b868608a14..0000000000 Binary files a/tools/sdk/lib/NONOSDK3V0/libwpa2.a and /dev/null differ diff --git a/tools/sdk/lib/NONOSDK3V0/libwps.a b/tools/sdk/lib/NONOSDK3V0/libwps.a deleted file mode 100644 index 1f6b365e3a..0000000000 Binary files a/tools/sdk/lib/NONOSDK3V0/libwps.a and /dev/null differ diff --git a/tools/sdk/lib/NONOSDK3V0/version b/tools/sdk/lib/NONOSDK3V0/version deleted file mode 100644 index 06e014650a..0000000000 --- a/tools/sdk/lib/NONOSDK3V0/version +++ /dev/null @@ -1 +0,0 @@ -v2.2.0-28-g89920dc \ No newline at end of file diff --git a/tools/sdk/lib/README.md b/tools/sdk/lib/README.md index 6b92d04307..6e30c9a58d 100644 --- a/tools/sdk/lib/README.md +++ b/tools/sdk/lib/README.md @@ -1,8 +1,20 @@ +## Adding a new SDK library + +- Create a directory for the new SDK. +- Copy .a files from SDK `lib` directory to the new directory +- Add the new SDK directory to those supported in `eval_fix_sdks.sh` and `fix_sdk_libs.sh`. +- To support WPA2 Enterprise connections, some patches are reguired review `wpa2_eap_patch.cpp` and `eval_fix_sdks.sh` for details. +- Use `./eval_fix_sdks.sh --analyze` to aid in finding relevant differences. + - Also, you can compare two SDKs with something like `./eval_fix_sdks.sh --analyze "NONOSDK305\nNONOSDK306"` +- Apply updates to `fix_sdk_libs.sh` and `wpa2_eap_patch.cpp`. You can run `./eval_fix_sdks.sh --patch` to do a batch run of `fix_sdk_libs.sh` against each SDK. +- If you used this section, you can skip _Updating SDK libraries_. + ## Updating SDK libraries - Copy .a files from SDK `lib` directory to this directory - Run `fix_sdk_libs.sh` + ## Updating libstdc++ After building gcc using crosstool-NG, get compiled libstdc++ and remove some objects: @@ -17,4 +29,3 @@ xtensa-lx106-elf-ar d libstdc++.a del_opv.o xtensa-lx106-elf-ar d libstdc++.a new_op.o xtensa-lx106-elf-ar d libstdc++.a new_opv.o ``` - diff --git a/tools/sdk/lib/eval_fix_sdks.sh b/tools/sdk/lib/eval_fix_sdks.sh new file mode 100755 index 0000000000..389ee31650 --- /dev/null +++ b/tools/sdk/lib/eval_fix_sdks.sh @@ -0,0 +1,128 @@ +#!/bin/bash +# set -e + +single_sdk="${2}" +if [[ -n "$single_sdk" ]]; then + if [[ "NONOSDK" != "${single_sdk:0:7}" ]]; then + single_sdk="" + fi +fi + +add_path_ifexist() { + if [[ -d $1 ]]; then + export PATH=$( realpath $1 ):$PATH + return 0 + fi + return 1 +} + +if ! which xtensa-lx106-elf-ar | grep "tools/xtensa-lx106-elf/bin" >>/dev/null; then + add_path_ifexist "../../../xtensa-lx106-elf/bin" || add_path_ifexist "../../xtensa-lx106-elf/bin" +fi + +help_msg() { + cat <new.txt + if [[ -f old.txt ]]; then + echo "eap_peer_config_deinit: diff $prev_sdk $sdk" + diff old.txt new.txt + echo "" + fi + mv new.txt old.txt + prev_sdk=${sdk} + done + + unset prev_sdk + for sdk in `list_sdks`; do + unasm -j ".text.wpa2_sm_rx_eapol" ${sdk}/eap.o >new.txt + if [[ -f old2.txt ]]; then + echo "wpa2_sm_rx_eapol: diff $prev_sdk $sdk" + diff old2.txt new.txt + echo "" + fi + mv new.txt old2.txt + prev_sdk=${sdk} + done + + # Find offsets for patching vPortFree with z2EapFree + for sdk in `list_sdks`; do + echo -en "\n${sdk}/eap.o:\n " + grep --byte-offset --only-matching --text vPortFree ${sdk}/eap.o + done + + cleanup +} + + +patch_all() { + for sdk in `list_sdks`; do + pushd $sdk + ../fix_sdk_libs.sh + popd + done +} + +if [[ "${1}" == "--analyze" ]]; then + analyze +elif [[ "${1}" == "--patch" ]]; then + patch_all +else + help_msg +fi +exit 0 diff --git a/tools/sdk/lib/fix_sdk_libs.sh b/tools/sdk/lib/fix_sdk_libs.sh index b6aea12752..d024b46a08 100755 --- a/tools/sdk/lib/fix_sdk_libs.sh +++ b/tools/sdk/lib/fix_sdk_libs.sh @@ -1,36 +1,156 @@ #!/bin/bash set -e -export PATH=../../xtensa-lx106-elf/bin:$PATH +add_path_ifexist() { + if [[ -d $1 ]]; then + export PATH=$( realpath $1 ):$PATH + return 0 + fi + return 1 +} + +if ! which xtensa-lx106-elf-ar | grep "tools/xtensa-lx106-elf/bin" >>/dev/null; then + add_path_ifexist "../../../xtensa-lx106-elf/bin" || add_path_ifexist "../../xtensa-lx106-elf/bin" +fi +WORK_SPACE=${PWD} + VERSION=$(basename ${PWD}) addSymbol_system_func1() { - ADDRESS=$1 - xtensa-lx106-elf-objcopy --add-symbol system_func1=.irom0.text:${ADDRESS},function,global user_interface.o + if ! xtensa-lx106-elf-nm user_interface.o | grep -q " T system_func1"; then # Don't add symbol if it already exists + ADDRESS=$1 + xtensa-lx106-elf-objcopy --add-symbol system_func1=.irom0.text:${ADDRESS},function,global user_interface.o + fi +} + +patchFile() { + FILE=$1 + ADDRESS=$2 # DO NOT PASS AS HEX! + LENGTH=$3 # DO NOT PASS AS HEX! + EXPECTED=$4 + REPLACEWITH=$5 + if [[ "$(dd if=$FILE bs=1 count=$LENGTH skip=$ADDRESS status=none | base64 -w0)" = "$EXPECTED" ]]; then + echo "Patching $VERSION $1 ..." + echo $5 | base64 -d | dd of=$FILE bs=1 count=$LENGTH seek=$ADDRESS conv=notrunc + elif ! [[ "$(dd if=$FILE bs=1 count=$LENGTH skip=$ADDRESS status=none | base64 -w0)" = "$REPLACEWITH" ]]; then + echo "PATCH FAILED!" + echo "dd if=$FILE bs=1 count=$LENGTH skip=$ADDRESS status=none | base64 -w0" + dd if=$FILE bs=1 count=$LENGTH skip=$ADDRESS status=none | hexdump -C + dd if=$FILE bs=1 count=$LENGTH skip=$ADDRESS status=none | base64 -w0 + echo "" + exit 1 + fi +} + +grepPatchFiles() { + local SDKVER OLDNAME NEWNAME FILES OLDNAME64 NEWNAME64 FILE OFFSET PATTERN + SDKVER="${1}" + OLDNAME="${2}" + NEWNAME="${3}" + FILES="${4}" + [[ "${SDKVER:0:9}" != "NONOSDK30" ]] && return + if [[ -z "${FILES}" ]]; then + echo "grepPatchFile: bad input: file specification required" + exit 1 + fi + if [[ "${#OLDNAME}" != "${#NEWNAME}" ]]; then + echo "grepPatchFile: bad input: old name ${OLDNAME}(${#OLDNAME}) and new name ${NEWNAME}(${#NEWNAME}) must be the same length." + exit 1 + fi + OLDNAME64=( `echo -n "${OLDNAME}" | base64 -w0` ) + NEWNAME64=( `echo -n "${NEWNAME}" | base64 -w0` ) + + while read -u3 FILE OFFSET PATTERN; do + if [[ "${#OLDNAME}" == "${#PATTERN}" ]] && [[ "${OLDNAME}" == "${PATTERN}" ]]; then + patchFile "$FILE" "$OFFSET" "${#PATTERN}" "${OLDNAME64}" "${NEWNAME64}" + else + echo "grepPatchFile: bad parameters FILE=${FILE} OFFSET=${OFFSET} PATTERN=${PATTERN}" + exit 1 + fi + done 3< <( grep --with-filename --byte-offset --only-matching --text "${OLDNAME}" $FILES | tr ":" " " ) + return +} + +redefineSym() { + local SDKVER OLDNAME NEWNAME FILES FILE EXTRA PATTERN + SDKVER="${1}" + OLDNAME="${2}" + NEWNAME="${3}" + FILES="${4}" + [[ "${SDKVER:0:9}" != "NONOSDK30" ]] && return + if [[ -z "${FILES}" ]]; then + echo "redefineSym: bad input: file specification required" + exit 1 + fi + PATTERN="UND ${OLDNAME}" + for FILE in $FILES ; do + echo "xtensa-lx106-elf-objcopy --redefine-sym ${OLDNAME}=${NEWNAME} \"$FILE\"" + echo "Before:" + xtensa-lx106-elf-nm "$FILE" | grep ${OLDNAME} | sort -u + xtensa-lx106-elf-objcopy --redefine-sym ${OLDNAME}=${NEWNAME} "$FILE" + echo "After:" + xtensa-lx106-elf-nm "$FILE" | grep ${OLDNAME} | sort -u + done + return } +# # xtensa-lx106-elf-ar x libwpa2.a eap.o +if [[ "--shell" == "$1" ]]; then + # need to poke around a bit + bash --rcfile <(echo ". ~/.bashrc; cd ${WORK_SPACE}") + exit 0 +fi + +if [[ ! -f libmain.a ]]; then + echo -e "\n\n*** Archive libmain.a is missing ***\n\n" + exit 1 +fi # Remove mem_manager.o from libmain.a to use custom heap implementation, # and time.o to fix redefinition of time-related functions: xtensa-lx106-elf-ar d libmain.a mem_manager.o xtensa-lx106-elf-ar d libmain.a time.o +# Patch WPA2-Enterprise double-free +xtensa-lx106-elf-ar x libwpa2.a eap.o +eapcs=$(sha256sum eap.o | awk '{print $1}') + # Rename `hostname` and `default_hostname` symbols: xtensa-lx106-elf-ar x libmain.a eagle_lwip_if.o user_interface.o -xtensa-lx106-elf-objcopy --redefine-sym hostname=wifi_station_hostname user_interface.o -xtensa-lx106-elf-objcopy --redefine-sym hostname=wifi_station_hostname eagle_lwip_if.o -xtensa-lx106-elf-objcopy --redefine-sym default_hostname=wifi_station_default_hostname user_interface.o -xtensa-lx106-elf-objcopy --redefine-sym default_hostname=wifi_station_default_hostname eagle_lwip_if.o +lwipcs=$(sha256sum eagle_lwip_if.o | awk '{print $1}') +uics=$(sha256sum user_interface.o | awk '{print $1}') +xtensa-lx106-elf-objcopy --redefine-sym hostname=wifi_station_hostname user_interface.o +xtensa-lx106-elf-objcopy --redefine-sym hostname=wifi_station_hostname eagle_lwip_if.o +xtensa-lx106-elf-objcopy --redefine-sym default_hostname=wifi_station_default_hostname user_interface.o +xtensa-lx106-elf-objcopy --redefine-sym default_hostname=wifi_station_default_hostname eagle_lwip_if.o + if [[ ${VERSION} == "NONOSDK221" ]]; then addSymbol_system_func1 "0x60" + patchFile "eap.o" "3055" "2" "wAA=" "8CA=" # WPA2-Enterprise patch which replaces a double-free with nop, see #8082 + patchFile "eap.o" "26352" "9" "dlBvcnRGcmVl" "ejJFYXBGcmVl" # special vPortFree to recover leaked memory elif [[ ${VERSION} == "NONOSDK22x"* ]]; then addSymbol_system_func1 "0x54" -elif [[ ${VERSION} == "NONOSDK3"* ]]; then - addSymbol_system_func1 "0x60" + patchFile "eap.o" "3059" "2" "wAA=" "8CA=" # WPA2-Enterprise patch which replaces a double-free with nop, see #8082 + patchFile "eap.o" "26356" "9" "dlBvcnRGcmVl" "ejJFYXBGcmVl" # special vPortFree to recover leaked memory +elif [[ ${VERSION} == "NONOSDK305" ]]; then + addSymbol_system_func1 "0x54" + patchFile "eap.o" "67670" "9" "dlBvcnRGcmVl" "ejJFYXBGcmVl" # special vPortFree to recover leaked memory else echo "WARN: Unknown address for system_func1() called by system_restart_local()" fi -xtensa-lx106-elf-ar r libmain.a eagle_lwip_if.o user_interface.o -rm -f eagle_lwip_if.o user_interface.o +if [[ $(sha256sum eap.o | awk '{print $1}') != $eapcs ]]; then + xtensa-lx106-elf-ar r libwpa2.a eap.o +fi +if [[ $(sha256sum user_interface.o | awk '{print $1}') != $uics || $(sha256sum eagle_lwip_if.o | awk '{print $1}') != $lwipcs ]]; then + xtensa-lx106-elf-ar r libmain.a eagle_lwip_if.o user_interface.o +fi +rm -f eagle_lwip_if.o user_interface.o eap.o + +if [[ ${VERSION:0:9} == "NONOSDK30" ]]; then + # v3.0.0 and up use a non-standard pvPortMalloc. + # SDK Library global replace + redefineSym "${VERSION}" "pvPortMalloc" "sdk3_pvPortMalloc" '*.a' + xtensa-lx106-elf-objcopy --weaken-symbol load_non_32_wide_handler libmain.a +fi diff --git a/tools/sdk/lib/libbearssl.a b/tools/sdk/lib/libbearssl.a index 475cb3912d..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 06b59b1d1f..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 0e22ca6214..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 c2ee7b6ad2..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 208e29ac6f..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 70a57224a5..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 037cc30ee2..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 94fc1e2606..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 0fbda80329..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 a10099b2ce..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 6105635531..5166f2bb03 160000 --- a/tools/sdk/ssl/bearssl +++ b/tools/sdk/ssl/bearssl @@ -1 +1 @@ -Subproject commit 6105635531027f5b298aa656d44be2289b2d434f +Subproject commit 5166f2bb03fb03597b0f2c8c7fbcf01616df67c9 diff --git a/tools/signing.py b/tools/signing.py index 40405288c9..9274bb537a 100755 --- a/tools/signing.py +++ b/tools/signing.py @@ -4,6 +4,7 @@ import argparse import hashlib import os +import struct import subprocess import sys @@ -30,7 +31,7 @@ def sign_and_write(data, priv_key, out_file): with open(out_file, "wb") as out: out.write(data) out.write(signout) - out.write(b'\x00\x01\x00\x00') + out.write(struct.pack(". -from __future__ import print_function import argparse import os import subprocess import sys +import locale + +sys.stdout = sys.stderr + + +# retrieve *system* encoding, not the one used by python internally +if sys.version_info >= (3, 11): + def get_encoding(): + return locale.getencoding() +else: + def get_encoding(): + return locale.getdefaultlocale()[1] + + +def get_segment_sizes(elf, path, mmu): + iram_size = 0 + iheap_size = 0 + icache_size = 32168 + + for line in mmu.split(): + words = line.split("=") + if line.startswith("-DMMU_IRAM_SIZE"): + iram_size = int(words[1], 16) + elif line.startswith("-DMMU_ICACHE_SIZE"): + icache_size = int(words[1], 16) + elif line.startswith("-DMMU_SEC_HEAP_SIZE"): + iheap_size = int(words[1], 16) + + sizes = [ + [ + "Variables and constants in RAM (global, static)", + [ + { + "DATA": 0, + "RODATA": 0, + "BSS": 0, + }, + 80192, + ], + ], + [ + "Instruction RAM (IRAM_ATTR, ICACHE_RAM_ATTR)", + [ + { + "ICACHE": icache_size, + "IHEAP": iheap_size, + "IRAM": 0, + }, + 65536, + ], + ], + ["Code in flash (default, ICACHE_FLASH_ATTR)", [{"IROM": 0}, 1048576]], + ] + + section_mapping = ( + (".irom0.text", "IROM"), + (".text", "IRAM"), + (".data", "DATA"), + (".rodata", "RODATA"), + (".bss", "BSS"), + ) + + cmd = [os.path.join(path, "xtensa-lx106-elf-size"), "-A", elf] + with subprocess.Popen(cmd, stdout=subprocess.PIPE, universal_newlines=True, encoding=get_encoding()) as proc: + lines = proc.stdout.readlines() + for line in lines: + words = line.split() + for section, target in section_mapping: + if not line.startswith(section): + continue + for group, (segments, total) in sizes: + if target in segments: + segments[target] += int(words[1]) + assert segments[target] <= total -def get_segment_hints(iram): - hints = {} - hints['ICACHE'] = ' - flash instruction cache' - hints['IROM'] = ' - code in flash (default or ICACHE_FLASH_ATTR)' - hints['IRAM'] = ' / ' + str(iram) + ' - code in IRAM (IRAM_ATTR, ISRs...)' - hints['DATA'] = ') - initialized variables (global, static) in RAM/HEAP' - hints['RODATA'] = ') / 81920 - constants (global, static) in RAM/HEAP' - hints['BSS'] = ') - zeroed variables (global, static) in RAM/HEAP' - return hints - -def get_segment_sizes(elf, path): - sizes = {} - sizes['ICACHE'] = 0 - sizes['IROM'] = 0 - sizes['IRAM'] = 0 - sizes['DATA'] = 0 - sizes['RODATA'] = 0 - sizes['BSS'] = 0 - p = subprocess.Popen([path + "/xtensa-lx106-elf-size", '-A', elf], stdout=subprocess.PIPE, universal_newlines=True ) - lines = p.stdout.readlines() - for line in lines: - words = line.split() - if line.startswith('.irom0.text'): - sizes['IROM'] = sizes['IROM'] + int(words[1]) - elif line.startswith('.text'): # Gets .text and .text1 - sizes['IRAM'] = sizes['IRAM'] + int(words[1]) - elif line.startswith('.data'): # Gets .text and .text1 - sizes['DATA'] = sizes['DATA'] + int(words[1]) - elif line.startswith('.rodata'): # Gets .text and .text1 - sizes['RODATA'] = sizes['RODATA'] + int(words[1]) - elif line.startswith('.bss'): # Gets .text and .text1 - sizes['BSS'] = sizes['BSS'] + int(words[1]) return sizes -def get_mmu_sizes(mmu, sizes): - iram = 0x8000 - sizes['ICACHE'] = 0x8000 - lines = mmu.split(' ') - for line in lines: - words = line.split('=') - if line.startswith('-DMMU_IRAM_SIZE'): - iram = int(words[1], 16) - elif line.startswith('-DMMU_ICACHE_SIZE'): - sizes['ICACHE'] = int(words[1], 16) - return [iram, sizes] + +def percentage(lhs, rhs): + return "{}%".format(int(100.0 * float(lhs) / float(rhs))) + + +HINTS = { + "ICACHE": "reserved space for flash instruction cache", + "IRAM": "code in IRAM", + "IHEAP": "secondary heap space", + "IROM": "code in flash", + "DATA": "initialized variables", + "RODATA": "constants", + "BSS": "zeroed variables", +} + + +def safe_prefix(n, length): + if n == length: + return "`--" + + return "|--" + + +def prefix(n, length): + if n == length: + return "╚══" + + return "╠══" + + +def filter_segments(segments): + used = 0 + number = 0 + available = [] + + for (segment, size) in segments.items(): + if not size: + continue + used += size + number += 1 + available.append((number, segment, size)) + + return (number, used, available) + def main(): - parser = argparse.ArgumentParser(description='Report the different segment sizes of a compiled ELF file') - parser.add_argument('-e', '--elf', action='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/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 */