diff --git a/.github/workflows/build-clang-doxy.yml b/.github/workflows/build-clang-doxy.yml index c1b5c893d..6df9ee3e6 100644 --- a/.github/workflows/build-clang-doxy.yml +++ b/.github/workflows/build-clang-doxy.yml @@ -12,160 +12,322 @@ on: required: true jobs: + build-esp32sx-esptool: + name: Build ESP32-Sx + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + arduino-platform: ["funhouse_noota"] + include: + - offset: "0x1000" + steps: + - uses: actions/setup-python@v4 + with: + python-version: "3.x" + - uses: actions/checkout@v4 + - name: Get WipperSnapper version + run: | + git fetch --prune --unshallow --tags + git describe --dirty --tags + echo >>$GITHUB_ENV WS_VERSION=$(git describe --dirty --tags) + - uses: actions/checkout@v4 + with: + repository: adafruit/ci-arduino + path: ci + ref: ci-wippersnapper + - name: Checkout Board Definitions + uses: actions/checkout@v4 + with: + repository: adafruit/Wippersnapper_Boards + path: ws-boards + - name: Install CI-Arduino + run: bash ci/actions_install.sh + - name: Install extra Arduino libraries + run: | + git clone --quiet https://github.com/milesburton/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library + git clone --quiet https://github.com/pstolarz/OneWireNg.git /home/runner/Arduino/libraries/OneWireNg + git clone --quiet https://github.com/adafruit/Adafruit_HX8357_Library.git /home/runner/Arduino/libraries/Adafruit_HX8357_Library + git clone --quiet https://github.com/adafruit/Adafruit_ILI9341.git /home/runner/Arduino/libraries/Adafruit_ILI9341 + git clone --quiet https://github.com/adafruit/Adafruit_STMPE610.git /home/runner/Arduino/libraries/Adafruit_STMPE610 + git clone --quiet https://github.com/adafruit/Adafruit-ST7735-Library.git /home/runner/Arduino/libraries/Adafruit-ST7735-Library + git clone --quiet https://github.com/adafruit/Adafruit_TouchScreen.git /home/runner/Arduino/libraries/Adafruit_TouchScreen + git clone --quiet https://github.com/adafruit/Adafruit_TinyUSB_Arduino /home/runner/Arduino/libraries/Adafruit_TinyUSB_Arduino + git clone --depth 1 --branch wippersnapper https://github.com/brentru/lvgl.git /home/runner/Arduino/libraries/lvgl + git clone --depth 1 --branch development https://github.com/brentru/Adafruit_LvGL_Glue.git /home/runner/Arduino/libraries/Adafruit_LittlevGL_Glue_Library + - name: Download and install stable Nanopb + run: | + # Download and extract nanopb + wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.8.tar.gz + tar -xf nanopb-0.4.8.tar.gz + # Copy files to WipperSnapper's src/nanopb directory + cp nanopb/pb_common.* nanopb/pb_encode.* nanopb/pb_decode.* src/nanopb + mv nanopb/pb.h src/nanopb/nanopb.pb.h + - name: List all files in Adafruit_LittlevGL_Glue_Library folder + run: | + ls /home/runner/Arduino/libraries/Adafruit_LittlevGL_Glue_Library + - name: Copy lv_conf.h file in Adafruit_LittlevGL_Glue_Library to the arduino library folder + run: | + cp /home/runner/Arduino/libraries/Adafruit_LittlevGL_Glue_Library/lv_conf.h /home/runner/Arduino/libraries + - name: Install Dependencies (esptool) + run: | + pip3 install esptool + - name: Build for ESP32-SX + run: python3 ci/build_platform.py ${{ matrix.arduino-platform }} --build_timeout 48000 + - name: list files + run: | + ls -Rla examples/ + - name: Rename build artifacts to reflect the platform name + run: | + mv examples/Wippersnapper_demo/build/*/Wippersnapper_demo.ino.bin wippersnapper.${{ matrix.arduino-platform }}.fatfs.${{ env.WS_VERSION }}.bin + mv examples/Wippersnapper_demo/build/*/Wippersnapper_demo.ino.elf wippersnapper.${{ matrix.arduino-platform }}.fatfs.${{ env.WS_VERSION }}.elf + mv examples/Wippersnapper_demo/build/*/Wippersnapper_demo.ino.map wippersnapper.${{ matrix.arduino-platform }}.fatfs.${{ env.WS_VERSION }}.map + mv examples/Wippersnapper_demo/build/*/Wippersnapper_demo.ino.bootloader.bin wippersnapper.${{ matrix.arduino-platform }}.fatfs.${{ env.WS_VERSION }}.bootloader.bin + mv examples/Wippersnapper_demo/build/*/Wippersnapper_demo.ino.partitions.bin wippersnapper.${{ matrix.arduino-platform }}.fatfs.${{ env.WS_VERSION }}.partitions.bin + - name: Get Board Flash Parameters + id: get_board_json + run: | + board_name=${{ matrix.arduino-platform }} + # Remove '_noota' suffix if present + board_name=${board_name%_noota} + # Remove 'wippersnapper_' prefix if present + board_name=${board_name#wippersnapper_} + content=$(cat ws-boards/boards/${board_name//_/-}/definition.json) + { + echo 'boardJson<> "$GITHUB_OUTPUT" + - name: Check boot_app0 file existence (esp32sx built from core, not-source) + id: check_files + uses: andstor/file-existence-action@v2 + with: + files: "/home/runner/.arduino15/packages/esp32/hardware/esp32/*/tools/partitions/boot_app0.bin" + - name: list arduino esp32 core files + if: steps.check_files.outputs.files_exists == 'true' + run: | + ls /home/runner/.arduino15/packages/esp32/hardware/esp32/*/tools/partitions + - name: list arduino esp32 bsp core files + if: steps.check_files.outputs.files_exists == 'false' + run: | + ls /home/runner/Arduino/hardware/espressif/esp32/tools/partitions + - name: boot_app0 file from arduino-cli core + if: steps.check_files.outputs.files_exists == 'true' + run: mv /home/runner/.arduino15/packages/esp32/hardware/esp32/*/tools/partitions/boot_app0.bin wippersnapper.${{ matrix.arduino-platform }}.fatfs.${{ env.WS_VERSION }}.boot_app0.bin + - name: boot_app0 file from esp32 source bsp + if: steps.check_files.outputs.files_exists == 'false' + run: mv /home/runner/Arduino/hardware/espressif/esp32/tools/partitions/boot_app0.bin wippersnapper.${{ matrix.arduino-platform }}.fatfs.${{ env.WS_VERSION }}.boot_app0.bin + - name: Create combined binary using Esptool merge_bin + run: | + echo ${{ steps.get_board_json.outputs.boardJson }} + echo ${{ fromJson(steps.get_board_json.outputs.boardJson) }} + python3 -m esptool --chip ${{fromJson(steps.get_board_json.outputs.boardJson).esptool.chip}} merge_bin \ + --flash_mode ${{fromJson(steps.get_board_json.outputs.boardJson).esptool.flashMode}} \ + --flash_freq ${{fromJson(steps.get_board_json.outputs.boardJson).esptool.flashFreq}} \ + --flash_size ${{fromJson(steps.get_board_json.outputs.boardJson).esptool.flashSize}} \ + -o wippersnapper.${{ matrix.arduino-platform }}.fatfs.${{ env.WS_VERSION }}.combined.bin \ + ${{ matrix.offset }} wippersnapper.${{ matrix.arduino-platform }}.fatfs.${{ env.WS_VERSION }}.bootloader.bin \ + 0x8000 wippersnapper.${{ matrix.arduino-platform }}.fatfs.${{ env.WS_VERSION }}.partitions.bin \ + 0xe000 wippersnapper.${{ matrix.arduino-platform }}.fatfs.${{ env.WS_VERSION }}.boot_app0.bin \ + 0x10000 wippersnapper.${{ matrix.arduino-platform }}.fatfs.${{ env.WS_VERSION }}.bin + - name: Zip build artifacts + run: | + zip -r wippersnapper.${{ matrix.arduino-platform }}.fatfs.${{ env.WS_VERSION }}.zip wippersnapper.${{ matrix.arduino-platform }}.fatfs.${{ env.WS_VERSION }}.* + - name: upload build artifacts zip + uses: actions/upload-artifact@v3 + with: + name: build-files + path: | + wippersnapper.${{ matrix.arduino-platform }}.fatfs.${{ env.WS_VERSION }}.zip + - name: Rename build artifacts to reflect the platform name + run: | + mv examples/*/build/*/Wippersnapper_demo.ino.uf2 wippersnapper.${{ matrix.arduino-platform }}.${{ env.WS_VERSION }}.uf2 + - name: upload build artifacts + uses: actions/upload-artifact@v3 + with: + name: build-files + path: | + wippersnapper.${{ matrix.arduino-platform }}.${{ env.WS_VERSION }}.uf2 + build-esp32sx: - name: Build WipperSnapper ESP32-Sx + name: Build ESP32-Sx runs-on: ubuntu-latest strategy: fail-fast: false matrix: - arduino-platform: ["funhouse_noota", "magtag", - "metroesp32s2", "feather_esp32s2", - "feather_esp32s2_tft", "feather_esp32s2_reverse_tft", - "feather_esp32s3", "feather_esp32s3_4mbflash_2mbpsram", - "feather_esp32s3_tft", "qtpy_esp32s3", - "qtpy_esp32s2", "feather_esp32s3_reverse_tft", - "qtpy_esp32s3_n4r2"] + arduino-platform: + [ + "magtag", + "metroesp32s2", + "feather_esp32s2", + "feather_esp32s2_tft", + "feather_esp32s2_reverse_tft", + "feather_esp32s3", + "feather_esp32s3_4mbflash_2mbpsram", + "feather_esp32s3_tft", + "qtpy_esp32s3", + "qtpy_esp32s2", + "feather_esp32s3_reverse_tft", + "qtpy_esp32s3_n4r2", + ] steps: - - uses: actions/setup-python@v4 - with: - python-version: '3.x' - - uses: actions/checkout@v4 - - name: Get WipperSnapper version - run: | - git fetch --prune --unshallow --tags - git describe --dirty --tags - echo >>$GITHUB_ENV WS_VERSION=$(git describe --dirty --tags) - - uses: actions/checkout@v4 - with: - repository: adafruit/ci-arduino - path: ci - - name: Install CI-Arduino - run: bash ci/actions_install.sh - - name: Install extra Arduino libraries - run: | - git clone --quiet https://github.com/milesburton/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library - git clone --quiet https://github.com/pstolarz/OneWireNg.git /home/runner/Arduino/libraries/OneWireNg - git clone --quiet https://github.com/adafruit/Adafruit_HX8357_Library.git /home/runner/Arduino/libraries/Adafruit_HX8357_Library - git clone --quiet https://github.com/adafruit/Adafruit_ILI9341.git /home/runner/Arduino/libraries/Adafruit_ILI9341 - git clone --quiet https://github.com/adafruit/Adafruit_STMPE610.git /home/runner/Arduino/libraries/Adafruit_STMPE610 - git clone --quiet https://github.com/adafruit/Adafruit-ST7735-Library.git /home/runner/Arduino/libraries/Adafruit-ST7735-Library - git clone --quiet https://github.com/adafruit/Adafruit_TouchScreen.git /home/runner/Arduino/libraries/Adafruit_TouchScreen - git clone --depth 1 --branch wippersnapper https://github.com/brentru/lvgl.git /home/runner/Arduino/libraries/lvgl - git clone --depth 1 --branch development https://github.com/brentru/Adafruit_LvGL_Glue.git /home/runner/Arduino/libraries/Adafruit_LittlevGL_Glue_Library - - name: Download and install stable Nanopb - run: | - # Download and extract nanopb - wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.8.tar.gz - tar -xf nanopb-0.4.8.tar.gz - # Copy files to WipperSnapper's src/nanopb directory - cp nanopb/pb_common.* nanopb/pb_encode.* nanopb/pb_decode.* src/nanopb - mv nanopb/pb.h src/nanopb/nanopb.pb.h - - name: List all files in Adafruit_LittlevGL_Glue_Library folder - run: | - ls /home/runner/Arduino/libraries/Adafruit_LittlevGL_Glue_Library - - name: Copy lv_conf.h file in Adafruit_LittlevGL_Glue_Library to the arduino library folder - run: | - cp /home/runner/Arduino/libraries/Adafruit_LittlevGL_Glue_Library/lv_conf.h /home/runner/Arduino/libraries - - name: Build for ESP32-SX - run: python3 ci/build_platform.py ${{ matrix.arduino-platform }} --build_timeout 48000 - - name: list - run: | - ls - ls examples/*/build/ - - name: Rename build artifacts to reflect the platform name - run: | - mv examples/*/build/*/Wippersnapper_demo.ino.uf2 wippersnapper.${{ matrix.arduino-platform }}.${{ env.WS_VERSION }}.uf2 - mv examples/*/build/*/Wippersnapper_demo.ino.bin wippersnapper.${{ matrix.arduino-platform }}.${{ env.WS_VERSION }}.bin - - name: upload build artifacts - uses: actions/upload-artifact@v3 - with: - name: build-files - path: | + - uses: actions/setup-python@v4 + with: + python-version: "3.x" + - uses: actions/checkout@v4 + - name: Get WipperSnapper version + run: | + git fetch --prune --unshallow --tags + git describe --dirty --tags + echo >>$GITHUB_ENV WS_VERSION=$(git describe --dirty --tags) + - uses: actions/checkout@v4 + with: + repository: adafruit/ci-arduino + path: ci + ref: ci-wippersnapper + - name: Install CI-Arduino + run: bash ci/actions_install.sh + - name: Install extra Arduino libraries + run: | + git clone --quiet https://github.com/milesburton/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library + git clone --quiet https://github.com/pstolarz/OneWireNg.git /home/runner/Arduino/libraries/OneWireNg + git clone --quiet https://github.com/adafruit/Adafruit_HX8357_Library.git /home/runner/Arduino/libraries/Adafruit_HX8357_Library + git clone --quiet https://github.com/adafruit/Adafruit_ILI9341.git /home/runner/Arduino/libraries/Adafruit_ILI9341 + git clone --quiet https://github.com/adafruit/Adafruit_STMPE610.git /home/runner/Arduino/libraries/Adafruit_STMPE610 + git clone --quiet https://github.com/adafruit/Adafruit-ST7735-Library.git /home/runner/Arduino/libraries/Adafruit-ST7735-Library + git clone --quiet https://github.com/adafruit/Adafruit_TouchScreen.git /home/runner/Arduino/libraries/Adafruit_TouchScreen + git clone --quiet https://github.com/adafruit/Adafruit_TinyUSB_Arduino /home/runner/Arduino/libraries/Adafruit_TinyUSB_Arduino + git clone --depth 1 --branch wippersnapper https://github.com/brentru/lvgl.git /home/runner/Arduino/libraries/lvgl + git clone --depth 1 --branch development https://github.com/brentru/Adafruit_LvGL_Glue.git /home/runner/Arduino/libraries/Adafruit_LittlevGL_Glue_Library + - name: Download and install stable Nanopb + run: | + # Download and extract nanopb + wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.8.tar.gz + tar -xf nanopb-0.4.8.tar.gz + # Copy files to WipperSnapper's src/nanopb directory + cp nanopb/pb_common.* nanopb/pb_encode.* nanopb/pb_decode.* src/nanopb + mv nanopb/pb.h src/nanopb/nanopb.pb.h + - name: List all files in Adafruit_LittlevGL_Glue_Library folder + run: | + ls /home/runner/Arduino/libraries/Adafruit_LittlevGL_Glue_Library + - name: Copy lv_conf.h file in Adafruit_LittlevGL_Glue_Library to the arduino library folder + run: | + cp /home/runner/Arduino/libraries/Adafruit_LittlevGL_Glue_Library/lv_conf.h /home/runner/Arduino/libraries + - name: Build for ESP32-SX + run: python3 ci/build_platform.py ${{ matrix.arduino-platform }} --build_timeout 48000 + - name: list + run: | + ls + ls examples/*/build/ + - name: Rename build artifacts to reflect the platform name + run: | + mv examples/*/build/*/Wippersnapper_demo.ino.uf2 wippersnapper.${{ matrix.arduino-platform }}.${{ env.WS_VERSION }}.uf2 + mv examples/*/build/*/Wippersnapper_demo.ino.bin wippersnapper.${{ matrix.arduino-platform }}.${{ env.WS_VERSION }}.bin + - name: upload build artifacts + uses: actions/upload-artifact@v3 + with: + name: build-files + path: | wippersnapper.${{ matrix.arduino-platform }}.${{ env.WS_VERSION }}.uf2 wippersnapper.${{ matrix.arduino-platform }}.${{ env.WS_VERSION }}.bin build-esp32: - name: Build WipperSnapper ESP32, ESP32-C3 + name: Build ESP32 and Cx runs-on: ubuntu-latest strategy: fail-fast: false matrix: - arduino-platform: ["feather_esp32", "qtpy_esp32", "feather_esp32_v2", "itsybitsy_esp32", "qtpy_esp32c3"] + arduino-platform: + [ + "wippersnapper_feather_esp32", + "qtpy_esp32", + "feather_esp32_v2", + "itsybitsy_esp32", + "wippersnapper_qtpy_esp32c3", + "wippersnapper_feather_esp32c6", + ] include: - offset: "0x1000" - offset: "0x0" - arduino-platform: "qtpy_esp32c3" + arduino-platform: "wippersnapper_qtpy_esp32c3" + - offset: "0x0" + arduino-platform: "wippersnapper_feather_esp32c6" steps: - - uses: actions/setup-python@v4 - with: - python-version: '3.x' - - uses: actions/checkout@v4 - - name: Get WipperSnapper version - run: | - git fetch --prune --unshallow --tags - git describe --dirty --tags - echo >>$GITHUB_ENV WS_VERSION=$(git describe --dirty --tags) - - uses: actions/checkout@v4 - with: - repository: adafruit/ci-arduino - path: ci - - name: Checkout Board Definitions - uses: actions/checkout@v4 - with: - repository: adafruit/Wippersnapper_Boards - path: ws-boards - - name: Install CI-Arduino - run: bash ci/actions_install.sh - - name: Install extra Arduino libraries - run: | - git clone --quiet https://github.com/milesburton/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library - git clone --quiet https://github.com/pstolarz/OneWireNg.git /home/runner/Arduino/libraries/OneWireNg - - name: Download and install stable Nanopb - run: | - # Download and extract nanopb - wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.8.tar.gz - tar -xf nanopb-0.4.8.tar.gz - # Copy files to WipperSnapper's src/nanopb directory - cp nanopb/pb_common.* nanopb/pb_encode.* nanopb/pb_decode.* src/nanopb - mv nanopb/pb.h src/nanopb/nanopb.pb.h - - name: Install Dependencies - run: | - pip3 install esptool - - name: build ESP32 platforms - run: python3 ci/build_platform.py ${{ matrix.arduino-platform }} --build_timeout 48000 - - name: Check artifacts - run: | - ls examples/Wippersnapper_demo/build/* - - name: Rename build artifacts to reflect the platform name - run: | - mv examples/Wippersnapper_demo/build/*/Wippersnapper_demo.ino.bin wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.bin - mv examples/Wippersnapper_demo/build/*/Wippersnapper_demo.ino.elf wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.elf - mv examples/Wippersnapper_demo/build/*/Wippersnapper_demo.ino.map wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.map - mv examples/Wippersnapper_demo/build/*/Wippersnapper_demo.ino.bootloader.bin wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.bootloader.bin - mv examples/Wippersnapper_demo/build/*/Wippersnapper_demo.ino.partitions.bin wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.partitions.bin - - name: Check boot_app0 file existence (esp32 built from core, not-source) - id: check_files - uses: andstor/file-existence-action@v2 - with: - files: "/home/runner/.arduino15/packages/esp32/hardware/esp32/*/tools/partitions/boot_app0.bin" - - name: boot_app0 file from arduino-cli core - if: steps.check_files.outputs.files_exists == 'true' - run: mv /home/runner/.arduino15/packages/esp32/hardware/esp32/*/tools/partitions/boot_app0.bin wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.boot_app0.bin - - name: boot_app0 file from esp32 source bsp - if: steps.check_files.outputs.files_exists == 'false' - run: mv /home/runner/Arduino/hardware/espressif/esp32/tools/partitions/boot_app0.bin wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.boot_app0.bin - - name: Get Board Flash Parameters - id: get_board_json - run: | + - uses: actions/setup-python@v4 + with: + python-version: "3.x" + - uses: actions/checkout@v4 + - name: Get WipperSnapper version + run: | + git fetch --prune --unshallow --tags + git describe --dirty --tags + echo >>$GITHUB_ENV WS_VERSION=$(git describe --dirty --tags) + - uses: actions/checkout@v4 + with: + repository: adafruit/ci-arduino + path: ci + ref: ci-wippersnapper + - name: Checkout Board Definitions + uses: actions/checkout@v4 + with: + repository: adafruit/Wippersnapper_Boards + path: ws-boards + - name: Install CI-Arduino + run: bash ci/actions_install.sh + - name: Install extra Arduino libraries + run: | + git clone --quiet https://github.com/milesburton/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library + git clone --quiet https://github.com/pstolarz/OneWireNg.git /home/runner/Arduino/libraries/OneWireNg + - name: Download and install stable Nanopb + run: | + # Download and extract nanopb + wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.8.tar.gz + tar -xf nanopb-0.4.8.tar.gz + # Copy files to WipperSnapper's src/nanopb directory + cp nanopb/pb_common.* nanopb/pb_encode.* nanopb/pb_decode.* src/nanopb + mv nanopb/pb.h src/nanopb/nanopb.pb.h + - name: Install Dependencies + run: | + pip3 install esptool + - name: build ESP32 platforms + run: python3 ci/build_platform.py ${{ matrix.arduino-platform }} --build_timeout 48000 + - name: Check artifacts + run: | + ls examples/Wippersnapper_demo/build/* + - name: Rename build artifacts to reflect the platform name + run: | + mv examples/Wippersnapper_demo/build/*/Wippersnapper_demo.ino.bin wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.bin + mv examples/Wippersnapper_demo/build/*/Wippersnapper_demo.ino.elf wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.elf + mv examples/Wippersnapper_demo/build/*/Wippersnapper_demo.ino.map wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.map + mv examples/Wippersnapper_demo/build/*/Wippersnapper_demo.ino.bootloader.bin wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.bootloader.bin + mv examples/Wippersnapper_demo/build/*/Wippersnapper_demo.ino.partitions.bin wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.partitions.bin + - name: Check boot_app0 file existence (esp32 built from core, not-source) + id: check_files + uses: andstor/file-existence-action@v2 + with: + files: "/home/runner/.arduino15/packages/esp32/hardware/esp32/*/tools/partitions/boot_app0.bin" + - name: boot_app0 file from arduino-cli core + if: steps.check_files.outputs.files_exists == 'true' + run: mv /home/runner/.arduino15/packages/esp32/hardware/esp32/*/tools/partitions/boot_app0.bin wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.boot_app0.bin + - name: boot_app0 file from esp32 source bsp + if: steps.check_files.outputs.files_exists == 'false' + run: mv /home/runner/Arduino/hardware/espressif/esp32/tools/partitions/boot_app0.bin wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.boot_app0.bin + - name: Get Board Flash Parameters + id: get_board_json + run: | board_name=${{ matrix.arduino-platform }} + # Remove '_noota' suffix if present + board_name=${board_name%_noota} + # Remove 'wippersnapper_' prefix if present + board_name=${board_name#wippersnapper_} content=$(cat ws-boards/boards/${board_name//_/-}/definition.json) { echo 'boardJson<> "$GITHUB_OUTPUT" - - name: Create combined binary using Esptool merge_bin - run: | + - name: Create combined binary using Esptool merge_bin + run: | echo ${{ steps.get_board_json.outputs.boardJson }} echo ${{ fromJson(steps.get_board_json.outputs.boardJson) }} python3 -m esptool --chip ${{fromJson(steps.get_board_json.outputs.boardJson).esptool.chip}} merge_bin \ @@ -177,14 +339,14 @@ jobs: 0x8000 wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.partitions.bin \ 0xe000 wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.boot_app0.bin \ 0x10000 wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.bin - - name: Zip build artifacts - run: | - zip -r wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.zip wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.* - - name: upload build artifacts zip - uses: actions/upload-artifact@v3 - with: - name: build-files - path: | + - name: Zip build artifacts + run: | + zip -r wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.zip wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.* + - name: upload build artifacts zip + uses: actions/upload-artifact@v3 + with: + name: build-files + path: | wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.zip build-samd: @@ -193,48 +355,54 @@ jobs: strategy: fail-fast: false matrix: - arduino-platform: ["pyportal_tinyusb", "pyportal_titano_tinyusb", "metro_m4_airliftlite_tinyusb"] + arduino-platform: + [ + "pyportal_tinyusb", + "pyportal_titano_tinyusb", + "metro_m4_airliftlite_tinyusb", + ] steps: - - uses: actions/setup-python@v4 - with: - python-version: '3.x' - - uses: actions/checkout@v4 - - name: Get WipperSnapper version - run: | - git fetch --prune --unshallow --tags - git describe --dirty --tags - echo >>$GITHUB_ENV WS_VERSION=$(git describe --dirty --tags) - - uses: actions/checkout@v4 - with: - repository: adafruit/ci-arduino - path: ci - - name: Install CI-Arduino - run: bash ci/actions_install.sh - # manually install Adafruit WiFiNINA library fork - - name: Install extra Arduino libraries - run: | - git clone --quiet https://github.com/adafruit/WiFiNINA.git /home/runner/Arduino/libraries/WiFiNINA - git clone --quiet https://github.com/milesburton/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library - git clone --quiet https://github.com/PaulStoffregen/OneWire.git /home/runner/Arduino/libraries/OneWire - - name: Download and install stable Nanopb - run: | - # Download and extract nanopb - wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.8.tar.gz - tar -xf nanopb-0.4.8.tar.gz - # Copy files to WipperSnapper's src/nanopb directory - cp nanopb/pb_common.* nanopb/pb_encode.* nanopb/pb_decode.* src/nanopb - mv nanopb/pb.h src/nanopb/nanopb.pb.h - - name: build platforms - run: python3 ci/build_platform.py ${{ matrix.arduino-platform }} --build_timeout 48000 - - name: Rename build artifacts to reflect the platform name - run: | - mv examples/*/build/*/Wippersnapper_demo.ino.uf2 wippersnapper.${{ matrix.arduino-platform }}.${{ env.WS_VERSION }}.uf2 - mv examples/*/build/*/Wippersnapper_demo.ino.hex wippersnapper.${{ matrix.arduino-platform }}.${{ env.WS_VERSION }}.hex - - name: upload build artifacts - uses: actions/upload-artifact@v3 - with: - name: build-files - path: | + - uses: actions/setup-python@v4 + with: + python-version: "3.x" + - uses: actions/checkout@v4 + - name: Get WipperSnapper version + run: | + git fetch --prune --unshallow --tags + git describe --dirty --tags + echo >>$GITHUB_ENV WS_VERSION=$(git describe --dirty --tags) + - uses: actions/checkout@v4 + with: + repository: adafruit/ci-arduino + path: ci + - name: Install CI-Arduino + run: bash ci/actions_install.sh + # manually install Adafruit WiFiNINA library fork + - name: Install extra Arduino libraries + run: | + git clone --quiet https://github.com/adafruit/WiFiNINA.git /home/runner/Arduino/libraries/WiFiNINA + git clone --quiet https://github.com/milesburton/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library + git clone --quiet https://github.com/PaulStoffregen/OneWire.git /home/runner/Arduino/libraries/OneWire + git clone --quiet https://github.com/adafruit/Adafruit_TinyUSB_Arduino /home/runner/Arduino/libraries/Adafruit_TinyUSB_Arduino + - name: Download and install stable Nanopb + run: | + # Download and extract nanopb + wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.8.tar.gz + tar -xf nanopb-0.4.8.tar.gz + # Copy files to WipperSnapper's src/nanopb directory + cp nanopb/pb_common.* nanopb/pb_encode.* nanopb/pb_decode.* src/nanopb + mv nanopb/pb.h src/nanopb/nanopb.pb.h + - name: build platforms + run: python3 ci/build_platform.py ${{ matrix.arduino-platform }} --build_timeout 48000 + - name: Rename build artifacts to reflect the platform name + run: | + mv examples/*/build/*/Wippersnapper_demo.ino.uf2 wippersnapper.${{ matrix.arduino-platform }}.${{ env.WS_VERSION }}.uf2 + mv examples/*/build/*/Wippersnapper_demo.ino.hex wippersnapper.${{ matrix.arduino-platform }}.${{ env.WS_VERSION }}.hex + - name: upload build artifacts + uses: actions/upload-artifact@v3 + with: + name: build-files + path: | wippersnapper.${{ matrix.arduino-platform }}.${{ env.WS_VERSION }}.uf2 wippersnapper.${{ matrix.arduino-platform }}.${{ env.WS_VERSION }}.hex @@ -246,47 +414,47 @@ jobs: matrix: arduino-platform: ["picow_rp2040_tinyusb"] steps: - - uses: actions/setup-python@v4 - with: - python-version: '3.x' - - uses: actions/checkout@v4 - - name: Get WipperSnapper version - run: | - git fetch --prune --unshallow --tags - git describe --dirty --tags - echo >>$GITHUB_ENV WS_VERSION=$(git describe --dirty --tags) - - uses: actions/checkout@v4 - with: - repository: adafruit/ci-arduino - path: ci - - name: Install CI-Arduino - run: bash ci/actions_install.sh - # manually install OneWireNG/TempControlLib for OneWireNg (RP2040 Supported OneWire w/backwards compat.) - - name: Install extra Arduino libraries - run: | - git clone --quiet https://github.com/pstolarz/OneWireNg.git /home/runner/Arduino/libraries/OneWireNg - git clone --quiet https://github.com/pstolarz/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library - - name: Download and install stable Nanopb - run: | - # Download and extract nanopb - wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.8.tar.gz - tar -xf nanopb-0.4.8.tar.gz - # Copy files to WipperSnapper's src/nanopb directory - cp nanopb/pb_common.* nanopb/pb_encode.* nanopb/pb_decode.* src/nanopb - mv nanopb/pb.h src/nanopb/nanopb.pb.h - - name: build platforms - run: python3 ci/build_platform.py ${{ matrix.arduino-platform }} --build_timeout 48000 - - name: Rename build artifacts to reflect the platform name - run: | - mv examples/*/build/*/Wippersnapper_demo.ino.uf2 wippersnapper.${{ matrix.arduino-platform }}.${{ env.WS_VERSION }}.uf2 - - name: upload build artifacts - uses: actions/upload-artifact@v3 - with: - name: build-files - path: | + - uses: actions/setup-python@v4 + with: + python-version: "3.x" + - uses: actions/checkout@v4 + - name: Get WipperSnapper version + run: | + git fetch --prune --unshallow --tags + git describe --dirty --tags + echo >>$GITHUB_ENV WS_VERSION=$(git describe --dirty --tags) + - uses: actions/checkout@v4 + with: + repository: adafruit/ci-arduino + path: ci + - name: Install CI-Arduino + run: bash ci/actions_install.sh + # manually install OneWireNG/TempControlLib for OneWireNg (RP2040 Supported OneWire w/backwards compat.) + - name: Install extra Arduino libraries + run: | + git clone --quiet https://github.com/pstolarz/OneWireNg.git /home/runner/Arduino/libraries/OneWireNg + git clone --quiet https://github.com/pstolarz/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library + git clone --quiet https://github.com/adafruit/Adafruit_TinyUSB_Arduino /home/runner/Arduino/libraries/Adafruit_TinyUSB_Arduino + - name: Download and install stable Nanopb + run: | + # Download and extract nanopb + wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.8.tar.gz + tar -xf nanopb-0.4.8.tar.gz + # Copy files to WipperSnapper's src/nanopb directory + cp nanopb/pb_common.* nanopb/pb_encode.* nanopb/pb_decode.* src/nanopb + mv nanopb/pb.h src/nanopb/nanopb.pb.h + - name: build platforms + run: python3 ci/build_platform.py ${{ matrix.arduino-platform }} --build_timeout 48000 + - name: Rename build artifacts to reflect the platform name + run: | + mv examples/*/build/*/Wippersnapper_demo.ino.uf2 wippersnapper.${{ matrix.arduino-platform }}.${{ env.WS_VERSION }}.uf2 + - name: upload build artifacts + uses: actions/upload-artifact@v3 + with: + name: build-files + path: | wippersnapper.${{ matrix.arduino-platform }}.${{ env.WS_VERSION }}.uf2 - # NOTE: This does NOT release artifacts, it only builds build-samd-non-fs: name: Build WipperSnapper SAMD (NO-TINYUSB) @@ -296,37 +464,37 @@ jobs: matrix: arduino-platform: ["mkrwifi1010", "nano_33_iot"] steps: - - uses: actions/setup-python@v4 - with: - python-version: '3.x' - - uses: actions/checkout@v4 - - name: Get WipperSnapper version - run: | - git fetch --prune --unshallow --tags - git describe --dirty --tags - echo >>$GITHUB_ENV WS_VERSION=$(git describe --dirty --tags) - - uses: actions/checkout@v4 - with: - repository: adafruit/ci-arduino - path: ci - - name: Install CI-Arduino - run: bash ci/actions_install.sh - - name: Install extra Arduino libraries - run: | - git clone --quiet https://github.com/arduino-libraries/WiFiNINA.git /home/runner/Arduino/libraries/WiFiNINA - git clone --quiet https://github.com/arduino-libraries/Servo.git /home/runner/Arduino/libraries/Servo - git clone --quiet https://github.com/milesburton/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library - git clone --quiet https://github.com/PaulStoffregen/OneWire.git /home/runner/Arduino/libraries/OneWire - - name: Download and install stable Nanopb - run: | + - uses: actions/setup-python@v4 + with: + python-version: "3.x" + - uses: actions/checkout@v4 + - name: Get WipperSnapper version + run: | + git fetch --prune --unshallow --tags + git describe --dirty --tags + echo >>$GITHUB_ENV WS_VERSION=$(git describe --dirty --tags) + - uses: actions/checkout@v4 + with: + repository: adafruit/ci-arduino + path: ci + - name: Install CI-Arduino + run: bash ci/actions_install.sh + - name: Install extra Arduino libraries + run: | + git clone --quiet https://github.com/arduino-libraries/WiFiNINA.git /home/runner/Arduino/libraries/WiFiNINA + git clone --quiet https://github.com/arduino-libraries/Servo.git /home/runner/Arduino/libraries/Servo + git clone --quiet https://github.com/milesburton/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library + git clone --quiet https://github.com/PaulStoffregen/OneWire.git /home/runner/Arduino/libraries/OneWire + - name: Download and install stable Nanopb + run: | # Download and extract nanopb wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.8.tar.gz tar -xf nanopb-0.4.8.tar.gz # Copy files to WipperSnapper's src/nanopb directory cp nanopb/pb_common.* nanopb/pb_encode.* nanopb/pb_decode.* src/nanopb mv nanopb/pb.h src/nanopb/nanopb.pb.h - - name: build platforms - run: python3 ci/build_platform.py ${{ matrix.arduino-platform }} --build_timeout 48000 + - name: build platforms + run: python3 ci/build_platform.py ${{ matrix.arduino-platform }} --build_timeout 48000 build-esp8266: name: Build WipperSnapper ESP8266 @@ -336,52 +504,52 @@ jobs: matrix: arduino-platform: ["feather_esp8266"] steps: - - uses: actions/setup-python@v4 - with: - python-version: '3.x' - - uses: actions/checkout@v4 - - name: Get WipperSnapper version - run: | - git fetch --prune --unshallow --tags - git describe --dirty --tags - echo >>$GITHUB_ENV WS_VERSION=$(git describe --dirty --tags) - - uses: actions/checkout@v4 - with: - repository: adafruit/ci-arduino - path: ci - - name: Install CI-Arduino - run: bash ci/actions_install.sh - - name: Install extra Arduino library - run: | - git clone --quiet https://github.com/milesburton/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library - git clone --quiet https://github.com/PaulStoffregen/OneWire.git /home/runner/Arduino/libraries/OneWire - - name: Download and install stable Nanopb - run: | - # Download and extract nanopb - wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.8.tar.gz - tar -xf nanopb-0.4.8.tar.gz - # Copy files to WipperSnapper's src/nanopb directory - cp nanopb/pb_common.* nanopb/pb_encode.* nanopb/pb_decode.* src/nanopb - mv nanopb/pb.h src/nanopb/nanopb.pb.h - - name: build platforms - run: python3 ci/build_platform.py ${{ matrix.arduino-platform }} --build_timeout 48000 - - name: list build artifacts - run: | - ls - ls examples/* - - name: Rename build artifacts to reflect the platform name - run: | - mv examples/*/build/*/Wippersnapper_demo.ino.bin wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.bin - mv examples/*/build/*/Wippersnapper_demo.ino.elf wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.elf - mv examples/*/build/*/Wippersnapper_demo.ino.map wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.map - - name: Zip build artifacts - run: | - zip -r wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.zip wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.* - - name: upload build artifacts zip - uses: actions/upload-artifact@v3 - with: - name: build-files - path: | + - uses: actions/setup-python@v4 + with: + python-version: "3.x" + - uses: actions/checkout@v4 + - name: Get WipperSnapper version + run: | + git fetch --prune --unshallow --tags + git describe --dirty --tags + echo >>$GITHUB_ENV WS_VERSION=$(git describe --dirty --tags) + - uses: actions/checkout@v4 + with: + repository: adafruit/ci-arduino + path: ci + - name: Install CI-Arduino + run: bash ci/actions_install.sh + - name: Install extra Arduino library + run: | + git clone --quiet https://github.com/milesburton/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library + git clone --quiet https://github.com/PaulStoffregen/OneWire.git /home/runner/Arduino/libraries/OneWire + - name: Download and install stable Nanopb + run: | + # Download and extract nanopb + wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.8.tar.gz + tar -xf nanopb-0.4.8.tar.gz + # Copy files to WipperSnapper's src/nanopb directory + cp nanopb/pb_common.* nanopb/pb_encode.* nanopb/pb_decode.* src/nanopb + mv nanopb/pb.h src/nanopb/nanopb.pb.h + - name: build platforms + run: python3 ci/build_platform.py ${{ matrix.arduino-platform }} --build_timeout 48000 + - name: list build artifacts + run: | + ls + ls examples/* + - name: Rename build artifacts to reflect the platform name + run: | + mv examples/*/build/*/Wippersnapper_demo.ino.bin wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.bin + mv examples/*/build/*/Wippersnapper_demo.ino.elf wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.elf + mv examples/*/build/*/Wippersnapper_demo.ino.map wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.map + - name: Zip build artifacts + run: | + zip -r wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.zip wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.* + - name: upload build artifacts zip + uses: actions/upload-artifact@v3 + with: + name: build-files + path: | wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.zip build-esp32sx-dev: @@ -390,90 +558,218 @@ jobs: strategy: fail-fast: false matrix: - arduino-platform: ["feather_esp32s2_debug", "feather_esp32s2_tft_debug", - "feather_esp32s3_debug", "feather_esp32s3_4mbflash_2mbpsram_debug", "feather_esp32s3_tft_debug"] + arduino-platform: + [ + "feather_esp32s2_debug", + "feather_esp32s2_tft_debug", + "feather_esp32s3_debug", + "feather_esp32s3_4mbflash_2mbpsram_debug", + "feather_esp32s3_tft_debug", + ] steps: - - uses: actions/setup-python@v4 - with: - python-version: '3.x' - - uses: actions/checkout@v4 - - name: Get WipperSnapper version - run: | - git fetch --prune --unshallow --tags - git describe --dirty --tags - echo >>$GITHUB_ENV WS_VERSION=$(git describe --dirty --tags) - - uses: actions/checkout@v4 - with: - repository: adafruit/ci-arduino - path: ci - - name: Install CI-Arduino - run: bash ci/actions_install.sh - - name: Install extra Arduino libraries - run: | - git clone --quiet https://github.com/milesburton/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library - git clone --quiet https://github.com/pstolarz/OneWireNg.git /home/runner/Arduino/libraries/OneWireNg - git clone --quiet https://github.com/adafruit/Adafruit_HX8357_Library.git /home/runner/Arduino/libraries/Adafruit_HX8357_Library - git clone --quiet https://github.com/adafruit/Adafruit_ILI9341.git /home/runner/Arduino/libraries/Adafruit_ILI9341 - git clone --quiet https://github.com/adafruit/Adafruit_STMPE610.git /home/runner/Arduino/libraries/Adafruit_STMPE610 - git clone --quiet https://github.com/adafruit/Adafruit-ST7735-Library.git /home/runner/Arduino/libraries/Adafruit-ST7735-Library - git clone --quiet https://github.com/adafruit/Adafruit_TouchScreen.git /home/runner/Arduino/libraries/Adafruit_TouchScreen - git clone --depth 1 --branch wippersnapper https://github.com/brentru/lvgl.git /home/runner/Arduino/libraries/lvgl - git clone --depth 1 --branch development https://github.com/brentru/Adafruit_LvGL_Glue.git /home/runner/Arduino/libraries/Adafruit_LittlevGL_Glue_Library - - name: Download and install stable Nanopb - run: | - # Download and extract nanopb - wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.8.tar.gz - tar -xf nanopb-0.4.8.tar.gz - # Copy files to WipperSnapper's src/nanopb directory - cp nanopb/pb_common.* nanopb/pb_encode.* nanopb/pb_decode.* src/nanopb - mv nanopb/pb.h src/nanopb/nanopb.pb.h - - name: List all files in Adafruit_LittlevGL_Glue_Library folder - run: | - ls /home/runner/Arduino/libraries/Adafruit_LittlevGL_Glue_Library - - name: Copy lv_conf.h file in Adafruit_LittlevGL_Glue_Library to the arduino library folder - run: | - cp /home/runner/Arduino/libraries/Adafruit_LittlevGL_Glue_Library/lv_conf.h /home/runner/Arduino/libraries - - name: Build for ESP32-SX - run: python3 ci/build_platform.py ${{ matrix.arduino-platform }} --build_timeout 48000 - - name: list - run: | - ls - ls examples/*/build/ - - name: Rename build artifacts to reflect the platform name - run: | - mv examples/*/build/*/wippersnapper_debug.ino.uf2 wippersnapper.${{ matrix.arduino-platform }}.${{ env.WS_VERSION }}.uf2 - mv examples/*/build/*/wippersnapper_debug.ino.bin wippersnapper.${{ matrix.arduino-platform }}.${{ env.WS_VERSION }}.bin - - name: upload build artifacts - uses: actions/upload-artifact@v3 - with: - name: build-files-dev - path: | + - uses: actions/setup-python@v4 + with: + python-version: "3.x" + - uses: actions/checkout@v4 + - name: Get WipperSnapper version + run: | + git fetch --prune --unshallow --tags + git describe --dirty --tags + echo >>$GITHUB_ENV WS_VERSION=$(git describe --dirty --tags) + - uses: actions/checkout@v4 + with: + repository: adafruit/ci-arduino + path: ci + ref: ci-wippersnapper + - name: Install CI-Arduino + run: bash ci/actions_install.sh + - name: Install extra Arduino libraries + run: | + git clone --quiet https://github.com/milesburton/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library + git clone --quiet https://github.com/pstolarz/OneWireNg.git /home/runner/Arduino/libraries/OneWireNg + git clone --quiet https://github.com/adafruit/Adafruit_HX8357_Library.git /home/runner/Arduino/libraries/Adafruit_HX8357_Library + git clone --quiet https://github.com/adafruit/Adafruit_ILI9341.git /home/runner/Arduino/libraries/Adafruit_ILI9341 + git clone --quiet https://github.com/adafruit/Adafruit_STMPE610.git /home/runner/Arduino/libraries/Adafruit_STMPE610 + git clone --quiet https://github.com/adafruit/Adafruit-ST7735-Library.git /home/runner/Arduino/libraries/Adafruit-ST7735-Library + git clone --quiet https://github.com/adafruit/Adafruit_TouchScreen.git /home/runner/Arduino/libraries/Adafruit_TouchScreen + git clone --quiet https://github.com/adafruit/Adafruit_TinyUSB_Arduino /home/runner/Arduino/libraries/Adafruit_TinyUSB_Arduino + git clone --depth 1 --branch wippersnapper https://github.com/brentru/lvgl.git /home/runner/Arduino/libraries/lvgl + git clone --depth 1 --branch development https://github.com/brentru/Adafruit_LvGL_Glue.git /home/runner/Arduino/libraries/Adafruit_LittlevGL_Glue_Library + - name: Download and install stable Nanopb + run: | + # Download and extract nanopb + wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.8.tar.gz + tar -xf nanopb-0.4.8.tar.gz + # Copy files to WipperSnapper's src/nanopb directory + cp nanopb/pb_common.* nanopb/pb_encode.* nanopb/pb_decode.* src/nanopb + mv nanopb/pb.h src/nanopb/nanopb.pb.h + - name: List all files in Adafruit_LittlevGL_Glue_Library folder + run: | + ls /home/runner/Arduino/libraries/Adafruit_LittlevGL_Glue_Library + - name: Copy lv_conf.h file in Adafruit_LittlevGL_Glue_Library to the arduino library folder + run: | + cp /home/runner/Arduino/libraries/Adafruit_LittlevGL_Glue_Library/lv_conf.h /home/runner/Arduino/libraries + - name: Build for ESP32-SX + run: python3 ci/build_platform.py ${{ matrix.arduino-platform }} --build_timeout 48000 + - name: list + run: | + ls + ls examples/*/build/ + - name: Rename build artifacts to reflect the platform name + run: | + mv examples/*/build/*/wippersnapper_debug.ino.uf2 wippersnapper.${{ matrix.arduino-platform }}.${{ env.WS_VERSION }}.uf2 + mv examples/*/build/*/wippersnapper_debug.ino.bin wippersnapper.${{ matrix.arduino-platform }}.${{ env.WS_VERSION }}.bin + - name: upload build artifacts + uses: actions/upload-artifact@v3 + with: + name: build-files-dev + path: | wippersnapper.${{ matrix.arduino-platform }}.${{ env.WS_VERSION }}.uf2 wippersnapper.${{ matrix.arduino-platform }}.${{ env.WS_VERSION }}.bin + build-esp32-dev: + name: Build WipperSnapper ESP32/Cx DEV BUILDS + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + arduino-platform: + [ + "wippersnapper_feather_esp32c6_debug", + ] + include: + - offset: "0x1000" + - offset: "0x0" + arduino-platform: "wippersnapper_feather_esp32c6_debug" + steps: + - uses: actions/setup-python@v4 + with: + python-version: "3.x" + - uses: actions/checkout@v4 + - name: Get WipperSnapper version + run: | + git fetch --prune --unshallow --tags + git describe --dirty --tags + echo >>$GITHUB_ENV WS_VERSION=$(git describe --dirty --tags) + - uses: actions/checkout@v4 + with: + repository: adafruit/ci-arduino + path: ci + ref: ci-wippersnapper + - name: Checkout Board Definitions + uses: actions/checkout@v4 + with: + repository: adafruit/Wippersnapper_Boards + path: ws-boards + - name: Install CI-Arduino + run: bash ci/actions_install.sh + - name: Install extra Arduino libraries + run: | + git clone --quiet https://github.com/milesburton/Arduino-Temperature-Control-Library.git /home/runner/Arduino/libraries/Arduino-Temperature-Control-Library + git clone --quiet https://github.com/pstolarz/OneWireNg.git /home/runner/Arduino/libraries/OneWireNg + - name: Download and install stable Nanopb + run: | + # Download and extract nanopb + wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.8.tar.gz + tar -xf nanopb-0.4.8.tar.gz + # Copy files to WipperSnapper's src/nanopb directory + cp nanopb/pb_common.* nanopb/pb_encode.* nanopb/pb_decode.* src/nanopb + mv nanopb/pb.h src/nanopb/nanopb.pb.h + - name: Install Dependencies + run: | + pip3 install esptool + - name: build ESP32 platforms + run: python3 ci/build_platform.py ${{ matrix.arduino-platform }} --build_timeout 48000 + - name: Check artifacts + run: | + ls examples/wippersnapper_debug/build/* + - name: Rename build artifacts to reflect the platform name + run: | + mv examples/wippersnapper_debug/build/*/wippersnapper_debug.ino.bin wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.bin + mv examples/wippersnapper_debug/build/*/wippersnapper_debug.ino.elf wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.elf + mv examples/wippersnapper_debug/build/*/wippersnapper_debug.ino.map wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.map + mv examples/wippersnapper_debug/build/*/wippersnapper_debug.ino.bootloader.bin wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.bootloader.bin + mv examples/wippersnapper_debug/build/*/wippersnapper_debug.ino.partitions.bin wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.partitions.bin + - name: Check boot_app0 file existence (esp32 built from core, not-source) + id: check_files + uses: andstor/file-existence-action@v2 + with: + files: "/home/runner/.arduino15/packages/esp32/hardware/esp32/*/tools/partitions/boot_app0.bin" + - name: boot_app0 file from arduino-cli core + if: steps.check_files.outputs.files_exists == 'true' + run: mv /home/runner/.arduino15/packages/esp32/hardware/esp32/*/tools/partitions/boot_app0.bin wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.boot_app0.bin + - name: boot_app0 file from esp32 source bsp + if: steps.check_files.outputs.files_exists == 'false' + run: mv /home/runner/Arduino/hardware/espressif/esp32/tools/partitions/boot_app0.bin wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.boot_app0.bin + - name: Get Board Flash Parameters + id: get_board_json + run: | + board_name=${{ matrix.arduino-platform }} + # Remove '_noota' suffix if present + board_name=${board_name%_noota} + # Remove '_debug' suffix if present + board_name=${board_name%_debug} + # Remove 'wippersnapper_' prefix if present + board_name=${board_name#wippersnapper_} + content=$(cat ws-boards/boards/${board_name//_/-}/definition.json) + { + echo 'boardJson<> "$GITHUB_OUTPUT" + - name: Create combined binary using Esptool merge_bin + run: | + echo ${{ steps.get_board_json.outputs.boardJson }} + echo ${{ fromJson(steps.get_board_json.outputs.boardJson) }} + python3 -m esptool --chip ${{fromJson(steps.get_board_json.outputs.boardJson).esptool.chip}} merge_bin \ + --flash_mode ${{fromJson(steps.get_board_json.outputs.boardJson).esptool.flashMode}} \ + --flash_freq ${{fromJson(steps.get_board_json.outputs.boardJson).esptool.flashFreq}} \ + --flash_size ${{fromJson(steps.get_board_json.outputs.boardJson).esptool.flashSize}} \ + -o wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.combined.bin \ + ${{ matrix.offset }} wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.bootloader.bin \ + 0x8000 wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.partitions.bin \ + 0xe000 wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.boot_app0.bin \ + 0x10000 wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.bin + - name: Zip build artifacts + run: | + zip -r wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.zip wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.* + - name: upload build artifacts zip + uses: actions/upload-artifact@v3 + with: + name: build-files-dev + path: | + wippersnapper.${{ matrix.arduino-platform }}.littlefs.${{ env.WS_VERSION }}.zip clang_and_doxy: runs-on: ubuntu-latest - needs: [build-samd, build-esp32, build-esp32sx, build-esp8266, build-samd-non-fs, build-rp2040] + needs: + [ + build-samd, + build-esp32, + build-esp32sx, + build-esp8266, + build-samd-non-fs, + build-rp2040, + ] steps: - - uses: actions/setup-python@v4 - with: - python-version: '3.x' - - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + with: + python-version: "3.x" + - uses: actions/checkout@v4 - - uses: actions/checkout@v4 - with: - repository: adafruit/ci-arduino - path: ci - - name: pre-install - run: bash ci/actions_install.sh + - uses: actions/checkout@v4 + with: + repository: adafruit/ci-arduino + path: ci + - name: pre-install + run: bash ci/actions_install.sh - - name: clang - run: python3 ci/run-clang-format.py -r -e "ci/*" -e "bin/*" -e src/nanopb -e src/wippersnapper -e src/pb.h -e src/provisioning/tinyusb src/ + - name: clang + run: python3 ci/run-clang-format.py -r -e "ci/*" -e "bin/*" -e src/nanopb -e src/wippersnapper -e src/pb.h -e src/provisioning/tinyusb src/ - - name: doxygen - env: - GH_REPO_TOKEN: ${{ secrets.GH_REPO_TOKEN }} - PRETTYNAME : "Adafruit.io WipperSnapper Library" - run: bash ci/doxy_gen_and_deploy.sh \ No newline at end of file + - name: doxygen + env: + GH_REPO_TOKEN: ${{ secrets.GH_REPO_TOKEN }} + PRETTYNAME: "Adafruit.io WipperSnapper Library" + run: bash ci/doxy_gen_and_deploy.sh diff --git a/.github/workflows/release-callee.yml b/.github/workflows/release-callee.yml index a5b9956a2..eb960d18b 100644 --- a/.github/workflows/release-callee.yml +++ b/.github/workflows/release-callee.yml @@ -12,13 +12,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Download build artifacts from build-platform steps - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v3 with: name: build-files - name: List Files run: ls - name: Upload Assets to the GitHub Release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 if: startsWith(github.ref, 'refs/tags/') with: files: | diff --git a/.gitignore b/.gitignore index 4ab1f2a5d..b22a8d404 100644 --- a/.gitignore +++ b/.gitignore @@ -44,5 +44,8 @@ src/.vscode/settings.json examples/Wippersnapper_demo/build/ -#Platformio artifacts +# Platformio artifacts .pio/ + +# Secrets +data/ \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 6bad5c070..65076780b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,6 +3,7 @@ "limits": "c", "type_traits": "c" }, - "C_Cpp.dimInactiveRegions": false, - "dotnet.defaultSolution": "disable" + "C_Cpp.dimInactiveRegions": true, + "dotnet.defaultSolution": "disable", + "cmake.configureOnOpen": false } \ No newline at end of file diff --git a/examples/Wippersnapper_NoFS/.wippersnapper_feather_esp32.test.skip b/examples/Wippersnapper_NoFS/.wippersnapper_feather_esp32.test.skip new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/examples/Wippersnapper_NoFS/.wippersnapper_feather_esp32.test.skip @@ -0,0 +1 @@ + diff --git a/examples/Wippersnapper_NoFS/.wippersnapper_feather_esp32c6.test.skip b/examples/Wippersnapper_NoFS/.wippersnapper_feather_esp32c6.test.skip new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/examples/Wippersnapper_NoFS/.wippersnapper_feather_esp32c6.test.skip @@ -0,0 +1 @@ + diff --git a/examples/Wippersnapper_NoFS/.wippersnapper_feather_esp32c6_debug.test.skip b/examples/Wippersnapper_NoFS/.wippersnapper_feather_esp32c6_debug.test.skip new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/examples/Wippersnapper_NoFS/.wippersnapper_feather_esp32c6_debug.test.skip @@ -0,0 +1 @@ + diff --git a/examples/Wippersnapper_NoFS/.wippersnapper_qtpy_esp32c3.test.skip b/examples/Wippersnapper_NoFS/.wippersnapper_qtpy_esp32c3.test.skip new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/examples/Wippersnapper_NoFS/.wippersnapper_qtpy_esp32c3.test.skip @@ -0,0 +1 @@ + diff --git a/examples/Wippersnapper_demo/.wippersnapper_feather_esp32.generate b/examples/Wippersnapper_demo/.wippersnapper_feather_esp32.generate new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/examples/Wippersnapper_demo/.wippersnapper_feather_esp32.generate @@ -0,0 +1 @@ + diff --git a/examples/Wippersnapper_demo/.wippersnapper_feather_esp32c6.generate b/examples/Wippersnapper_demo/.wippersnapper_feather_esp32c6.generate new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/examples/Wippersnapper_demo/.wippersnapper_feather_esp32c6.generate @@ -0,0 +1 @@ + diff --git a/examples/Wippersnapper_demo/.wippersnapper_feather_esp32c6_debug.test.skip b/examples/Wippersnapper_demo/.wippersnapper_feather_esp32c6_debug.test.skip new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/examples/Wippersnapper_demo/.wippersnapper_feather_esp32c6_debug.test.skip @@ -0,0 +1 @@ + diff --git a/examples/Wippersnapper_demo/.wippersnapper_qtpy_esp32c3.generate b/examples/Wippersnapper_demo/.wippersnapper_qtpy_esp32c3.generate new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/examples/Wippersnapper_demo/.wippersnapper_qtpy_esp32c3.generate @@ -0,0 +1 @@ + diff --git a/examples/wippersnapper_debug/.wippersnapper_feather_esp32.test.skip b/examples/wippersnapper_debug/.wippersnapper_feather_esp32.test.skip new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/examples/wippersnapper_debug/.wippersnapper_feather_esp32.test.skip @@ -0,0 +1 @@ + diff --git a/examples/wippersnapper_debug/.wippersnapper_feather_esp32c6.test.skip b/examples/wippersnapper_debug/.wippersnapper_feather_esp32c6.test.skip new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/examples/wippersnapper_debug/.wippersnapper_feather_esp32c6.test.skip @@ -0,0 +1 @@ + diff --git a/examples/wippersnapper_debug/.wippersnapper_feather_esp32c6_debug.generate b/examples/wippersnapper_debug/.wippersnapper_feather_esp32c6_debug.generate new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/examples/wippersnapper_debug/.wippersnapper_feather_esp32c6_debug.generate @@ -0,0 +1 @@ + diff --git a/examples/wippersnapper_debug/.wippersnapper_qtpy_esp32c3.test.skip b/examples/wippersnapper_debug/.wippersnapper_qtpy_esp32c3.test.skip new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/examples/wippersnapper_debug/.wippersnapper_qtpy_esp32c3.test.skip @@ -0,0 +1 @@ + diff --git a/library.properties b/library.properties index b95287a64..2ac2bda95 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=Adafruit WipperSnapper -version=1.0.0-alpha.79 +version=1.0.0-beta.90 author=Adafruit maintainer=Adafruit sentence=Arduino application for Adafruit.io WipperSnapper @@ -7,4 +7,4 @@ paragraph=Arduino application for Adafruit.io WipperSnapper category=Communication url=https://github.com/adafruit/Adafruit_Wippersnapper_Arduino architectures=* -depends=Adafruit NeoPixel, Adafruit SPIFlash, ArduinoJson, Adafruit DotStar, Adafruit INA219, Adafruit LTR329 and LTR303, Adafruit LTR390 Library, Adafruit SleepyDog Library, Adafruit TMP117, Adafruit TinyUSB Library, Adafruit AHTX0, Adafruit BME280 Library, Adafruit BMP280 Library, Adafruit BMP3XX Library, Adafruit DPS310, Adafruit SCD30, Adafruit SGP30 Sensor, Adafruit SGP40 Sensor, Sensirion I2C SCD4x, Sensirion I2C SEN5X, arduino-sht, Adafruit Si7021 Library, Adafruit MQTT Library, Adafruit MS8607, Adafruit MCP9808 Library, Adafruit MCP9600 Library, Adafruit MPL115A2, Adafruit MPRLS Library, Adafruit TSL2591 Library, Adafruit_VL53L0X, Adafruit VL53L1X, Adafruit_VL6180X, Adafruit PM25 AQI Sensor, Adafruit VCNL4020 Library, Adafruit VCNL4040, Adafruit VEML7700 Library, Adafruit LC709203F, Adafruit LPS2X, Adafruit LPS35HW, Adafruit seesaw Library, Adafruit BME680 Library, Adafruit MAX1704X, Adafruit ADT7410 Library, Adafruit HTS221, Adafruit HTU21DF Library, Adafruit HTU31D Library, Adafruit PCT2075, hp_BH1750, ENS160 - Adafruit Fork +depends=Adafruit NeoPixel, Adafruit SPIFlash, ArduinoJson, Adafruit DotStar, Adafruit HDC302x, Adafruit INA219, Adafruit LTR329 and LTR303, Adafruit LTR390 Library, Adafruit MCP3421, Adafruit NAU7802 Library, Adafruit SleepyDog Library, Adafruit TMP117, Adafruit TinyUSB Library, Adafruit AHTX0, Adafruit BME280 Library, Adafruit BMP280 Library, Adafruit BMP3XX Library, Adafruit DPS310, Adafruit DS248x, Adafruit SCD30, Adafruit SGP30 Sensor, Adafruit SGP40 Sensor, Sensirion I2C SCD4x, Sensirion I2C SEN5X, arduino-sht, Adafruit Si7021 Library, Adafruit MQTT Library, Adafruit MS8607, Adafruit MCP9808 Library, Adafruit MCP9600 Library, Adafruit MPL115A2, Adafruit MPRLS Library, Adafruit TSL2591 Library, Adafruit_VL53L0X, Adafruit VL53L1X, STM32duino VL53L4CD, STM32duino VL53L4CX, Adafruit_VL6180X, Adafruit PM25 AQI Sensor, Adafruit VCNL4020 Library, Adafruit VCNL4040, Adafruit VEML7700 Library, Adafruit LC709203F, Adafruit LPS2X, Adafruit LPS35HW, Adafruit seesaw Library, Adafruit BME680 Library, Adafruit MAX1704X, Adafruit ADT7410 Library, Adafruit HTS221, Adafruit HTU21DF Library, Adafruit HTU31D Library, Adafruit PCT2075, hp_BH1750, ENS160 - Adafruit Fork diff --git a/platformio.ini b/platformio.ini index 2918aad85..797f358b5 100644 --- a/platformio.ini +++ b/platformio.ini @@ -17,7 +17,7 @@ monitor_speed = 115200 lib_compat_mode = strict lib_deps = adafruit/Adafruit Zero DMA Library - adafruit/Adafruit TinyUSB Library + https://github.com/adafruit/Adafruit_TinyUSB_Arduino.git adafruit/Adafruit NeoPixel adafruit/Adafruit SPIFlash adafruit/Adafruit DotStar @@ -28,7 +28,9 @@ lib_deps = adafruit/Adafruit BMP280 Library adafruit/Adafruit BMP3XX Library adafruit/Adafruit DPS310 + adafruit/Adafruit DS248x adafruit/Adafruit INA219 + adafruit/Adafruit HDC302x adafruit/Adafruit HTS221 adafruit/Adafruit HTU21DF Library adafruit/Adafruit HTU31D Library @@ -41,15 +43,19 @@ lib_deps = adafruit/Adafruit Si7021 Library adafruit/Adafruit VCNL4020 Library adafruit/Adafruit VCNL4040 + adafruit/Adafruit MCP3421 adafruit/Adafruit MCP9808 Library adafruit/Adafruit MCP9600 Library adafruit/Adafruit MPL115A2 adafruit/Adafruit MPRLS Library adafruit/Adafruit MS8607 + adafruit/Adafruit NAU7802 Library adafruit/Adafruit TMP117 adafruit/Adafruit TSL2591 Library adafruit/Adafruit_VL53L0X adafruit/Adafruit VL53L1X + stm32duino/STM32duino VL53L4CD + stm32duino/STM32duino VL53L4CX adafruit/Adafruit_VL6180X adafruit/Adafruit PM25 AQI Sensor adafruit/Adafruit VEML7700 Library @@ -65,7 +71,7 @@ lib_deps = adafruit/Adafruit TouchScreen adafruit/Adafruit MQTT Library bblanchon/ArduinoJson - PaulStoffregen/OneWire + https://github.com/pstolarz/OneWireNg.git https://github.com/milesburton/Arduino-Temperature-Control-Library.git https://github.com/Sensirion/arduino-sht.git https://github.com/Sensirion/arduino-i2c-scd4x.git @@ -73,22 +79,29 @@ lib_deps = https://github.com/adafruit/WiFiNINA.git https://github.com/Starmbi/hp_BH1750.git + ; Common build environment for ESP32 platform [common:esp32] -platform = espressif32 @ ^6.3.2 -lib_ignore = WiFiNINA +; platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.04/platform-espressif32.zip +; This is needed for Adafruit Feather C6 in platformio (until 51.03.05): +platform = https://github.com/pioarduino/platform-espressif32#develop +lib_ignore = WiFiNINA, WiFi101, OneWire monitor_filters = esp32_exception_decoder, time ; Common build environment for ESP8266 platform [common:esp8266] platform = espressif8266 -lib_ignore = WiFiNINA, Adafruit TinyUSB Library +lib_ignore = WiFiNINA, WiFi101, Adafruit TinyUSB Library, OneWire ; Common build environment for Atmel/Microchip SAMDx platform [common:atsamd] platform = atmelsam +platform_packages = + platformio/framework-arduino-samd-adafruit@^1.7.13 + platformio/tool-jlink@^1.78811.0 lib_ldf_mode = deep - +lib_archive = no ; debug timer issues see https://community.platformio.org/t/choose-usb-stack-as-tiny-usb/22451/5 +lib_ignore = OneWire [common:rp2040] platform = https://github.com/maxgerhardt/platform-raspberrypi.git @@ -101,9 +114,12 @@ board_build.core = earlephilhower board_build.filesystem_size = 0.5m build_flags = -DUSE_TINYUSB ; Once https://github.com/platformio/platformio-core > 6.1.11 these can be removed -lib_ignore = WiFiNINA, Adafruit Zero DMA Library +lib_ignore = WiFiNINA, WiFi101, Adafruit Zero DMA Library, OneWire lib_compat_mode = soft ; can be strict once pio detects SleepyDog on RP2040 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Individual Board Definitions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ; ESP32-x Boards ; ; Adafruit ESP32 Feather @@ -112,6 +128,7 @@ extends = common:esp32 board = featheresp32 build_flags = -DARDUINO_FEATHER_ESP32 board_build.filesystem = littlefs +board_build.partitions = min_spiffs.csv ; Adafruit ESP32 Feather V2 @@ -129,33 +146,67 @@ board = adafruit_itsybitsy_esp32 build_flags = -DARDUINO_ADAFRUIT_ITSYBITSY_ESP32 board_build.filesystem = littlefs +; Adafruit ESP32 Feather C6 +[env:adafruit_feather_esp32c6_4mbflash_nopsram] +extends = common:esp32 +board = adafruit_feather_esp32c6 +build_flags = + -DARDUINO_ADAFRUIT_FEATHER_ESP32C6 + -DARDUINO_USB_CDC_ON_BOOT=1 + -DCORE_DEBUG_LEVEL=3 +board_build.filesystem = littlefs +board_build.partitions = min_spiffs.csv + +; Espressif ESP32-C6 4MB NO PSRAM esp32-c6-devkitm-1 +[env:espressif_esp32-c6-devkitm-1] +extends = common:esp32 +board = esp32-c6-devkitm-1 +build_type = debug +build_flags = + -DARDUINO_ESPRESSIF_ESP32C6_DEVKITM_1 + -DARDUINO_ADAFRUIT_FEATHER_ESP32C6 + -DNDEBUG=1 + -DDEBUG=1 + -DESP_LOG_LEVEL=5 + -DARDUINO_CORE_DEBUG_LEVEL=5 + -DARDUINO_DEBUG_LEVEL=5 + -DARDUINO_LOG_LEVEL=5 + -DCORE_DEBUG_LEVEL=5 + -DARDUHAL_LOG_LEVEL=5 +board_build.filesystem = littlefs +board_build.partitions = min_spiffs.csv ; Adafruit Feather ESP32-S2 [env:featheresp32s2] extends = common:esp32 board = featheresp32-s2 -build_flags = -DARDUINO_ADAFRUIT_FEATHER_ESP32S2 +build_flags = -DARDUINO_ADAFRUIT_FEATHER_ESP32S2 -DBOARD_HAS_PSRAM +board_build.partitions = tinyuf2-partitions-4MB.csv extra_scripts = pre:rename_usb_config.py ; Adafruit Feather ESP32-S2 TFT [env:adafruit_feather_esp32s2_tft] extends = common:esp32 board = adafruit_feather_esp32s2_tft -build_flags = -DARDUINO_ADAFRUIT_FEATHER_ESP32S2_TFT +build_flags = -DARDUINO_ADAFRUIT_FEATHER_ESP32S2_TFT -DBOARD_HAS_PSRAM +board_build.partitions = tinyuf2-partitions-4MB.csv extra_scripts = pre:rename_usb_config.py ; Adafruit Feather ESP32-S2 Reverse TFT [env:adafruit_feather_esp32s2_reversetft] extends = common:esp32 board = adafruit_feather_esp32s2_reversetft -build_flags = -DARDUINO_ADAFRUIT_FEATHER_ESP32S2_REVTFT +build_flags = -DARDUINO_ADAFRUIT_FEATHER_ESP32S2_REVTFT -DBOARD_HAS_PSRAM +board_build.partitions = tinyuf2-partitions-4MB.csv extra_scripts = pre:rename_usb_config.py ; Adafruit Feather ESP32-S3 2MB PSRAM [env:adafruit_feather_esp32s3] extends = common:esp32 board = adafruit_feather_esp32s3 -build_flags = -DARDUINO_ADAFRUIT_FEATHER_ESP32S3 +build_flags = -DARDUINO_ADAFRUIT_FEATHER_ESP32S3 -DBOARD_HAS_PSRAM +;set partition to tinyuf2-partitions-4MB.csv as of idf 5.1 +board_build.partitions = tinyuf2-partitions-4MB.csv extra_scripts = pre:rename_usb_config.py ; Adafruit Feather ESP32-S3 NO PSRAM @@ -168,35 +219,48 @@ extra_scripts = pre:rename_usb_config.py ; Adafruit Feather ESP32-S3 TFT [env:adafruit_feather_esp32s3_tft] extends = common:esp32 +build_type = debug +debug_tool = esp-builtin board = adafruit_feather_esp32s3_tft -build_flags = -DARDUINO_ADAFRUIT_FEATHER_ESP32S3_TFT +build_flags = -DARDUINO_ADAFRUIT_FEATHER_ESP32S3_TFT -DBOARD_HAS_PSRAM +;set partition to tinyuf2-partitions-4MB.csv as of idf 5.1 +board_build.partitions = tinyuf2-partitions-4MB.csv extra_scripts = pre:rename_usb_config.py ; Adafruit Feather ESP32-S3 Reverse TFT [env:adafruit_feather_esp32s3_reversetft] extends = common:esp32 board = adafruit_feather_esp32s3_reversetft -build_flags = -DARDUINO_ADAFRUIT_FEATHER_ESP32S3_REVTFT +build_flags = -DARDUINO_ADAFRUIT_FEATHER_ESP32S3_REVTFT -DBOARD_HAS_PSRAM +;set partition to tinyuf2-partitions-4MB.csv as of idf 5.1 +board_build.partitions = tinyuf2-partitions-4MB.csv extra_scripts = pre:rename_usb_config.py ; Adafruit Magtag ESP32-S2 [env:adafruit_magtag29_esp32s2] extends = common:esp32 board = adafruit_magtag29_esp32s2 -build_flags = -DARDUINO_MAGTAG29_ESP32S2 +build_flags = -DARDUINO_MAGTAG29_ESP32S2 -DBOARD_HAS_PSRAM +;set partition to tinyuf2-partitions-4MB.csv as of idf 5.1 +board_build.partitions = tinyuf2-partitions-4MB.csv extra_scripts = pre:rename_usb_config.py ; Adafruit Metro ESP32-S2 [env:adafruit_metro_esp32s2] extends = common:esp32 board = adafruit_metro_esp32s2 -build_flags = -DARDUINO_METRO_ESP32S2 +build_flags = -DARDUINO_METRO_ESP32S2 -DBOARD_HAS_PSRAM +;set partition to tinyuf2-partitions-4MB.csv as of idf 5.1 +board_build.partitions = tinyuf2-partitions-4MB.csv extra_scripts = pre:rename_usb_config.py ; Adafruit QT Py ESP32 Pico [env:adafruit_qtpy_esp32] extends = common:esp32 board = adafruit_qtpy_esp32 +board_build.partitions = default_8MB.csv +board_build.filesystem = littlefs +build_type = debug build_flags = -DARDUINO_ADAFRUIT_QTPY_ESP32 ; Adafruit QT Py ESP32-C3 @@ -204,12 +268,16 @@ build_flags = -DARDUINO_ADAFRUIT_QTPY_ESP32 extends = common:esp32 board = adafruit_qtpy_esp32c3 build_flags = -DARDUINO_ADAFRUIT_QTPY_ESP32C3 +board_build.filesystem = littlefs +board_build.partitions = min_spiffs.csv ; Adafruit QT Py ESP32-S2 [env:adafruit_qtpy_esp32s2] extends = common:esp32 board = adafruit_qtpy_esp32s2 -build_flags = -DARDUINO_ADAFRUIT_QTPY_ESP32S2 +build_flags = -DARDUINO_ADAFRUIT_QTPY_ESP32S2 -DBOARD_HAS_PSRAM +;set partition to tinyuf2-partitions-4MB.csv as of idf 5.1 +board_build.partitions = tinyuf2-partitions-4MB.csv extra_scripts = pre:rename_usb_config.py ; Adafruit QT Py ESP32-S3 NO PSRAM @@ -219,6 +287,27 @@ board = adafruit_qtpy_esp32s3_nopsram build_flags = -DARDUINO_ADAFRUIT_QTPY_ESP32S3_NOPSRAM extra_scripts = pre:rename_usb_config.py +; Espressif ESP32-S3 NO PSRAM espressif_esp32s3_devkitc_1_n8 +[env:espressif_esp32s3_devkitc_1_n8] +extends = common:esp32 +board = esp32-s3-devkitc-1 +build_type = debug +build_flags = + -DUSE_TINYUSB=1 + -DARDUINO_ESPRESSIF_ESP32S3_DEVKITC_1_N8 + -DNDEBUG=1 + -DDEBUG=1 + -DESP_LOG_LEVEL=5 + -DARDUINO_CORE_DEBUG_LEVEL=5 + -DARDUINO_DEBUG_LEVEL=5 + ; -DARDUINO_DEBUG_OUTPUT=Serial + ; -DARDUINO_DEBUG_BAUD=115200 + -DARDUINO_LOG_LEVEL=5 + -DCORE_DEBUG_LEVEL=5 + -DARDUHAL_LOG_LEVEL=5 +board_build.partitions = tinyuf2-partitions-8MB.csv +extra_scripts = pre:rename_usb_config.py + ; ESP8266 Boards ; Adafruit Feather HUZZAH ESP8266 @@ -242,14 +331,39 @@ build_flags = -DUSE_TINYUSB=1 [env:adafruit_pyportal_m4_titano] extends = common:atsamd board = adafruit_pyportal_m4_titano -; build_type = debug +build_type = debug +upload_protocol = sam-ba ; upload_protocol = jlink -; debug_tool = jlink +debug_tool = jlink +monitor_port = auto ; monitor_port = jlink -; debug_init_break = +; monitor_port = socket://localhost:19021 +; debug_init_break = tbreak clearConfiguration lib_ignore = USBHost -build_flags = -DUSE_TINYUSB=1 +build_flags = -DUSE_TINYUSB + -D__SAMD51J20A__ + -DCRYSTALLESS -DADAFRUIT_PYPORTAL_M4_TITANO + -D__SAMD51__ + -D__FPU_PRESENT + -DARM_MATH_CM4 + -mfloat-abi=hard + -mfpu=fpv4-sp-d16 + -DCORE_DEBUG_LEVEL=5 + -DARDUINO_USB_CDC_ON_BOOT=1 + -DCFG_TUSB_DEBUG=1 + -DDEBUG=1 + -DNDEBUG=1 + -DUSE_AIRLIFT=1 + -g + ; -DUSBCON + ; -UCDC_DISABLED + ; -UPLUGGABLE_USB_DISABLED + ; -DCDC_ENABLED + ; -DPLUGGABLE_USB_ENABLED + ; -DSERIAL_DEBUG=1 + ; -DSERIAL_PORT=Serial1 + ; -DSERCOM_INSTANCE_SERIAL=1 ; Adafruit Metro M4 Airlift Lite [env:adafruit_metro_m4_airliftlite] @@ -303,4 +417,3 @@ build_flags = ; ; No USB stack ; build_flags = -DPIO_FRAMEWORK_ARDUINO_NO_USB ; -DPIO_FRAMEWORK_ARDUINO_ENABLE_IPV6 - \ No newline at end of file diff --git a/src/Wippersnapper.cpp b/src/Wippersnapper.cpp index fd6ff3292..a4c190825 100644 --- a/src/Wippersnapper.cpp +++ b/src/Wippersnapper.cpp @@ -182,6 +182,17 @@ void Wippersnapper::getMacAddr() { WS_DEBUG_PRINTLN("ERROR: Please define a network interface!"); } +/****************************************************************************/ +/*! + @brief Gets the network's RSSI. + @return int32_t RSSI value, 0 to 255, in dB +*/ +/****************************************************************************/ +int32_t Wippersnapper::getRSSI() { + WS_DEBUG_PRINTLN("ERROR: Please define a network interface!"); + return 0; +} + /****************************************************************************/ /*! @brief Sets up the MQTT client session. @@ -357,8 +368,8 @@ bool cbDecodePinConfigMsg(pb_istream_t *stream, const pb_field_t *field, // pb_decode the stream into a pinReqMessage wippersnapper_pin_v1_ConfigurePinRequest pinReqMsg = wippersnapper_pin_v1_ConfigurePinRequest_init_zero; - if (!pb_decode(stream, wippersnapper_pin_v1_ConfigurePinRequest_fields, - &pinReqMsg)) { + if (!ws_pb_decode(stream, wippersnapper_pin_v1_ConfigurePinRequest_fields, + &pinReqMsg)) { WS_DEBUG_PRINTLN("ERROR: Could not decode CreateSignalRequest") is_success = false; } @@ -398,7 +409,8 @@ bool cbDecodeDigitalPinWriteMsg(pb_istream_t *stream, const pb_field_t *field, // Decode stream into a PinEvent wippersnapper_pin_v1_PinEvent pinEventMsg = wippersnapper_pin_v1_PinEvent_init_zero; - if (!pb_decode(stream, wippersnapper_pin_v1_PinEvent_fields, &pinEventMsg)) { + if (!ws_pb_decode(stream, wippersnapper_pin_v1_PinEvent_fields, + &pinEventMsg)) { WS_DEBUG_PRINTLN("ERROR: Could not decode PinEvents") is_success = false; } @@ -442,8 +454,8 @@ bool cbSignalMsg(pb_istream_t *stream, const pb_field_t *field, void **arg) { msg.list.funcs.decode = cbDecodePinConfigMsg; msg.list.arg = field->pData; // decode each ConfigurePinRequest sub-message - if (!pb_decode(stream, wippersnapper_pin_v1_ConfigurePinRequests_fields, - &msg)) { + if (!ws_pb_decode(stream, wippersnapper_pin_v1_ConfigurePinRequests_fields, + &msg)) { WS_DEBUG_PRINTLN("ERROR: Could not decode CreateSignalRequest") is_success = false; WS.pinCfgCompleted = false; @@ -463,7 +475,7 @@ bool cbSignalMsg(pb_istream_t *stream, const pb_field_t *field, void **arg) { msg.list.funcs.decode = cbDecodeDigitalPinWriteMsg; msg.list.arg = field->pData; // decode each PinEvents sub-message - if (!pb_decode(stream, wippersnapper_pin_v1_PinEvents_fields, &msg)) { + if (!ws_pb_decode(stream, wippersnapper_pin_v1_PinEvents_fields, &msg)) { WS_DEBUG_PRINTLN("ERROR: Could not decode CreateSign2alRequest") is_success = false; } @@ -496,8 +508,8 @@ bool Wippersnapper::decodeSignalMsg( // decode the CreateSignalRequest, calls cbSignalMessage and assoc. callbacks pb_istream_t stream = pb_istream_from_buffer(WS._buffer, WS.bufSize); - if (!pb_decode(&stream, wippersnapper_signal_v1_CreateSignalRequest_fields, - encodedSignalMsg)) { + if (!ws_pb_decode(&stream, wippersnapper_signal_v1_CreateSignalRequest_fields, + encodedSignalMsg)) { WS_DEBUG_PRINTLN( "ERROR (decodeSignalMsg):, Could not decode CreateSignalRequest") is_success = false; @@ -546,8 +558,12 @@ void publishI2CResponse(wippersnapper_signal_v1_I2CResponse *msgi2cResponse) { pb_get_encoded_size(&msgSz, wippersnapper_signal_v1_I2CResponse_fields, msgi2cResponse); WS_DEBUG_PRINT("Publishing Message: I2CResponse..."); - WS._mqtt->publish(WS._topic_signal_i2c_device, WS._buffer_outgoing, msgSz, 1); - WS_DEBUG_PRINTLN("Published!"); + if (!WS._mqtt->publish(WS._topic_signal_i2c_device, WS._buffer_outgoing, + msgSz, 1)) { + WS_DEBUG_PRINTLN("ERROR: Failed to publish I2C Response!"); + } else { + WS_DEBUG_PRINTLN("Published!"); + } } /******************************************************************************************/ @@ -562,8 +578,8 @@ bool encodeI2CResponse(wippersnapper_signal_v1_I2CResponse *msgi2cResponse) { memset(WS._buffer_outgoing, 0, sizeof(WS._buffer_outgoing)); pb_ostream_t ostream = pb_ostream_from_buffer(WS._buffer_outgoing, sizeof(WS._buffer_outgoing)); - if (!pb_encode(&ostream, wippersnapper_signal_v1_I2CResponse_fields, - msgi2cResponse)) { + if (!ws_pb_encode(&ostream, wippersnapper_signal_v1_I2CResponse_fields, + msgi2cResponse)) { WS_DEBUG_PRINTLN("ERROR: Unable to encode I2C response message!"); return false; } @@ -609,8 +625,8 @@ bool cbDecodeI2CDeviceInitRequestList(pb_istream_t *stream, // Decode stream into individual msgI2CDeviceInitRequest messages wippersnapper_i2c_v1_I2CDeviceInitRequest msgI2CDeviceInitRequest = wippersnapper_i2c_v1_I2CDeviceInitRequest_init_zero; - if (!pb_decode(stream, wippersnapper_i2c_v1_I2CDeviceInitRequest_fields, - &msgI2CDeviceInitRequest)) { + if (!ws_pb_decode(stream, wippersnapper_i2c_v1_I2CDeviceInitRequest_fields, + &msgI2CDeviceInitRequest)) { WS_DEBUG_PRINTLN("ERROR: Could not decode I2CDeviceInitRequest message."); return false; } @@ -682,8 +698,8 @@ bool cbDecodeSignalRequestI2C(pb_istream_t *stream, const pb_field_t *field, // Decode I2CBusScanRequest wippersnapper_i2c_v1_I2CBusScanRequest msgScanReq = wippersnapper_i2c_v1_I2CBusScanRequest_init_zero; - if (!pb_decode(stream, wippersnapper_i2c_v1_I2CBusScanRequest_fields, - &msgScanReq)) { + if (!ws_pb_decode(stream, wippersnapper_i2c_v1_I2CBusScanRequest_fields, + &msgScanReq)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode wippersnapper_i2c_v1_I2CBusScanRequest"); return false; // fail out if we can't decode the request @@ -735,8 +751,8 @@ bool cbDecodeSignalRequestI2C(pb_istream_t *stream, const pb_field_t *field, cbDecodeI2CDeviceInitRequestList; msgI2CDeviceInitRequestList.list.arg = field->pData; // Decode each sub-message - if (!pb_decode(stream, wippersnapper_i2c_v1_I2CDeviceInitRequests_fields, - &msgI2CDeviceInitRequestList)) { + if (!ws_pb_decode(stream, wippersnapper_i2c_v1_I2CDeviceInitRequests_fields, + &msgI2CDeviceInitRequestList)) { WS_DEBUG_PRINTLN("ERROR: Could not decode I2CDeviceInitRequests"); is_success = false; } @@ -751,8 +767,8 @@ bool cbDecodeSignalRequestI2C(pb_istream_t *stream, const pb_field_t *field, wippersnapper_i2c_v1_I2CDeviceInitRequest msgI2CDeviceInitRequest = wippersnapper_i2c_v1_I2CDeviceInitRequest_init_zero; // Decode stream into struct, msgI2CDeviceInitRequest - if (!pb_decode(stream, wippersnapper_i2c_v1_I2CDeviceInitRequest_fields, - &msgI2CDeviceInitRequest)) { + if (!ws_pb_decode(stream, wippersnapper_i2c_v1_I2CDeviceInitRequest_fields, + &msgI2CDeviceInitRequest)) { WS_DEBUG_PRINTLN("ERROR: Could not decode I2CDeviceInitRequest message."); return false; // fail out if we can't decode } @@ -797,8 +813,9 @@ bool cbDecodeSignalRequestI2C(pb_istream_t *stream, const pb_field_t *field, wippersnapper_i2c_v1_I2CDeviceUpdateRequest_init_zero; // Decode stream into message - if (!pb_decode(stream, wippersnapper_i2c_v1_I2CDeviceUpdateRequest_fields, - &msgI2CDeviceUpdateRequest)) { + if (!ws_pb_decode(stream, + wippersnapper_i2c_v1_I2CDeviceUpdateRequest_fields, + &msgI2CDeviceUpdateRequest)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode I2CDeviceUpdateRequest message."); return false; // fail out if we can't decode @@ -829,8 +846,9 @@ bool cbDecodeSignalRequestI2C(pb_istream_t *stream, const pb_field_t *field, wippersnapper_i2c_v1_I2CDeviceDeinitRequest msgI2CDeviceDeinitRequest = wippersnapper_i2c_v1_I2CDeviceDeinitRequest_init_zero; // Decode stream into struct, msgI2CDeviceDeinitRequest - if (!pb_decode(stream, wippersnapper_i2c_v1_I2CDeviceDeinitRequest_fields, - &msgI2CDeviceDeinitRequest)) { + if (!ws_pb_decode(stream, + wippersnapper_i2c_v1_I2CDeviceDeinitRequest_fields, + &msgI2CDeviceDeinitRequest)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode I2CDeviceDeinitRequest message."); return false; // fail out if we can't decode @@ -891,8 +909,8 @@ void cbSignalI2CReq(char *data, uint16_t len) { // Decode I2C signal request pb_istream_t istream = pb_istream_from_buffer(WS._buffer, WS.bufSize); - if (!pb_decode(&istream, wippersnapper_signal_v1_I2CRequest_fields, - &WS.msgSignalI2C)) + if (!ws_pb_decode(&istream, wippersnapper_signal_v1_I2CRequest_fields, + &WS.msgSignalI2C)) WS_DEBUG_PRINTLN("ERROR: Unable to decode I2C message"); } @@ -917,8 +935,8 @@ bool cbDecodeServoMsg(pb_istream_t *stream, const pb_field_t *field, // Attempt to decode contents of servo_attach message wippersnapper_servo_v1_ServoAttachRequest msgServoAttachReq = wippersnapper_servo_v1_ServoAttachRequest_init_zero; - if (!pb_decode(stream, wippersnapper_servo_v1_ServoAttachRequest_fields, - &msgServoAttachReq)) { + if (!ws_pb_decode(stream, wippersnapper_servo_v1_ServoAttachRequest_fields, + &msgServoAttachReq)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode wippersnapper_servo_v1_ServoAttachRequest"); #ifdef USE_DISPLAY @@ -969,8 +987,8 @@ bool cbDecodeServoMsg(pb_istream_t *stream, const pb_field_t *field, memset(WS._buffer_outgoing, 0, sizeof(WS._buffer_outgoing)); pb_ostream_t ostream = pb_ostream_from_buffer(WS._buffer_outgoing, sizeof(WS._buffer_outgoing)); - if (!pb_encode(&ostream, wippersnapper_signal_v1_ServoResponse_fields, - &msgServoResp)) { + if (!ws_pb_encode(&ostream, wippersnapper_signal_v1_ServoResponse_fields, + &msgServoResp)) { WS_DEBUG_PRINTLN("ERROR: Unable to encode servo response message!"); return false; } @@ -988,8 +1006,8 @@ bool cbDecodeServoMsg(pb_istream_t *stream, const pb_field_t *field, wippersnapper_servo_v1_ServoWriteRequest msgServoWriteReq = wippersnapper_servo_v1_ServoWriteRequest_init_zero; - if (!pb_decode(stream, wippersnapper_servo_v1_ServoWriteRequest_fields, - &msgServoWriteReq)) { + if (!ws_pb_decode(stream, wippersnapper_servo_v1_ServoWriteRequest_fields, + &msgServoWriteReq)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode wippersnapper_servo_v1_ServoWriteRequest"); return false; // fail out if we can't decode the request @@ -1018,8 +1036,8 @@ bool cbDecodeServoMsg(pb_istream_t *stream, const pb_field_t *field, // Attempt to decode contents of servo detach message wippersnapper_servo_v1_ServoDetachRequest msgServoDetachReq = wippersnapper_servo_v1_ServoDetachRequest_init_zero; - if (!pb_decode(stream, wippersnapper_servo_v1_ServoDetachRequest_fields, - &msgServoDetachReq)) { + if (!ws_pb_decode(stream, wippersnapper_servo_v1_ServoDetachRequest_fields, + &msgServoDetachReq)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode wippersnapper_servo_v1_ServoDetachRequest"); return false; // fail out if we can't decode the request @@ -1071,8 +1089,8 @@ void cbServoMsg(char *data, uint16_t len) { // Decode servo message from buffer pb_istream_t istream = pb_istream_from_buffer(WS._buffer, WS.bufSize); - if (!pb_decode(&istream, wippersnapper_signal_v1_ServoRequest_fields, - &WS.msgServo)) + if (!ws_pb_decode(&istream, wippersnapper_signal_v1_ServoRequest_fields, + &WS.msgServo)) WS_DEBUG_PRINTLN("ERROR: Unable to decode servo message"); } @@ -1096,8 +1114,8 @@ bool cbPWMDecodeMsg(pb_istream_t *stream, const pb_field_t *field, void **arg) { // Attempt to decode contents of PWM attach message wippersnapper_pwm_v1_PWMAttachRequest msgPWMAttachRequest = wippersnapper_pwm_v1_PWMAttachRequest_init_zero; - if (!pb_decode(stream, wippersnapper_pwm_v1_PWMAttachRequest_fields, - &msgPWMAttachRequest)) { + if (!ws_pb_decode(stream, wippersnapper_pwm_v1_PWMAttachRequest_fields, + &msgPWMAttachRequest)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode wippersnapper_pwm_v1_PWMAttachRequest"); #ifdef USE_DISPLAY @@ -1134,8 +1152,8 @@ bool cbPWMDecodeMsg(pb_istream_t *stream, const pb_field_t *field, void **arg) { memset(WS._buffer_outgoing, 0, sizeof(WS._buffer_outgoing)); pb_ostream_t ostream = pb_ostream_from_buffer(WS._buffer_outgoing, sizeof(WS._buffer_outgoing)); - if (!pb_encode(&ostream, wippersnapper_signal_v1_PWMResponse_fields, - &msgPWMResponse)) { + if (!ws_pb_encode(&ostream, wippersnapper_signal_v1_PWMResponse_fields, + &msgPWMResponse)) { WS_DEBUG_PRINTLN("ERROR: Unable to encode PWM response message!"); return false; } @@ -1143,8 +1161,11 @@ bool cbPWMDecodeMsg(pb_istream_t *stream, const pb_field_t *field, void **arg) { pb_get_encoded_size(&msgSz, wippersnapper_signal_v1_PWMResponse_fields, &msgPWMResponse); WS_DEBUG_PRINT("PUBLISHING: PWM Attach Response..."); - WS._mqtt->publish(WS._topic_signal_pwm_device, WS._buffer_outgoing, msgSz, - 1); + if (!WS._mqtt->publish(WS._topic_signal_pwm_device, WS._buffer_outgoing, + msgSz, 1)) { + WS_DEBUG_PRINTLN("ERROR: Failed to publish PWM Attach Response!"); + return false; + } WS_DEBUG_PRINTLN("Published!"); #ifdef USE_DISPLAY @@ -1160,8 +1181,8 @@ bool cbPWMDecodeMsg(pb_istream_t *stream, const pb_field_t *field, void **arg) { // Attempt to decode contents of PWM detach message wippersnapper_pwm_v1_PWMDetachRequest msgPWMDetachRequest = wippersnapper_pwm_v1_PWMDetachRequest_init_zero; - if (!pb_decode(stream, wippersnapper_pwm_v1_PWMDetachRequest_fields, - &msgPWMDetachRequest)) { + if (!ws_pb_decode(stream, wippersnapper_pwm_v1_PWMDetachRequest_fields, + &msgPWMDetachRequest)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode wippersnapper_pwm_v1_PWMDetachRequest"); #ifdef USE_DISPLAY @@ -1187,8 +1208,9 @@ bool cbPWMDecodeMsg(pb_istream_t *stream, const pb_field_t *field, void **arg) { // Attempt to decode contents of PWM detach message wippersnapper_pwm_v1_PWMWriteFrequencyRequest msgPWMWriteFreqRequest = wippersnapper_pwm_v1_PWMWriteFrequencyRequest_init_zero; - if (!pb_decode(stream, wippersnapper_pwm_v1_PWMWriteFrequencyRequest_fields, - &msgPWMWriteFreqRequest)) { + if (!ws_pb_decode(stream, + wippersnapper_pwm_v1_PWMWriteFrequencyRequest_fields, + &msgPWMWriteFreqRequest)) { WS_DEBUG_PRINTLN("ERROR: Could not decode " "wippersnapper_pwm_v1_PWMWriteFrequencyRequest"); #ifdef USE_DISPLAY @@ -1220,8 +1242,9 @@ bool cbPWMDecodeMsg(pb_istream_t *stream, const pb_field_t *field, void **arg) { // Attempt to decode contents of PWM detach message wippersnapper_pwm_v1_PWMWriteDutyCycleRequest msgPWMWriteDutyCycleRequest = wippersnapper_pwm_v1_PWMWriteDutyCycleRequest_init_zero; - if (!pb_decode(stream, wippersnapper_pwm_v1_PWMWriteDutyCycleRequest_fields, - &msgPWMWriteDutyCycleRequest)) { + if (!ws_pb_decode(stream, + wippersnapper_pwm_v1_PWMWriteDutyCycleRequest_fields, + &msgPWMWriteDutyCycleRequest)) { WS_DEBUG_PRINTLN("ERROR: Could not decode " "wippersnapper_pwm_v1_PWMWriteDutyCycleRequest"); #ifdef USE_DISPLAY @@ -1275,8 +1298,8 @@ void cbPWMMsg(char *data, uint16_t len) { // Decode servo message from buffer pb_istream_t istream = pb_istream_from_buffer(WS._buffer, WS.bufSize); - if (!pb_decode(&istream, wippersnapper_signal_v1_PWMRequest_fields, - &WS.msgPWM)) + if (!ws_pb_decode(&istream, wippersnapper_signal_v1_PWMRequest_fields, + &WS.msgPWM)) WS_DEBUG_PRINTLN("ERROR: Unable to decode PWM message"); } @@ -1303,8 +1326,9 @@ bool cbDecodeDs18x20Msg(pb_istream_t *stream, const pb_field_t *field, wippersnapper_ds18x20_v1_Ds18x20InitRequest msgDS18xInitReq = wippersnapper_ds18x20_v1_Ds18x20InitRequest_init_zero; - if (!pb_decode(stream, wippersnapper_ds18x20_v1_Ds18x20InitRequest_fields, - &msgDS18xInitReq)) { + if (!ws_pb_decode(stream, + wippersnapper_ds18x20_v1_Ds18x20InitRequest_fields, + &msgDS18xInitReq)) { WS_DEBUG_PRINTLN("ERROR: Could not decode " "wippersnapper_ds18x20_v1_Ds18x20InitRequest"); return false; // fail out if we can't decode the request @@ -1319,8 +1343,9 @@ bool cbDecodeDs18x20Msg(pb_istream_t *stream, const pb_field_t *field, // Attempt to decode contents of message wippersnapper_ds18x20_v1_Ds18x20DeInitRequest msgDS18xDeInitReq = wippersnapper_ds18x20_v1_Ds18x20DeInitRequest_init_zero; - if (!pb_decode(stream, wippersnapper_ds18x20_v1_Ds18x20DeInitRequest_fields, - &msgDS18xDeInitReq)) { + if (!ws_pb_decode(stream, + wippersnapper_ds18x20_v1_Ds18x20DeInitRequest_fields, + &msgDS18xDeInitReq)) { WS_DEBUG_PRINTLN("ERROR: Could not decode " "wippersnapper_ds18x20_v1_Ds18x20DeInitRequest"); return false; // fail out if we can't decode the request @@ -1363,8 +1388,8 @@ void cbSignalDSReq(char *data, uint16_t len) { // Decode DS signal request pb_istream_t istream = pb_istream_from_buffer(WS._buffer, WS.bufSize); - if (!pb_decode(&istream, wippersnapper_signal_v1_Ds18x20Request_fields, - &WS.msgSignalDS)) + if (!ws_pb_decode(&istream, wippersnapper_signal_v1_Ds18x20Request_fields, + &WS.msgSignalDS)) WS_DEBUG_PRINTLN("ERROR: Unable to decode DS message"); } @@ -1393,8 +1418,9 @@ bool cbDecodePixelsMsg(pb_istream_t *stream, const pb_field_t *field, // attempt to decode create message wippersnapper_pixels_v1_PixelsCreateRequest msgPixelsCreateReq = wippersnapper_pixels_v1_PixelsCreateRequest_init_zero; - if (!pb_decode(stream, wippersnapper_pixels_v1_PixelsCreateRequest_fields, - &msgPixelsCreateReq)) { + if (!ws_pb_decode(stream, + wippersnapper_pixels_v1_PixelsCreateRequest_fields, + &msgPixelsCreateReq)) { WS_DEBUG_PRINTLN("ERROR: Could not decode message of type " "wippersnapper_pixels_v1_PixelsCreateRequest!"); #ifdef USE_DISPLAY @@ -1414,8 +1440,9 @@ bool cbDecodePixelsMsg(pb_istream_t *stream, const pb_field_t *field, // attempt to decode delete strand message wippersnapper_pixels_v1_PixelsDeleteRequest msgPixelsDeleteReq = wippersnapper_pixels_v1_PixelsDeleteRequest_init_zero; - if (!pb_decode(stream, wippersnapper_pixels_v1_PixelsDeleteRequest_fields, - &msgPixelsDeleteReq)) { + if (!ws_pb_decode(stream, + wippersnapper_pixels_v1_PixelsDeleteRequest_fields, + &msgPixelsDeleteReq)) { WS_DEBUG_PRINTLN("ERROR: Could not decode message of type " "wippersnapper_pixels_v1_PixelsDeleteRequest!"); return false; @@ -1432,8 +1459,8 @@ bool cbDecodePixelsMsg(pb_istream_t *stream, const pb_field_t *field, // attempt to decode pixel write message wippersnapper_pixels_v1_PixelsWriteRequest msgPixelsWritereq = wippersnapper_pixels_v1_PixelsWriteRequest_init_zero; - if (!pb_decode(stream, wippersnapper_pixels_v1_PixelsWriteRequest_fields, - &msgPixelsWritereq)) { + if (!ws_pb_decode(stream, wippersnapper_pixels_v1_PixelsWriteRequest_fields, + &msgPixelsWritereq)) { WS_DEBUG_PRINTLN("ERROR: Could not decode message of type " "wippersnapper_pixels_v1_PixelsWriteRequest!"); return false; @@ -1474,8 +1501,8 @@ void cbPixelsMsg(char *data, uint16_t len) { // Decode pixel message from buffer pb_istream_t istream = pb_istream_from_buffer(WS._buffer, WS.bufSize); - if (!pb_decode(&istream, wippersnapper_signal_v1_PixelsRequest_fields, - &WS.msgPixels)) + if (!ws_pb_decode(&istream, wippersnapper_signal_v1_PixelsRequest_fields, + &WS.msgPixels)) WS_DEBUG_PRINTLN("ERROR: Unable to decode pixel topic message"); } @@ -1503,8 +1530,9 @@ bool cbDecodeUARTMessage(pb_istream_t *stream, const pb_field_t *field, // attempt to decode create message wippersnapper_uart_v1_UARTDeviceAttachRequest msgUARTInitReq = wippersnapper_uart_v1_UARTDeviceAttachRequest_init_zero; - if (!pb_decode(stream, wippersnapper_uart_v1_UARTDeviceAttachRequest_fields, - &msgUARTInitReq)) { + if (!ws_pb_decode(stream, + wippersnapper_uart_v1_UARTDeviceAttachRequest_fields, + &msgUARTInitReq)) { WS_DEBUG_PRINTLN( "ERROR: Could not decode message of type: UARTDeviceAttachRequest!"); return false; @@ -1534,8 +1562,8 @@ bool cbDecodeUARTMessage(pb_istream_t *stream, const pb_field_t *field, memset(WS._buffer_outgoing, 0, sizeof(WS._buffer_outgoing)); pb_ostream_t ostream = pb_ostream_from_buffer(WS._buffer_outgoing, sizeof(WS._buffer_outgoing)); - if (!pb_encode(&ostream, wippersnapper_signal_v1_UARTResponse_fields, - &msgUARTResponse)) { + if (!ws_pb_encode(&ostream, wippersnapper_signal_v1_UARTResponse_fields, + &msgUARTResponse)) { WS_DEBUG_PRINTLN("ERROR: Unable to encode UART response message!"); return false; } @@ -1543,8 +1571,11 @@ bool cbDecodeUARTMessage(pb_istream_t *stream, const pb_field_t *field, pb_get_encoded_size(&msgSz, wippersnapper_signal_v1_UARTResponse_fields, &msgUARTResponse); WS_DEBUG_PRINT("PUBLISHING: UART Attach Response..."); - WS._mqtt->publish(WS._topic_signal_uart_device, WS._buffer_outgoing, msgSz, - 1); + if (!WS._mqtt->publish(WS._topic_signal_uart_device, WS._buffer_outgoing, + msgSz, 1)) { + WS_DEBUG_PRINTLN("ERROR: Failed to publish UART Attach Response!"); + return false; + } WS_DEBUG_PRINTLN("Published!"); } else if (field->tag == @@ -1553,8 +1584,9 @@ bool cbDecodeUARTMessage(pb_istream_t *stream, const pb_field_t *field, // attempt to decode uart detach request message wippersnapper_uart_v1_UARTDeviceDetachRequest msgUARTDetachReq = wippersnapper_uart_v1_UARTDeviceDetachRequest_init_zero; - if (!pb_decode(stream, wippersnapper_uart_v1_UARTDeviceDetachRequest_fields, - &msgUARTDetachReq)) { + if (!ws_pb_decode(stream, + wippersnapper_uart_v1_UARTDeviceDetachRequest_fields, + &msgUARTDetachReq)) { WS_DEBUG_PRINTLN("ERROR: Could not decode message!"); return false; } @@ -1594,8 +1626,8 @@ void cbSignalUARTReq(char *data, uint16_t len) { // Decode DS signal request pb_istream_t istream = pb_istream_from_buffer(WS._buffer, WS.bufSize); - if (!pb_decode(&istream, wippersnapper_signal_v1_UARTRequest_fields, - &WS.msgSignalUART)) + if (!ws_pb_decode(&istream, wippersnapper_signal_v1_UARTRequest_fields, + &WS.msgSignalUART)) WS_DEBUG_PRINTLN("ERROR: Unable to decode UART Signal message"); } @@ -1624,8 +1656,8 @@ bool Wippersnapper::encodePinEvent( // Encode signal message pb_ostream_t stream = pb_ostream_from_buffer(WS._buffer_outgoing, sizeof(WS._buffer_outgoing)); - if (!pb_encode(&stream, wippersnapper_signal_v1_CreateSignalRequest_fields, - outgoingSignalMsg)) { + if (!ws_pb_encode(&stream, wippersnapper_signal_v1_CreateSignalRequest_fields, + outgoingSignalMsg)) { WS_DEBUG_PRINTLN("ERROR: Unable to encode signal message"); is_success = false; } @@ -1673,9 +1705,7 @@ void cbErrorTopic(char *errorData, uint16_t len) { #endif // WDT reset - for (;;) { - delay(100); - } + WS.haltError("IO MQTT Ban Error"); } /**************************************************************************/ @@ -2372,7 +2402,6 @@ void Wippersnapper::runNetFSM() { haltError("ERROR: Unable to find WiFi network, rebooting soon...", WS_LED_STATUS_WIFI_CONNECTING); } - WS_DEBUG_PRINTLN("SSID found!"); // Attempt to connect to wireless network maxAttempts = 5; while (maxAttempts > 0) { @@ -2432,6 +2461,9 @@ void Wippersnapper::runNetFSM() { fsmNetwork = FSM_NET_CHECK_MQTT; break; } + WS_DEBUG_PRINT("MQTT Connection Error: "); + WS_DEBUG_PRINTLN(mqttRC); + WS_DEBUG_PRINTLN(WS._mqtt->connectErrorString(mqttRC)); WS_DEBUG_PRINTLN( "Unable to connect to Adafruit IO MQTT, retrying in 3 seconds..."); delay(3000); @@ -2468,17 +2500,17 @@ void Wippersnapper::runNetFSM() { */ /**************************************************************************/ void Wippersnapper::haltError(String error, ws_led_status_t ledStatusColor) { - WS_DEBUG_PRINT("ERROR [WDT RESET]: "); - WS_DEBUG_PRINTLN(error); for (;;) { + WS_DEBUG_PRINT("ERROR [WDT RESET]: "); + WS_DEBUG_PRINTLN(error); // let the WDT fail out and reset! statusLEDSolid(ledStatusColor); #ifndef ARDUINO_ARCH_ESP8266 - delay(100); + delay(1000); #else // Calls to delay() and yield() feed the ESP8266's // hardware and software watchdog timers, delayMicroseconds does not. - delayMicroseconds(100); + delayMicroseconds(1000000); #endif } } @@ -2526,10 +2558,17 @@ void Wippersnapper::pingBroker() { // ping within keepalive-10% to keep connection open if (millis() > (_prv_ping + (WS_KEEPALIVE_INTERVAL_MS - (WS_KEEPALIVE_INTERVAL_MS * 0.10)))) { - WS_DEBUG_PRINTLN("PING!"); - // TODO: Add back, is crashing currently - WS._mqtt->ping(); + WS_DEBUG_PRINT("Sending MQTT PING: "); + if (WS._mqtt->ping()) { + WS_DEBUG_PRINTLN("SUCCESS!"); + } else { + WS_DEBUG_PRINTLN("FAILURE! Running network FSM..."); + WS._mqtt->disconnect(); + runNetFSM(); + } _prv_ping = millis(); + WS_DEBUG_PRINT("WiFi RSSI: "); + WS_DEBUG_PRINTLN(getRSSI()); } // blink status LED every STATUS_LED_KAT_BLINK_TIME millis if (millis() > (_prvKATBlink + STATUS_LED_KAT_BLINK_TIME)) { @@ -2562,11 +2601,7 @@ void Wippersnapper::enableWDT(int timeoutMS) { Watchdog.disable(); #endif if (Watchdog.enable(timeoutMS) == 0) { - WS_DEBUG_PRINTLN("ERROR: WDT initialization failure!"); - setStatusLEDColor(LED_ERROR); - for (;;) { - delay(100); - } + WS.haltError("WDT initialization failure!"); } } @@ -2604,7 +2639,9 @@ void Wippersnapper::publish(const char *topic, uint8_t *payload, uint16_t bLen, // runNetFSM(); // NOTE: Removed for now, causes error with virtual _connect // method when caused with WS object in another file. WS.feedWDT(); - WS._mqtt->publish(topic, payload, bLen, qos); + if (!WS._mqtt->publish(topic, payload, bLen, qos)) { + WS_DEBUG_PRINTLN("Failed to publish MQTT message!"); + } } /**************************************************************/ @@ -2795,9 +2832,9 @@ void Wippersnapper::publishPinConfigComplete() { pb_ostream_t _msg_stream = pb_ostream_from_buffer(_message_buffer, sizeof(_message_buffer)); - bool _status = - pb_encode(&_msg_stream, - wippersnapper_description_v1_RegistrationComplete_fields, &msg); + bool _status = ws_pb_encode( + &_msg_stream, wippersnapper_description_v1_RegistrationComplete_fields, + &msg); size_t _message_len = _msg_stream.bytes_written; // verify message encoded correctly @@ -2841,9 +2878,11 @@ ws_status_t Wippersnapper::run() { // Process DS18x20 sensor events WS._ds18x20Component->update(); + WS.feedWDT(); // Process UART sensor events WS._uartComponent->update(); + WS.feedWDT(); return WS_NET_CONNECTED; // TODO: Make this funcn void! -} \ No newline at end of file +} diff --git a/src/Wippersnapper.h b/src/Wippersnapper.h index e30e93d1d..ccc55c9f9 100644 --- a/src/Wippersnapper.h +++ b/src/Wippersnapper.h @@ -1,442 +1,498 @@ -/*! - * @file Wippersnapper.h - * - * This is the documentation for Adafruit's Wippersnapper firmware for the - * Arduino platform. It is designed specifically to work with - * Adafruit IO Wippersnapper IoT platform. - * - * Adafruit invests time and resources providing this open source code, - * please support Adafruit and open-source hardware by purchasing - * products from Adafruit! - * - * Copyright (c) Brent Rubell 2020-2024 for Adafruit Industries. - * - * BSD license, all text here must be included in any redistribution. - * - */ - -#ifndef WIPPERSNAPPER_H -#define WIPPERSNAPPER_H - -// Cpp STD -#include - -// Nanopb dependencies -#include -#include -#include -#include - -#include // description.proto -#include // signal.proto - -// External libraries -#include "Adafruit_MQTT.h" // MQTT Client -#include "Adafruit_SleepyDog.h" // Watchdog -#include "Arduino.h" // Wiring -#include // SPI - -// Wippersnapper API Helpers -#include "Wippersnapper_Boards.h" -#include "components/statusLED/Wippersnapper_StatusLED.h" -#include "provisioning/ConfigJson.h" - -#define WS_DEBUG ///< Define to enable debugging to serial terminal -#define WS_PRINTER Serial ///< Where debug messages will be printed - -// Define actual debug output functions when necessary. -#ifdef WS_DEBUG -#define WS_DEBUG_PRINT(...) \ - { WS_PRINTER.print(__VA_ARGS__); } ///< Prints debug output. -#define WS_DEBUG_PRINTLN(...) \ - { WS_PRINTER.println(__VA_ARGS__); } ///< Prints line from debug output. -#define WS_DEBUG_PRINTHEX(...) \ - { WS_PRINTER.print(__VA_ARGS__, HEX); } ///< Prints debug output. -#else -#define WS_DEBUG_PRINT(...) \ - {} ///< Prints debug output -#define WS_DEBUG_PRINTLN(...) \ - {} ///< Prints line from debug output. -#endif - -// Wippersnapper components -#include "components/analogIO/Wippersnapper_AnalogIO.h" -#include "components/digitalIO/Wippersnapper_DigitalGPIO.h" -#include "components/i2c/WipperSnapper_I2C.h" - -// LEDC-Manager, ESP32-only -#ifdef ARDUINO_ARCH_ESP32 -#include "components/ledc/ws_ledc.h" -#endif - -// Display -#ifdef USE_DISPLAY -#include "display/ws_display_driver.h" -#include "display/ws_display_ui_helper.h" -#endif - -#include "components/ds18x20/ws_ds18x20.h" -#include "components/pixels/ws_pixels.h" -#include "components/pwm/ws_pwm.h" -#include "components/servo/ws_servo.h" -#include "components/uart/ws_uart.h" - -#if defined(USE_TINYUSB) -#include "provisioning/tinyusb/Wippersnapper_FS.h" -#endif - -#if defined(USE_LITTLEFS) -#include "provisioning/littlefs/WipperSnapper_LittleFS.h" -#endif - -#define WS_VERSION \ - "1.0.0-alpha.79" ///< WipperSnapper app. version (semver-formatted) - -// Reserved Adafruit IO MQTT topics -#define TOPIC_IO_THROTTLE "/throttle" ///< Adafruit IO Throttle MQTT Topic -#define TOPIC_IO_ERRORS "/errors" ///< Adafruit IO Error MQTT Topic - -// Reserved Wippersnapper topics -#define TOPIC_WS "/wprsnpr/" ///< WipperSnapper topic -#define TOPIC_INFO "/info/" ///< Registration sub-topic -#define TOPIC_SIGNALS "/signals/" ///< Signals sub-topic -#define TOPIC_I2C "/i2c" ///< I2C sub-topic -#define MQTT_TOPIC_PIXELS_DEVICE \ - "/signals/device/pixel" ///< Pixels device->broker topic -#define MQTT_TOPIC_PIXELS_BROKER \ - "/signals/broker/pixel" ///< Pixels broker->device topic - -/** Defines the Adafruit IO connection status */ -typedef enum { - WS_IDLE = 0, // Waiting for connection establishement - WS_NET_DISCONNECTED = 1, // Network disconnected - WS_DISCONNECTED = 2, // Disconnected from Adafruit IO - WS_FINGERPRINT_UNKOWN = 3, // Unknown WS_SSL_FINGERPRINT - - WS_NET_CONNECT_FAILED = 10, // Failed to connect to network - WS_CONNECT_FAILED = 11, // Failed to connect to Adafruit IO - WS_FINGERPRINT_INVALID = 12, // Unknown WS_SSL_FINGERPRINT - WS_AUTH_FAILED = 13, // Invalid Adafruit IO login credentials provided. - WS_SSID_INVALID = - 14, // SSID is "" or otherwise invalid, connection not attempted - - WS_NET_CONNECTED = 20, // Connected to Adafruit IO - WS_CONNECTED = 21, // Connected to network - WS_CONNECTED_INSECURE = 22, // Insecurely (non-SSL) connected to network - WS_FINGERPRINT_UNSUPPORTED = 23, // Unsupported WS_SSL_FINGERPRINT - WS_FINGERPRINT_VALID = 24, // Valid WS_SSL_FINGERPRINT - WS_BOARD_DESC_INVALID = 25, // Unable to send board description - WS_BOARD_RESYNC_FAILED = 26 // Board sync failure -} ws_status_t; - -/** Defines the Adafruit IO MQTT broker's connection return codes */ -typedef enum { - WS_MQTT_CONNECTED = 0, // Connected - WS_MQTT_INVALID_PROTOCOL = 1, // Invalid mqtt protocol - WS_MQTT_INVALID_CID = 2, // Client id rejected - WS_MQTT_SERVICE_UNAVALIABLE = 3, // Malformed user/pass - WS_MQTT_INVALID_USER_PASS = 4, // Unauthorized access to resource - WS_MQTT_UNAUTHORIZED = 5, // MQTT service unavailable - WS_MQTT_THROTTLED = 6, // Account throttled - WS_MQTT_BANNED = 7 // Account banned -} ws_mqtt_status_t; - -/** Defines the Wippersnapper client's hardware registration status */ -typedef enum { - WS_BOARD_DEF_IDLE, - WS_BOARD_DEF_SEND_FAILED, - WS_BOARD_DEF_SENT, - WS_BOARD_DEF_OK, - WS_BOARD_DEF_INVALID, - WS_BOARD_DEF_UNSPECIFIED -} ws_board_status_t; - -/** Defines the Wippersnapper client's network status */ -typedef enum { - FSM_NET_IDLE, - FSM_NET_CONNECTED, - FSM_MQTT_CONNECTED, - FSM_NET_CHECK_MQTT, - FSM_NET_CHECK_NETWORK, - FSM_NET_ESTABLISH_NETWORK, - FSM_NET_ESTABLISH_MQTT, -} fsm_net_t; - -#define WS_WDT_TIMEOUT 60000 ///< WDT timeout -/* MQTT Configuration */ -#define WS_KEEPALIVE_INTERVAL_MS \ - 5000 ///< Session keepalive interval time, in milliseconds - -#define WS_MQTT_MAX_PAYLOAD_SIZE \ - 512 ///< MAXIMUM expected payload size, in bytes - -class Wippersnapper_DigitalGPIO; -class Wippersnapper_AnalogIO; -class Wippersnapper_FS; -class WipperSnapper_LittleFS; -#ifdef USE_DISPLAY -class ws_display_driver; -class ws_display_ui_helper; -#endif -#ifdef ARDUINO_ARCH_ESP32 -class ws_ledc; -#endif -class WipperSnapper_Component_I2C; -class ws_servo; -class ws_pwm; -class ws_ds18x20; -class ws_pixels; -class ws_uart; - -/**************************************************************************/ -/*! - @brief Class that provides storage and functions for the Adafruit IO - Wippersnapper interface. -*/ -/**************************************************************************/ -class Wippersnapper { -public: - Wippersnapper(); - virtual ~Wippersnapper(); - - void provision(); - - bool lockStatusNeoPixel; ///< True if status LED is using the status neopixel - bool lockStatusDotStar; ///< True if status LED is using the status dotstar - bool lockStatusLED; ///< True if status LED is using the built-in LED - float status_pixel_brightness = - STATUS_PIXEL_BRIGHTNESS_DEFAULT; ///< Global status pixel's brightness - ///< (from 0.0 to 1.0) - - virtual void set_user_key(); - virtual void set_ssid_pass(const char *ssid, const char *ssidPassword); - virtual void set_ssid_pass(); - virtual bool check_valid_ssid(); - - virtual void _connect(); - virtual void _disconnect(); - void connect(); - void disconnect(); - - virtual void getMacAddr(); - virtual void setupMQTTClient(const char *clientID); - - virtual ws_status_t networkStatus(); - ws_board_status_t getBoardStatus(); - - bool generateDeviceUID(); - bool generateWSTopics(); - bool generateWSErrorTopics(); - - // Registration API - bool registerBoard(); - bool encodePubRegistrationReq(); - void decodeRegistrationResp(char *data, uint16_t len); - void pollRegistrationResp(); - // Configuration API - void publishPinConfigComplete(); - - // run() loop - ws_status_t run(); - void processPackets(); - void publish(const char *topic, uint8_t *payload, uint16_t bLen, - uint8_t qos = 0); - - // Networking helpers - void pingBroker(); - void runNetFSM(); - - // WDT helpers - void enableWDT(int timeoutMS = 0); - void feedWDT(); - - // Error handling helpers - void haltError(String error, - ws_led_status_t ledStatusColor = WS_LED_STATUS_ERROR_RUNTIME); - void errorWriteHang(String error); - - // MQTT topic callbacks // - // Decodes a signal message - bool decodeSignalMsg( - wippersnapper_signal_v1_CreateSignalRequest *encodedSignalMsg); - - // Encodes a pin event message - bool - encodePinEvent(wippersnapper_signal_v1_CreateSignalRequest *outgoingSignalMsg, - uint8_t pinName, int pinVal); - - // Pin configure message - bool configureDigitalPinReq(wippersnapper_pin_v1_ConfigurePinRequest *pinMsg); - bool configAnalogInPinReq(wippersnapper_pin_v1_ConfigurePinRequest *pinMsg); - - // I2C - std::vector - i2cComponents; ///< Vector containing all I2C components - WipperSnapper_Component_I2C *_i2cPort0 = - NULL; ///< WipperSnapper I2C Component for I2C port #0 - WipperSnapper_Component_I2C *_i2cPort1 = - NULL; ///< WipperSnapper I2C Component for I2C port #1 - bool _isI2CPort0Init = - false; ///< True if I2C port 0 has been initialized, False otherwise. - bool _isI2CPort1Init = - false; ///< True if I2C port 1 has been initialized, False otherwise. - - uint8_t _buffer[WS_MQTT_MAX_PAYLOAD_SIZE]; /*!< Shared buffer to save callback - payload */ - uint8_t - _buffer_outgoing[WS_MQTT_MAX_PAYLOAD_SIZE]; /*!< buffer which contains - outgoing payload data */ - uint16_t bufSize; /*!< Length of data inside buffer */ - - ws_board_status_t _boardStatus = - WS_BOARD_DEF_IDLE; ///< Hardware's registration status - - // TODO: We really should look at making these static definitions, not dynamic - // to free up space on the heap - Wippersnapper_DigitalGPIO *_digitalGPIO; ///< Instance of digital gpio class - Wippersnapper_AnalogIO *_analogIO; ///< Instance of analog io class - Wippersnapper_FS *_fileSystem; ///< Instance of Filesystem (native USB) - WipperSnapper_LittleFS - *_littleFS; ///< Instance of LittleFS Filesystem (non-native USB) -#ifdef USE_DISPLAY - ws_display_driver *_display = nullptr; ///< Instance of display driver class - ws_display_ui_helper *_ui_helper = - nullptr; ///< Instance of display UI helper class -#endif - ws_pixels *_ws_pixelsComponent; ///< ptr to instance of ws_pixels class - ws_pwm *_pwmComponent; ///< Instance of pwm class - ws_servo *_servoComponent; ///< Instance of servo class - ws_ds18x20 *_ds18x20Component; ///< Instance of DS18x20 class - ws_uart *_uartComponent; ///< Instance of UART class - - // TODO: does this really need to be global? - uint8_t _macAddr[6]; /*!< Unique network iface identifier */ - char sUID[13]; /*!< Unique network iface identifier */ - const char *_boardId; /*!< Adafruit IO+ board string */ - Adafruit_MQTT *_mqtt; /*!< Reference to Adafruit_MQTT, _mqtt. */ - - secretsConfig _config; /*!< Wippersnapper secrets.json as a struct. */ - - // TODO: Does this need to be within this class? - int32_t totalDigitalPins; /*!< Total number of digital-input capable pins */ - - char *_topic_description = NULL; /*!< MQTT topic for the device description */ - char *_topic_signal_device = NULL; /*!< Device->Wprsnpr messages */ - char *_topic_signal_i2c_brkr = NULL; /*!< Topic carries messages from a device - to a broker. */ - char *_topic_signal_i2c_device = NULL; /*!< Topic carries messages from a - broker to a device. */ - char *_topic_signal_servo_brkr = NULL; /*!< Topic carries messages from a - device to a broker. */ - char *_topic_signal_servo_device = NULL; /*!< Topic carries messages from a - broker to a device. */ - char *_topic_signal_pwm_brkr = - NULL; /*!< Topic carries PWM messages from a device to a broker. */ - char *_topic_signal_pwm_device = - NULL; /*!< Topic carries PWM messages from a broker to a device. */ - char *_topic_signal_ds18_brkr = NULL; /*!< Topic carries ds18x20 messages from - a device to a broker. */ - char *_topic_signal_ds18_device = NULL; /*!< Topic carries ds18x20 messages - from a broker to a device. */ - char *_topic_signal_pixels_brkr = NULL; /*!< Topic carries pixel messages */ - char *_topic_signal_pixels_device = NULL; /*!< Topic carries pixel messages */ - char *_topic_signal_uart_brkr = NULL; /*!< Topic carries UART messages */ - char *_topic_signal_uart_device = NULL; /*!< Topic carries UART messages */ - - wippersnapper_signal_v1_CreateSignalRequest - _incomingSignalMsg; /*!< Incoming signal message from broker */ - wippersnapper_signal_v1_I2CRequest msgSignalI2C = - wippersnapper_signal_v1_I2CRequest_init_zero; ///< I2C request wrapper - ///< message - - // ds signal msg - wippersnapper_signal_v1_Ds18x20Request msgSignalDS = - wippersnapper_signal_v1_Ds18x20Request_init_zero; ///< DS request message - ///< wrapper - - // servo message - wippersnapper_signal_v1_ServoRequest - msgServo; ///< ServoRequest wrapper message - wippersnapper_signal_v1_PWMRequest msgPWM = - wippersnapper_signal_v1_PWMRequest_init_zero; ///< PWM request wrapper - ///< message. - - // pixels signal message - wippersnapper_signal_v1_PixelsRequest - msgPixels; ///< PixelsRequest wrapper message - - wippersnapper_signal_v1_UARTRequest - msgSignalUART; ///< UARTReq wrapper message - - char *throttleMessage; /*!< Pointer to throttle message data. */ - int throttleTime; /*!< Total amount of time to throttle the device, in - milliseconds. */ - - bool pinCfgCompleted = false; /*!< Did initial pin sync complete? */ - -// enable LEDC if esp32 -#ifdef ARDUINO_ARCH_ESP32 - ws_ledc *_ledc = nullptr; ///< Pointer to LEDC object -#endif - -private: - void _init(); - -protected: - ws_status_t _status = WS_IDLE; /*!< Adafruit IO connection status */ - uint32_t _last_mqtt_connect = 0; /*!< Previous time when client connected to - Adafruit IO, in milliseconds. */ - uint32_t _prv_ping = 0; /*!< Previous time when client pinged Adafruit IO's - MQTT broker, in milliseconds. */ - uint32_t _prvKATBlink = 0; /*!< Previous time when client pinged Adafruit IO's - MQTT broker, in milliseconds. */ - - // Device information - const char *_deviceId; /*!< Adafruit IO+ device identifier string */ - char *_device_uid; /*!< Unique device identifier */ - - // MQTT topics - char *_topic_description_status = - NULL; /*!< MQTT subtopic carrying the description - status resp. from the broker */ - char *_topic_description_status_complete = NULL; /*!< MQTT topic carrying the - ACK signal from the device to the - broker after registration */ - char *_topic_device_pin_config_complete = - NULL; /*!< MQTT topic carrying the ACK signal - from the device to the broker after - hardware configuration */ - char *_topic_signal_brkr = NULL; /*!< Wprsnpr->Device messages */ - char *_err_topic = NULL; /*!< Adafruit IO MQTT error message topic. */ - char *_throttle_topic = NULL; /*!< Adafruit IO MQTT throttle message topic. */ - - Adafruit_MQTT_Subscribe *_topic_description_sub; /*!< Subscription callback - for registration topic. */ - Adafruit_MQTT_Publish *_topic_signal_device_pub; /*!< Subscription callback - for D2C signal topic. */ - Adafruit_MQTT_Subscribe *_topic_signal_brkr_sub; /*!< Subscription callback - for C2D signal topic. */ - Adafruit_MQTT_Subscribe - *_topic_signal_i2c_sub; /*!< Subscription callback for I2C topic. */ - Adafruit_MQTT_Subscribe - *_topic_signal_servo_sub; /*!< Subscription callback for servo topic. */ - Adafruit_MQTT_Subscribe - *_topic_signal_pwm_sub; /*!< Subscription callback for pwm topic. */ - Adafruit_MQTT_Subscribe - *_topic_signal_ds18_sub; /*!< Subscribes to signal's ds18x20 topic. */ - Adafruit_MQTT_Subscribe - *_topic_signal_pixels_sub; /*!< Subscribes to pixel device topic. */ - Adafruit_MQTT_Subscribe - *_topic_signal_uart_sub; /*!< Subscribes to signal's UART topic. */ - - Adafruit_MQTT_Subscribe - *_err_sub; /*!< Subscription to Adafruit IO Error topic. */ - Adafruit_MQTT_Subscribe - *_throttle_sub; /*!< Subscription to Adafruit IO Throttle topic. */ - - wippersnapper_signal_v1_CreateSignalRequest - _outgoingSignalMsg; /*!< Outgoing signal message from device */ -}; -extern Wippersnapper WS; ///< Global member variable for callbacks - -#endif // ADAFRUIT_WIPPERSNAPPER_H +/*! + * @file Wippersnapper.h + * + * This is the documentation for Adafruit's Wippersnapper firmware for the + * Arduino platform. It is designed specifically to work with + * Adafruit IO Wippersnapper IoT platform. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2020-2024 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ + +#ifndef WIPPERSNAPPER_H +#define WIPPERSNAPPER_H + +// Cpp STD +#include + +// Nanopb dependencies +#include +#include +#include +#include + +#include // description.proto +#include // signal.proto + +// External libraries +#include "Adafruit_MQTT.h" // MQTT Client +#include "Adafruit_SleepyDog.h" // Watchdog +#include "Arduino.h" // Wiring +#include // SPI + +// Wippersnapper API Helpers +#include "Wippersnapper_Boards.h" +#include "components/statusLED/Wippersnapper_StatusLED.h" +#include "provisioning/ConfigJson.h" + +#define WS_DEBUG ///< Define to enable debugging to serial terminal +#define WS_PRINTER Serial ///< Where debug messages will be printed + +// Define actual debug output functions when necessary. +#ifdef WS_DEBUG +#define WS_DEBUG_PRINT(...) \ + { WS_PRINTER.print(__VA_ARGS__); } ///< Prints debug output. +#define WS_DEBUG_PRINTLN(...) \ + { WS_PRINTER.println(__VA_ARGS__); } ///< Prints line from debug output. +#define WS_DEBUG_PRINTHEX(...) \ + { WS_PRINTER.print(__VA_ARGS__, HEX); } ///< Prints debug output. +#else +#define WS_DEBUG_PRINT(...) \ + {} ///< Prints debug output +#define WS_DEBUG_PRINTLN(...) \ + {} ///< Prints line from debug output. +#endif + +#define WS_DELAY_WITH_WDT(timeout) \ + { \ + unsigned long start = millis(); \ + while (millis() - start < timeout) { \ + delay(10); \ + yield(); \ + feedWDT(); \ + if (millis() < start) { \ + start = millis(); /* if rollover */ \ + } \ + } \ + } ///< Delay function + +/**************************************************************************/ +/*! + @brief Retry a function until a condition is met or a timeout is reached. + @param func + The function to retry. + @param result_type + The type of the result of the function. + @param result_var + The variable to store the last result of the function. + @param condition + The condition to check the result against. + @param timeout + The maximum time to retry the function. + @param interval + The time to wait between retries. + @param ... + The arguments to pass to the function. +*/ +/**************************************************************************/ +#define RETRY_FUNCTION_UNTIL_TIMEOUT(func, result_type, result_var, condition, \ + timeout, interval, ...) \ + { \ + unsigned long startTime = millis(); \ + while (millis() - startTime < timeout) { \ + result_type result_var = func(__VA_ARGS__); \ + if (condition(result_var)) { \ + break; \ + } \ + if (startTime > millis()) { \ + startTime = millis(); /* if rollover */ \ + } \ + WS_DELAY_WITH_WDT(interval); \ + } \ + } ///< Retry a function until a condition is met or a timeout is reached. + +// Wippersnapper pb helpers +#include + +// Wippersnapper components +#include "components/analogIO/Wippersnapper_AnalogIO.h" +#include "components/digitalIO/Wippersnapper_DigitalGPIO.h" +#include "components/i2c/WipperSnapper_I2C.h" + +// Includes for ESP32-only +#ifdef ARDUINO_ARCH_ESP32 +#include "components/ledc/ws_ledc.h" +#include +#endif + +// Display +#ifdef USE_DISPLAY +#include "display/ws_display_driver.h" +#include "display/ws_display_ui_helper.h" +#endif + +#include "components/ds18x20/ws_ds18x20.h" +#include "components/pixels/ws_pixels.h" +#include "components/pwm/ws_pwm.h" +#include "components/servo/ws_servo.h" +#include "components/uart/ws_uart.h" + +#if defined(USE_TINYUSB) +#include "provisioning/tinyusb/Wippersnapper_FS.h" +#endif + +#if defined(USE_LITTLEFS) +#include "provisioning/littlefs/WipperSnapper_LittleFS.h" +#endif + +#define WS_VERSION \ + "1.0.0-beta.90" ///< WipperSnapper app. version (semver-formatted) + +// Reserved Adafruit IO MQTT topics +#define TOPIC_IO_THROTTLE "/throttle" ///< Adafruit IO Throttle MQTT Topic +#define TOPIC_IO_ERRORS "/errors" ///< Adafruit IO Error MQTT Topic + +// Reserved Wippersnapper topics +#define TOPIC_WS "/wprsnpr/" ///< WipperSnapper topic +#define TOPIC_INFO "/info/" ///< Registration sub-topic +#define TOPIC_SIGNALS "/signals/" ///< Signals sub-topic +#define TOPIC_I2C "/i2c" ///< I2C sub-topic +#define MQTT_TOPIC_PIXELS_DEVICE \ + "/signals/device/pixel" ///< Pixels device->broker topic +#define MQTT_TOPIC_PIXELS_BROKER \ + "/signals/broker/pixel" ///< Pixels broker->device topic + +/** Defines the Adafruit IO connection status */ +typedef enum { + WS_IDLE = 0, // Waiting for connection establishement + WS_NET_DISCONNECTED = 1, // Network disconnected + WS_DISCONNECTED = 2, // Disconnected from Adafruit IO + WS_FINGERPRINT_UNKOWN = 3, // Unknown WS_SSL_FINGERPRINT + + WS_NET_CONNECT_FAILED = 10, // Failed to connect to network + WS_CONNECT_FAILED = 11, // Failed to connect to Adafruit IO + WS_FINGERPRINT_INVALID = 12, // Unknown WS_SSL_FINGERPRINT + WS_AUTH_FAILED = 13, // Invalid Adafruit IO login credentials provided. + WS_SSID_INVALID = + 14, // SSID is "" or otherwise invalid, connection not attempted + + WS_NET_CONNECTED = 20, // Connected to Adafruit IO + WS_CONNECTED = 21, // Connected to network + WS_CONNECTED_INSECURE = 22, // Insecurely (non-SSL) connected to network + WS_FINGERPRINT_UNSUPPORTED = 23, // Unsupported WS_SSL_FINGERPRINT + WS_FINGERPRINT_VALID = 24, // Valid WS_SSL_FINGERPRINT + WS_BOARD_DESC_INVALID = 25, // Unable to send board description + WS_BOARD_RESYNC_FAILED = 26 // Board sync failure +} ws_status_t; + +/** Defines the Adafruit IO MQTT broker's connection return codes */ +typedef enum { + WS_MQTT_CONNECTED = 0, // Connected + WS_MQTT_INVALID_PROTOCOL = 1, // Invalid mqtt protocol + WS_MQTT_INVALID_CID = 2, // Client id rejected + WS_MQTT_SERVICE_UNAVALIABLE = 3, // Malformed user/pass + WS_MQTT_INVALID_USER_PASS = 4, // Unauthorized access to resource + WS_MQTT_UNAUTHORIZED = 5, // MQTT service unavailable + WS_MQTT_THROTTLED = 6, // Account throttled + WS_MQTT_BANNED = 7 // Account banned +} ws_mqtt_status_t; + +/** Defines the Wippersnapper client's hardware registration status */ +typedef enum { + WS_BOARD_DEF_IDLE, + WS_BOARD_DEF_SEND_FAILED, + WS_BOARD_DEF_SENT, + WS_BOARD_DEF_OK, + WS_BOARD_DEF_INVALID, + WS_BOARD_DEF_UNSPECIFIED +} ws_board_status_t; + +/** Defines the Wippersnapper client's network status */ +typedef enum { + FSM_NET_IDLE, + FSM_NET_CONNECTED, + FSM_MQTT_CONNECTED, + FSM_NET_CHECK_MQTT, + FSM_NET_CHECK_NETWORK, + FSM_NET_ESTABLISH_NETWORK, + FSM_NET_ESTABLISH_MQTT, +} fsm_net_t; + +#define WS_WDT_TIMEOUT 60000 ///< WDT timeout +#define WS_MAX_ALT_WIFI_NETWORKS 3 ///< Maximum number of alternative networks +/* MQTT Configuration */ +#define WS_KEEPALIVE_INTERVAL_MS \ + 5000 ///< Session keepalive interval time, in milliseconds + +#define WS_MQTT_MAX_PAYLOAD_SIZE \ + 512 ///< MAXIMUM expected payload size, in bytes + +class Wippersnapper_DigitalGPIO; +class Wippersnapper_AnalogIO; +class Wippersnapper_FS; +class WipperSnapper_LittleFS; +#ifdef USE_DISPLAY +class ws_display_driver; +class ws_display_ui_helper; +#endif +#ifdef ARDUINO_ARCH_ESP32 +class ws_ledc; +#endif +class WipperSnapper_Component_I2C; +class ws_servo; +class ws_pwm; +class ws_ds18x20; +class ws_pixels; +class ws_uart; + +/**************************************************************************/ +/*! + @brief Class that provides storage and functions for the Adafruit IO + Wippersnapper interface. +*/ +/**************************************************************************/ +class Wippersnapper { +public: + Wippersnapper(); + virtual ~Wippersnapper(); + + void provision(); + + bool lockStatusNeoPixel; ///< True if status LED is using the status neopixel + bool lockStatusDotStar; ///< True if status LED is using the status dotstar + bool lockStatusLED; ///< True if status LED is using the built-in LED + float status_pixel_brightness = + STATUS_PIXEL_BRIGHTNESS_DEFAULT; ///< Global status pixel's brightness + ///< (from 0.0 to 1.0) + + virtual void set_user_key(); + virtual void set_ssid_pass(const char *ssid, const char *ssidPassword); + virtual void set_ssid_pass(); + virtual bool check_valid_ssid(); + + virtual void _connect(); + virtual void _disconnect(); + void connect(); + void disconnect(); + + virtual void getMacAddr(); + virtual int32_t getRSSI(); + virtual void setupMQTTClient(const char *clientID); + + virtual ws_status_t networkStatus(); + ws_board_status_t getBoardStatus(); + + bool generateDeviceUID(); + bool generateWSTopics(); + bool generateWSErrorTopics(); + + // Registration API + bool registerBoard(); + bool encodePubRegistrationReq(); + void decodeRegistrationResp(char *data, uint16_t len); + void pollRegistrationResp(); + // Configuration API + void publishPinConfigComplete(); + + // run() loop + ws_status_t run(); + void processPackets(); + void publish(const char *topic, uint8_t *payload, uint16_t bLen, + uint8_t qos = 0); + + // Networking helpers + void pingBroker(); + void runNetFSM(); + + // WDT helpers + void enableWDT(int timeoutMS = 0); + void feedWDT(); + + // Error handling helpers + void haltError(String error, + ws_led_status_t ledStatusColor = WS_LED_STATUS_ERROR_RUNTIME); + void errorWriteHang(String error); + + // MQTT topic callbacks // + // Decodes a signal message + bool decodeSignalMsg( + wippersnapper_signal_v1_CreateSignalRequest *encodedSignalMsg); + + // Encodes a pin event message + bool + encodePinEvent(wippersnapper_signal_v1_CreateSignalRequest *outgoingSignalMsg, + uint8_t pinName, int pinVal); + + // Pin configure message + bool configureDigitalPinReq(wippersnapper_pin_v1_ConfigurePinRequest *pinMsg); + bool configAnalogInPinReq(wippersnapper_pin_v1_ConfigurePinRequest *pinMsg); + + // I2C + std::vector + i2cComponents; ///< Vector containing all I2C components + WipperSnapper_Component_I2C *_i2cPort0 = + NULL; ///< WipperSnapper I2C Component for I2C port #0 + WipperSnapper_Component_I2C *_i2cPort1 = + NULL; ///< WipperSnapper I2C Component for I2C port #1 + bool _isI2CPort0Init = + false; ///< True if I2C port 0 has been initialized, False otherwise. + bool _isI2CPort1Init = + false; ///< True if I2C port 1 has been initialized, False otherwise. + + uint8_t _buffer[WS_MQTT_MAX_PAYLOAD_SIZE]; /*!< Shared buffer to save callback + payload */ + uint8_t + _buffer_outgoing[WS_MQTT_MAX_PAYLOAD_SIZE]; /*!< buffer which contains + outgoing payload data */ + uint16_t bufSize; /*!< Length of data inside buffer */ + + ws_board_status_t _boardStatus = + WS_BOARD_DEF_IDLE; ///< Hardware's registration status + + // TODO: We really should look at making these static definitions, not dynamic + // to free up space on the heap + Wippersnapper_DigitalGPIO *_digitalGPIO; ///< Instance of digital gpio class + Wippersnapper_AnalogIO *_analogIO; ///< Instance of analog io class + Wippersnapper_FS *_fileSystem; ///< Instance of Filesystem (native USB) + WipperSnapper_LittleFS + *_littleFS; ///< Instance of LittleFS Filesystem (non-native USB) +#ifdef USE_DISPLAY + ws_display_driver *_display = nullptr; ///< Instance of display driver class + ws_display_ui_helper *_ui_helper = + nullptr; ///< Instance of display UI helper class +#endif + ws_pixels *_ws_pixelsComponent; ///< ptr to instance of ws_pixels class + ws_pwm *_pwmComponent; ///< Instance of pwm class + ws_servo *_servoComponent; ///< Instance of servo class + ws_ds18x20 *_ds18x20Component; ///< Instance of DS18x20 class + ws_uart *_uartComponent; ///< Instance of UART class + + // TODO: does this really need to be global? + uint8_t _macAddr[6]; /*!< Unique network iface identifier */ + char sUID[13]; /*!< Unique network iface identifier */ + const char *_boardId; /*!< Adafruit IO+ board string */ + Adafruit_MQTT *_mqtt; /*!< Reference to Adafruit_MQTT, _mqtt. */ + + secretsConfig _config; /*!< Wippersnapper secrets.json as a struct. */ + networkConfig _multiNetworks[3]; /*!< Wippersnapper networks as structs. */ + bool _isWiFiMulti = false; /*!< True if multiple networks are defined. */ + + // TODO: Does this need to be within this class? + int32_t totalDigitalPins; /*!< Total number of digital-input capable pins */ + + char *_topic_description = NULL; /*!< MQTT topic for the device description */ + char *_topic_signal_device = NULL; /*!< Device->Wprsnpr messages */ + char *_topic_signal_i2c_brkr = NULL; /*!< Topic carries messages from a device + to a broker. */ + char *_topic_signal_i2c_device = NULL; /*!< Topic carries messages from a + broker to a device. */ + char *_topic_signal_servo_brkr = NULL; /*!< Topic carries messages from a + device to a broker. */ + char *_topic_signal_servo_device = NULL; /*!< Topic carries messages from a + broker to a device. */ + char *_topic_signal_pwm_brkr = + NULL; /*!< Topic carries PWM messages from a device to a broker. */ + char *_topic_signal_pwm_device = + NULL; /*!< Topic carries PWM messages from a broker to a device. */ + char *_topic_signal_ds18_brkr = NULL; /*!< Topic carries ds18x20 messages from + a device to a broker. */ + char *_topic_signal_ds18_device = NULL; /*!< Topic carries ds18x20 messages + from a broker to a device. */ + char *_topic_signal_pixels_brkr = NULL; /*!< Topic carries pixel messages */ + char *_topic_signal_pixels_device = NULL; /*!< Topic carries pixel messages */ + char *_topic_signal_uart_brkr = NULL; /*!< Topic carries UART messages */ + char *_topic_signal_uart_device = NULL; /*!< Topic carries UART messages */ + + wippersnapper_signal_v1_CreateSignalRequest + _incomingSignalMsg; /*!< Incoming signal message from broker */ + wippersnapper_signal_v1_I2CRequest msgSignalI2C = + wippersnapper_signal_v1_I2CRequest_init_zero; ///< I2C request wrapper + ///< message + + // ds signal msg + wippersnapper_signal_v1_Ds18x20Request msgSignalDS = + wippersnapper_signal_v1_Ds18x20Request_init_zero; ///< DS request message + ///< wrapper + + // servo message + wippersnapper_signal_v1_ServoRequest + msgServo; ///< ServoRequest wrapper message + wippersnapper_signal_v1_PWMRequest msgPWM = + wippersnapper_signal_v1_PWMRequest_init_zero; ///< PWM request wrapper + ///< message. + + // pixels signal message + wippersnapper_signal_v1_PixelsRequest + msgPixels; ///< PixelsRequest wrapper message + + wippersnapper_signal_v1_UARTRequest + msgSignalUART; ///< UARTReq wrapper message + + char *throttleMessage; /*!< Pointer to throttle message data. */ + int throttleTime; /*!< Total amount of time to throttle the device, in + milliseconds. */ + + bool pinCfgCompleted = false; /*!< Did initial pin sync complete? */ + +// enable LEDC if esp32 +#ifdef ARDUINO_ARCH_ESP32 + ws_ledc *_ledc = nullptr; ///< Pointer to LEDC object +#endif + +private: + void _init(); + +protected: + ws_status_t _status = WS_IDLE; /*!< Adafruit IO connection status */ + uint32_t _last_mqtt_connect = 0; /*!< Previous time when client connected to + Adafruit IO, in milliseconds. */ + uint32_t _prv_ping = 0; /*!< Previous time when client pinged Adafruit IO's + MQTT broker, in milliseconds. */ + uint32_t _prvKATBlink = 0; /*!< Previous time when client pinged Adafruit IO's + MQTT broker, in milliseconds. */ + + // Device information + const char *_deviceId; /*!< Adafruit IO+ device identifier string */ + char *_device_uid; /*!< Unique device identifier */ + + // MQTT topics + char *_topic_description_status = + NULL; /*!< MQTT subtopic carrying the description + status resp. from the broker */ + char *_topic_description_status_complete = NULL; /*!< MQTT topic carrying the + ACK signal from the device to the + broker after registration */ + char *_topic_device_pin_config_complete = + NULL; /*!< MQTT topic carrying the ACK signal + from the device to the broker after + hardware configuration */ + char *_topic_signal_brkr = NULL; /*!< Wprsnpr->Device messages */ + char *_err_topic = NULL; /*!< Adafruit IO MQTT error message topic. */ + char *_throttle_topic = NULL; /*!< Adafruit IO MQTT throttle message topic. */ + + Adafruit_MQTT_Subscribe *_topic_description_sub; /*!< Subscription callback + for registration topic. */ + Adafruit_MQTT_Publish *_topic_signal_device_pub; /*!< Subscription callback + for D2C signal topic. */ + Adafruit_MQTT_Subscribe *_topic_signal_brkr_sub; /*!< Subscription callback + for C2D signal topic. */ + Adafruit_MQTT_Subscribe + *_topic_signal_i2c_sub; /*!< Subscription callback for I2C topic. */ + Adafruit_MQTT_Subscribe + *_topic_signal_servo_sub; /*!< Subscription callback for servo topic. */ + Adafruit_MQTT_Subscribe + *_topic_signal_pwm_sub; /*!< Subscription callback for pwm topic. */ + Adafruit_MQTT_Subscribe + *_topic_signal_ds18_sub; /*!< Subscribes to signal's ds18x20 topic. */ + Adafruit_MQTT_Subscribe + *_topic_signal_pixels_sub; /*!< Subscribes to pixel device topic. */ + Adafruit_MQTT_Subscribe + *_topic_signal_uart_sub; /*!< Subscribes to signal's UART topic. */ + + Adafruit_MQTT_Subscribe + *_err_sub; /*!< Subscription to Adafruit IO Error topic. */ + Adafruit_MQTT_Subscribe + *_throttle_sub; /*!< Subscription to Adafruit IO Throttle topic. */ + + wippersnapper_signal_v1_CreateSignalRequest + _outgoingSignalMsg; /*!< Outgoing signal message from device */ +}; +extern Wippersnapper WS; ///< Global member variable for callbacks + +#endif // ADAFRUIT_WIPPERSNAPPER_H diff --git a/src/Wippersnapper_Boards.h b/src/Wippersnapper_Boards.h index bd3c263a6..b119329a1 100644 --- a/src/Wippersnapper_Boards.h +++ b/src/Wippersnapper_Boards.h @@ -150,6 +150,12 @@ #define USE_LITTLEFS #define USE_STATUS_LED #define STATUS_LED_PIN 13 +#elif defined(ARDUINO_ADAFRUIT_FEATHER_ESP32C6) +#define BOARD_ID "feather-esp32c6" +#define USE_LITTLEFS +#define USE_STATUS_NEOPIXEL +#define STATUS_NEOPIXEL_PIN PIN_NEOPIXEL +#define STATUS_NEOPIXEL_NUM 1 #elif defined(ARDUINO_ADAFRUIT_FEATHER_ESP32_V2) #define BOARD_ID "feather-esp32-v2" #define USE_LITTLEFS diff --git a/src/components/analogIO/Wippersnapper_AnalogIO.cpp b/src/components/analogIO/Wippersnapper_AnalogIO.cpp index 86ceebced..660208de2 100644 --- a/src/components/analogIO/Wippersnapper_AnalogIO.cpp +++ b/src/components/analogIO/Wippersnapper_AnalogIO.cpp @@ -89,6 +89,12 @@ void Wippersnapper_AnalogIO::setADCResolution(int resolution) { #elif defined(ARDUINO_ARCH_ESP32) scaleAnalogRead = true; _nativeResolution = 13; +#elif defined(ARDUINO_ARCH_RP2040) + scaleAnalogRead = true; + _nativeResolution = 10; +#else + scaleAnalogRead = true; + _nativeResolution = 10; #endif _adcResolution = resolution; @@ -205,7 +211,6 @@ void Wippersnapper_AnalogIO::deinitAnalogPin( uint16_t Wippersnapper_AnalogIO::getPinValue(int pin) { // get pin value uint16_t value = analogRead(pin); - // scale by the ADC resolution manually if not implemented by BSP if (scaleAnalogRead) { if (getADCresolution() > getNativeResolution()) { @@ -280,8 +285,8 @@ bool Wippersnapper_AnalogIO::encodePinEvent( // Encode signal message pb_ostream_t stream = pb_ostream_from_buffer(WS._buffer_outgoing, sizeof(WS._buffer_outgoing)); - if (!pb_encode(&stream, wippersnapper_signal_v1_CreateSignalRequest_fields, - &outgoingSignalMsg)) { + if (!ws_pb_encode(&stream, wippersnapper_signal_v1_CreateSignalRequest_fields, + &outgoingSignalMsg)) { WS_DEBUG_PRINTLN("ERROR: Unable to encode signal message"); return false; } diff --git a/src/components/ds18x20/ws_ds18x20.cpp b/src/components/ds18x20/ws_ds18x20.cpp index 91360271d..0cfa6e0c5 100644 --- a/src/components/ds18x20/ws_ds18x20.cpp +++ b/src/components/ds18x20/ws_ds18x20.cpp @@ -108,8 +108,8 @@ bool ws_ds18x20::addDS18x20( memset(WS._buffer_outgoing, 0, sizeof(WS._buffer_outgoing)); pb_ostream_t ostream = pb_ostream_from_buffer(WS._buffer_outgoing, sizeof(WS._buffer_outgoing)); - if (!pb_encode(&ostream, wippersnapper_signal_v1_Ds18x20Response_fields, - &msgInitResp)) { + if (!ws_pb_encode(&ostream, wippersnapper_signal_v1_Ds18x20Response_fields, + &msgInitResp)) { WS_DEBUG_PRINTLN("ERROR: Unable to encode msg_init response message!"); return false; } @@ -257,9 +257,9 @@ void ws_ds18x20::update() { memset(WS._buffer_outgoing, 0, sizeof(WS._buffer_outgoing)); pb_ostream_t ostream = pb_ostream_from_buffer( WS._buffer_outgoing, sizeof(WS._buffer_outgoing)); - if (!pb_encode(&ostream, - wippersnapper_signal_v1_Ds18x20Response_fields, - &msgDS18x20Response)) { + if (!ws_pb_encode(&ostream, + wippersnapper_signal_v1_Ds18x20Response_fields, + &msgDS18x20Response)) { WS_DEBUG_PRINTLN( "ERROR: Unable to encode DS18x20 event responsemessage!"); snprintf(buffer, 100, @@ -296,6 +296,8 @@ void ws_ds18x20::update() { WS_DEBUG_PRINT("PUBLISHING -> msgDS18x20Response Event Message..."); if (!WS._mqtt->publish(WS._topic_signal_ds18_device, WS._buffer_outgoing, msgSz, 1)) { + WS_DEBUG_PRINTLN("ERROR: Unable to publish DS18x20 event message - " + "MQTT Publish failed!"); return; }; WS_DEBUG_PRINTLN("PUBLISHED!"); diff --git a/src/components/i2c/WipperSnapper_I2C.cpp b/src/components/i2c/WipperSnapper_I2C.cpp index 0bf7690d0..bd47877e4 100644 --- a/src/components/i2c/WipperSnapper_I2C.cpp +++ b/src/components/i2c/WipperSnapper_I2C.cpp @@ -296,6 +296,17 @@ bool WipperSnapper_Component_I2C::initI2CDevice( _dps310->configureDriver(msgDeviceInitReq); drivers.push_back(_dps310); WS_DEBUG_PRINTLN("DPS310 Initialized Successfully!"); + } else if (strcmp("ds2484", msgDeviceInitReq->i2c_device_name) == 0) { + _ds2484 = new WipperSnapper_I2C_Driver_DS2484(this->_i2c, i2cAddress); + if (!_ds2484->begin()) { + WS_DEBUG_PRINTLN("ERROR: Failed to initialize DS2484!"); + _busStatusResponse = + wippersnapper_i2c_v1_BusResponse_BUS_RESPONSE_DEVICE_INIT_FAIL; + return false; + } + _ds2484->configureDriver(msgDeviceInitReq); + drivers.push_back(_ds2484); + WS_DEBUG_PRINTLN("DS2484 Initialized Successfully!"); } else if (strcmp("ens160", msgDeviceInitReq->i2c_device_name) == 0) { _ens160 = new WipperSnapper_I2C_Driver_ENS160(this->_i2c, i2cAddress); if (!_ens160->begin()) { @@ -307,6 +318,17 @@ bool WipperSnapper_Component_I2C::initI2CDevice( _ens160->configureDriver(msgDeviceInitReq); drivers.push_back(_ens160); WS_DEBUG_PRINTLN("ENS160 Initialized Successfully!"); + } else if (strcmp("hdc302x", msgDeviceInitReq->i2c_device_name) == 0) { + _hdc302x = new WipperSnapper_I2C_Driver_HDC302X(this->_i2c, i2cAddress); + if (!_hdc302x->begin()) { + WS_DEBUG_PRINTLN("ERROR: Failed to initialize HDC302X!"); + _busStatusResponse = + wippersnapper_i2c_v1_BusResponse_BUS_RESPONSE_DEVICE_INIT_FAIL; + return false; + } + _hdc302x->configureDriver(msgDeviceInitReq); + drivers.push_back(_hdc302x); + WS_DEBUG_PRINTLN("HDC302X Initialized Successfully!"); } else if (strcmp("hts221", msgDeviceInitReq->i2c_device_name) == 0) { _hts221 = new WipperSnapper_I2C_Driver_HTS221(this->_i2c, i2cAddress); if (!_hts221->begin()) { @@ -386,6 +408,17 @@ bool WipperSnapper_Component_I2C::initI2CDevice( _ltr329->configureDriver(msgDeviceInitReq); drivers.push_back(_ltr329); WS_DEBUG_PRINTLN("LTR329/303 Initialized Successfully!"); + } else if (strcmp("nau7802", msgDeviceInitReq->i2c_device_name) == 0) { + _nau7802 = new WipperSnapper_I2C_Driver_NAU7802(this->_i2c, i2cAddress); + if (!_nau7802->begin()) { + WS_DEBUG_PRINTLN("ERROR: Failed to initialize NAU7802"); + _busStatusResponse = + wippersnapper_i2c_v1_BusResponse_BUS_RESPONSE_DEVICE_INIT_FAIL; + return false; + } + _nau7802->configureDriver(msgDeviceInitReq); + drivers.push_back(_nau7802); + WS_DEBUG_PRINTLN("NAU7802 Initialized Successfully!"); } else if (strcmp("sgp30", msgDeviceInitReq->i2c_device_name) == 0) { _sgp30 = new WipperSnapper_I2C_Driver_SGP30(this->_i2c, i2cAddress); if (!_sgp30->begin()) { @@ -420,6 +453,17 @@ bool WipperSnapper_Component_I2C::initI2CDevice( _si7021->configureDriver(msgDeviceInitReq); drivers.push_back(_si7021); WS_DEBUG_PRINTLN("SI7021/SHT20 Initialized Successfully!"); + } else if (strcmp("mcp3421", msgDeviceInitReq->i2c_device_name) == 0) { + _mcp3421 = new WipperSnapper_I2C_Driver_MCP3421(this->_i2c, i2cAddress); + if (!_mcp3421->begin()) { + WS_DEBUG_PRINTLN("ERROR: Failed to initialize MCP3421!"); + _busStatusResponse = + wippersnapper_i2c_v1_BusResponse_BUS_RESPONSE_DEVICE_INIT_FAIL; + return false; + } + _mcp3421->configureDriver(msgDeviceInitReq); + drivers.push_back(_mcp3421); + WS_DEBUG_PRINTLN("MCP3421 Initialized Successfully!"); } else if (strcmp("mcp9808", msgDeviceInitReq->i2c_device_name) == 0) { _mcp9808 = new WipperSnapper_I2C_Driver_MCP9808(this->_i2c, i2cAddress); if (!_mcp9808->begin()) { @@ -530,7 +574,10 @@ bool WipperSnapper_Component_I2C::initI2CDevice( _scd40->configureDriver(msgDeviceInitReq); drivers.push_back(_scd40); WS_DEBUG_PRINTLN("SCD4x Initialized Successfully!"); - } else if (strcmp("sen5x", msgDeviceInitReq->i2c_device_name) == 0) { + } else if ((strcmp("sen5x", msgDeviceInitReq->i2c_device_name) == 0) || + (strcmp("sen55", msgDeviceInitReq->i2c_device_name) == 0) || + (strcmp("sen54", msgDeviceInitReq->i2c_device_name) == 0) || + (strcmp("sen50", msgDeviceInitReq->i2c_device_name) == 0)) { _sen5x = new WipperSnapper_I2C_Driver_SEN5X(this->_i2c, i2cAddress); if (!_sen5x->begin()) { WS_DEBUG_PRINTLN("ERROR: Failed to initialize SEN5X!"); @@ -680,6 +727,28 @@ bool WipperSnapper_Component_I2C::initI2CDevice( _vl53l1x->configureDriver(msgDeviceInitReq); drivers.push_back(_vl53l1x); WS_DEBUG_PRINTLN("VL53L1X Initialized Successfully!"); + } else if (strcmp("vl53l4cd", msgDeviceInitReq->i2c_device_name) == 0) { + _vl53l4cd = new WipperSnapper_I2C_Driver_VL53L4CD(this->_i2c, i2cAddress); + if (!_vl53l4cd->begin()) { + WS_DEBUG_PRINTLN("ERROR: Failed to initialize VL53L4CD!"); + _busStatusResponse = + wippersnapper_i2c_v1_BusResponse_BUS_RESPONSE_DEVICE_INIT_FAIL; + return false; + } + _vl53l4cd->configureDriver(msgDeviceInitReq); + drivers.push_back(_vl53l4cd); + WS_DEBUG_PRINTLN("VL53L4CD Initialized Successfully!"); + } else if (strcmp("vl53l4cx", msgDeviceInitReq->i2c_device_name) == 0) { + _vl53l4cx = new WipperSnapper_I2C_Driver_VL53L4CX(this->_i2c, i2cAddress); + if (!_vl53l4cx->begin()) { + WS_DEBUG_PRINTLN("ERROR: Failed to initialize VL53L4CX!"); + _busStatusResponse = + wippersnapper_i2c_v1_BusResponse_BUS_RESPONSE_DEVICE_INIT_FAIL; + return false; + } + _vl53l4cx->configureDriver(msgDeviceInitReq); + drivers.push_back(_vl53l4cx); + WS_DEBUG_PRINTLN("VL53L4CX Initialized Successfully!"); } else if (strcmp("vl6180x", msgDeviceInitReq->i2c_device_name) == 0) { _vl6180x = new WipperSnapper_I2C_Driver_VL6180X(this->_i2c, i2cAddress); if (!_vl6180x->begin()) { @@ -801,8 +870,8 @@ bool WipperSnapper_Component_I2C::encodePublishI2CDeviceEventMsg( memset(WS._buffer_outgoing, 0, sizeof(WS._buffer_outgoing)); pb_ostream_t ostream = pb_ostream_from_buffer(WS._buffer_outgoing, sizeof(WS._buffer_outgoing)); - if (!pb_encode(&ostream, wippersnapper_signal_v1_I2CResponse_fields, - msgi2cResponse)) { + if (!ws_pb_encode(&ostream, wippersnapper_signal_v1_I2CResponse_fields, + msgi2cResponse)) { WS_DEBUG_PRINTLN( "ERROR: Unable to encode I2C device event response message!"); return false; @@ -815,6 +884,7 @@ bool WipperSnapper_Component_I2C::encodePublishI2CDeviceEventMsg( WS_DEBUG_PRINT("PUBLISHING -> I2C Device Sensor Event Message..."); if (!WS._mqtt->publish(WS._topic_signal_i2c_device, WS._buffer_outgoing, msgSz, 1)) { + WS_DEBUG_PRINTLN("ERROR: MQTT Publish failed!"); return false; }; WS_DEBUG_PRINTLN("PUBLISHED!"); @@ -962,524 +1032,342 @@ void WipperSnapper_Component_I2C::update() { wippersnapper_signal_v1_I2CResponse_resp_i2c_device_event_tag; long curTime; - std::vector::iterator iter, end; - for (iter = drivers.begin(), end = drivers.end(); iter != end; ++iter) { - // Number of events which occured for this driver - msgi2cResponse.payload.resp_i2c_device_event.sensor_event_count = 0; - - // Event struct - sensors_event_t event; - - // AMBIENT_TEMPERATURE sensor (°C) - curTime = millis(); - if ((*iter)->getSensorAmbientTempPeriod() != 0L && - curTime - (*iter)->getSensorAmbientTempPeriodPrv() > - (*iter)->getSensorAmbientTempPeriod()) { - if ((*iter)->getEventAmbientTemp(&event)) { - WS_DEBUG_PRINT("Sensor 0x"); - WS_DEBUG_PRINTHEX((*iter)->getI2CAddress()); - WS_DEBUG_PRINTLN(""); - WS_DEBUG_PRINT("\tTemperature: "); - WS_DEBUG_PRINT(event.temperature); - WS_DEBUG_PRINTLN(" degrees C"); - - // pack event data into msg - fillEventMessage( - &msgi2cResponse, event.temperature, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_AMBIENT_TEMPERATURE); - - (*iter)->setSensorAmbientTempPeriodPrv(curTime); - } else { - WS_DEBUG_PRINTLN( - "ERROR: Failed to get ambient temperature sensor reading!"); - } - } - - // Ambient Temperature sensor (°F) - curTime = millis(); - if ((*iter)->getSensorAmbientTempFPeriod() != 0L && - curTime - (*iter)->getSensorAmbientTempFPeriodPrv() > - (*iter)->getSensorAmbientTempFPeriod()) { - if ((*iter)->getEventAmbientTempF(&event)) { - WS_DEBUG_PRINT("Sensor 0x"); - WS_DEBUG_PRINTHEX((*iter)->getI2CAddress()); - WS_DEBUG_PRINTLN(""); - WS_DEBUG_PRINT("\tAmbient Temp.: "); - WS_DEBUG_PRINT(event.temperature); - WS_DEBUG_PRINTLN("°F"); - - (*iter)->setSensorAmbientTempFPeriodPrv(curTime); - - fillEventMessage( - &msgi2cResponse, event.temperature, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_AMBIENT_TEMPERATURE_FAHRENHEIT); - } else { - WS_DEBUG_PRINTLN( - "ERROR: Failed to obtain ambient temp. (°F)) sensor reading!"); + bool sensorsReturningFalse = true; + int retries = 3; + + while (sensorsReturningFalse && retries > 0) { + sensorsReturningFalse = false; + retries--; + + std::vector::iterator iter, end; + for (iter = drivers.begin(), end = drivers.end(); iter != end; ++iter) { + // Number of events which occured for this driver + msgi2cResponse.payload.resp_i2c_device_event.sensor_event_count = 0; + + // Event struct + sensors_event_t event; + + // AMBIENT_TEMPERATURE sensor (°C) + sensorEventRead( + iter, curTime, &msgi2cResponse, + &WipperSnapper_I2C_Driver::getEventAmbientTemp, + &WipperSnapper_I2C_Driver::getSensorAmbientTempPeriod, + &WipperSnapper_I2C_Driver::getSensorAmbientTempPeriodPrv, + &WipperSnapper_I2C_Driver::setSensorAmbientTempPeriodPrv, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_AMBIENT_TEMPERATURE, + "Ambient Temperature", " degrees C", event, + &sensors_event_t::temperature, sensorsReturningFalse, retries); + + // Ambient Temperature sensor (°F) + sensorEventRead( + iter, curTime, &msgi2cResponse, + &WipperSnapper_I2C_Driver::getEventAmbientTempF, + &WipperSnapper_I2C_Driver::getSensorAmbientTempFPeriod, + &WipperSnapper_I2C_Driver::getSensorAmbientTempFPeriodPrv, + &WipperSnapper_I2C_Driver::setSensorAmbientTempFPeriodPrv, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_AMBIENT_TEMPERATURE_FAHRENHEIT, + "Ambient Temperature", " degrees F", event, + &sensors_event_t::temperature, sensorsReturningFalse, retries); + + // OBJECT_TEMPERATURE sensor (°C) + sensorEventRead( + iter, curTime, &msgi2cResponse, + &WipperSnapper_I2C_Driver::getEventObjectTemp, + &WipperSnapper_I2C_Driver::getSensorObjectTempPeriod, + &WipperSnapper_I2C_Driver::getSensorObjectTempPeriodPrv, + &WipperSnapper_I2C_Driver::setSensorObjectTempPeriodPrv, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_OBJECT_TEMPERATURE, + "Object Temperature", " degrees C", event, + &sensors_event_t::temperature, sensorsReturningFalse, retries); + + // OBJECT_TEMPERATURE sensor (°F) + sensorEventRead( + iter, curTime, &msgi2cResponse, + &WipperSnapper_I2C_Driver::getEventObjectTempF, + &WipperSnapper_I2C_Driver::getSensorObjectTempFPeriod, + &WipperSnapper_I2C_Driver::getSensorObjectTempFPeriodPrv, + &WipperSnapper_I2C_Driver::setSensorObjectTempFPeriodPrv, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_OBJECT_TEMPERATURE_FAHRENHEIT, + "Object Temperature", " degrees F", event, + &sensors_event_t::temperature, sensorsReturningFalse, retries); + + // RELATIVE_HUMIDITY sensor + sensorEventRead( + iter, curTime, &msgi2cResponse, + &WipperSnapper_I2C_Driver::getEventRelativeHumidity, + &WipperSnapper_I2C_Driver::getSensorRelativeHumidityPeriod, + &WipperSnapper_I2C_Driver::getSensorRelativeHumidityPeriodPrv, + &WipperSnapper_I2C_Driver::setSensorRelativeHumidityPeriodPrv, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_RELATIVE_HUMIDITY, + "Humidity", " %RH", event, &sensors_event_t::relative_humidity, + sensorsReturningFalse, retries); + + // PRESSURE sensor + sensorEventRead(iter, curTime, &msgi2cResponse, + &WipperSnapper_I2C_Driver::getEventPressure, + &WipperSnapper_I2C_Driver::getSensorPressurePeriod, + &WipperSnapper_I2C_Driver::getSensorPressurePeriodPrv, + &WipperSnapper_I2C_Driver::setSensorPressurePeriodPrv, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PRESSURE, + "Pressure", " hPa", event, &sensors_event_t::pressure, + sensorsReturningFalse, retries); + + // CO2 sensor + sensorEventRead(iter, curTime, &msgi2cResponse, + &WipperSnapper_I2C_Driver::getEventCO2, + &WipperSnapper_I2C_Driver::getSensorCO2Period, + &WipperSnapper_I2C_Driver::getSensorCO2PeriodPrv, + &WipperSnapper_I2C_Driver::setSensorCO2PeriodPrv, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_CO2, "CO2", + " ppm", event, &sensors_event_t::CO2, + sensorsReturningFalse, retries); + + // eCO2 sensor + sensorEventRead(iter, curTime, &msgi2cResponse, + &WipperSnapper_I2C_Driver::getEventECO2, + &WipperSnapper_I2C_Driver::getSensorECO2Period, + &WipperSnapper_I2C_Driver::getSensorECO2PeriodPrv, + &WipperSnapper_I2C_Driver::setSensorECO2PeriodPrv, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_ECO2, "eCO2", + " ppm", event, &sensors_event_t::eCO2, + sensorsReturningFalse, retries); + + // TVOC sensor + sensorEventRead(iter, curTime, &msgi2cResponse, + &WipperSnapper_I2C_Driver::getEventTVOC, + &WipperSnapper_I2C_Driver::getSensorTVOCPeriod, + &WipperSnapper_I2C_Driver::getSensorTVOCPeriodPrv, + &WipperSnapper_I2C_Driver::setSensorTVOCPeriodPrv, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_TVOC, "TVOC", + " ppb", event, &sensors_event_t::tvoc, + sensorsReturningFalse, retries); + + // Altitude sensor + sensorEventRead(iter, curTime, &msgi2cResponse, + &WipperSnapper_I2C_Driver::getEventAltitude, + &WipperSnapper_I2C_Driver::getSensorAltitudePeriod, + &WipperSnapper_I2C_Driver::getSensorAltitudePeriodPrv, + &WipperSnapper_I2C_Driver::setSensorAltitudePeriodPrv, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_ALTITUDE, + "Altitude", " m", event, &sensors_event_t::altitude, + sensorsReturningFalse, retries); + + // Light sensor + sensorEventRead(iter, curTime, &msgi2cResponse, + &WipperSnapper_I2C_Driver::getEventLight, + &WipperSnapper_I2C_Driver::getSensorLightPeriod, + &WipperSnapper_I2C_Driver::getSensorLightPeriodPrv, + &WipperSnapper_I2C_Driver::setSensorLightPeriodPrv, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_LIGHT, + "Light", " lux", event, &sensors_event_t::light, + sensorsReturningFalse, retries); + + // PM10_STD sensor + sensorEventRead(iter, curTime, &msgi2cResponse, + &WipperSnapper_I2C_Driver::getEventPM10_STD, + &WipperSnapper_I2C_Driver::getSensorPM10_STDPeriod, + &WipperSnapper_I2C_Driver::getSensorPM10_STDPeriodPrv, + &WipperSnapper_I2C_Driver::setSensorPM10_STDPeriodPrv, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM10_STD, + "PM1.0", " ppm", event, &sensors_event_t::pm10_std, + sensorsReturningFalse, retries); + + // PM25_STD sensor + sensorEventRead(iter, curTime, &msgi2cResponse, + &WipperSnapper_I2C_Driver::getEventPM25_STD, + &WipperSnapper_I2C_Driver::getSensorPM25_STDPeriod, + &WipperSnapper_I2C_Driver::getSensorPM25_STDPeriodPrv, + &WipperSnapper_I2C_Driver::setSensorPM25_STDPeriodPrv, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM25_STD, + "PM2.5", " ppm", event, &sensors_event_t::pm25_std, + sensorsReturningFalse, retries); + + // PM100_STD sensor + sensorEventRead(iter, curTime, &msgi2cResponse, + &WipperSnapper_I2C_Driver::getEventPM100_STD, + &WipperSnapper_I2C_Driver::getSensorPM100_STDPeriod, + &WipperSnapper_I2C_Driver::getSensorPM100_STDPeriodPrv, + &WipperSnapper_I2C_Driver::setSensorPM100_STDPeriodPrv, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM100_STD, + "PM10.0", " ppm", event, &sensors_event_t::pm100_std, + sensorsReturningFalse, retries); + + // Voltage sensor + sensorEventRead(iter, curTime, &msgi2cResponse, + &WipperSnapper_I2C_Driver::getEventVoltage, + &WipperSnapper_I2C_Driver::getSensorVoltagePeriod, + &WipperSnapper_I2C_Driver::getSensorVoltagePeriodPrv, + &WipperSnapper_I2C_Driver::setSensorVoltagePeriodPrv, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_VOLTAGE, + "Voltage", " V", event, &sensors_event_t::voltage, + sensorsReturningFalse, retries); + + // Current sensor + sensorEventRead(iter, curTime, &msgi2cResponse, + &WipperSnapper_I2C_Driver::getEventCurrent, + &WipperSnapper_I2C_Driver::getSensorCurrentPeriod, + &WipperSnapper_I2C_Driver::getSensorCurrentPeriodPrv, + &WipperSnapper_I2C_Driver::setSensorCurrentPeriodPrv, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_CURRENT, + "Current", " mA", event, &sensors_event_t::current, + sensorsReturningFalse, retries); + + // Unitless % sensor + sensorEventRead( + iter, curTime, &msgi2cResponse, + &WipperSnapper_I2C_Driver::getEventUnitlessPercent, + &WipperSnapper_I2C_Driver::getSensorUnitlessPercentPeriod, + &WipperSnapper_I2C_Driver::getSensorUnitlessPercentPeriodPrv, + &WipperSnapper_I2C_Driver::setSensorUnitlessPercentPeriodPrv, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_UNITLESS_PERCENT, + "Unitless Percent", " %", event, &sensors_event_t::unitless_percent, + sensorsReturningFalse, retries); + + // Raw sensor + sensorEventRead(iter, curTime, &msgi2cResponse, + &WipperSnapper_I2C_Driver::getEventRaw, + &WipperSnapper_I2C_Driver::getSensorRawPeriod, + &WipperSnapper_I2C_Driver::getSensorRawPeriodPrv, + &WipperSnapper_I2C_Driver::setSensorRawPeriodPrv, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_RAW, "Raw", + "", event, nullptr, sensorsReturningFalse, retries); + + // Gas sensor + sensorEventRead( + iter, curTime, &msgi2cResponse, + &WipperSnapper_I2C_Driver::getEventGasResistance, + &WipperSnapper_I2C_Driver::getSensorGasResistancePeriod, + &WipperSnapper_I2C_Driver::getSensorGasResistancePeriodPrv, + &WipperSnapper_I2C_Driver::setSensorGasResistancePeriodPrv, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_GAS_RESISTANCE, + "Gas Resistance", " Ohms", event, &sensors_event_t::gas_resistance, + sensorsReturningFalse, retries); + + // NOx-index sensor + sensorEventRead(iter, curTime, &msgi2cResponse, + &WipperSnapper_I2C_Driver::getEventNOxIndex, + &WipperSnapper_I2C_Driver::getSensorNOxIndexPeriod, + &WipperSnapper_I2C_Driver::getSensorNOxIndexPeriodPrv, + &WipperSnapper_I2C_Driver::setSensorNOxIndexPeriodPrv, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_NOX_INDEX, + "NOx Index", "", event, &sensors_event_t::nox_index, + sensorsReturningFalse, retries); + + // VOC-index sensor + sensorEventRead(iter, curTime, &msgi2cResponse, + &WipperSnapper_I2C_Driver::getEventVOCIndex, + &WipperSnapper_I2C_Driver::getSensorVOCIndexPeriod, + &WipperSnapper_I2C_Driver::getSensorVOCIndexPeriodPrv, + &WipperSnapper_I2C_Driver::setSensorVOCIndexPeriodPrv, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_VOC_INDEX, + "VOC Index", "", event, &sensors_event_t::voc_index, + sensorsReturningFalse, retries); + + // Proximity sensor -- sends using event.data[0] same as raw sensor_type + sensorEventRead(iter, curTime, &msgi2cResponse, + &WipperSnapper_I2C_Driver::getEventProximity, + &WipperSnapper_I2C_Driver::sensorProximityPeriod, + &WipperSnapper_I2C_Driver::SensorProximityPeriodPrv, + &WipperSnapper_I2C_Driver::setSensorProximityPeriodPrv, + wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PROXIMITY, + "Proximity", "", event, nullptr, sensorsReturningFalse, + retries); + + // Did this driver obtain data from sensors? + if (msgi2cResponse.payload.resp_i2c_device_event.sensor_event_count == + 0) { + continue; } - } - // OBJECT_TEMPERATURE sensor (°C) - curTime = millis(); - if ((*iter)->getSensorObjectTempPeriod() != 0L && - curTime - (*iter)->getSensorObjectTempPeriodPrv() > - (*iter)->getSensorObjectTempPeriod()) { - if ((*iter)->getEventObjectTemp(&event)) { - WS_DEBUG_PRINT("Sensor 0x"); - WS_DEBUG_PRINTHEX((*iter)->getI2CAddress()); - WS_DEBUG_PRINTLN(""); - WS_DEBUG_PRINT("\tTemperature: "); - WS_DEBUG_PRINT(event.temperature); - WS_DEBUG_PRINTLN("°C"); - - // pack event data into msg - fillEventMessage( - &msgi2cResponse, event.temperature, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_OBJECT_TEMPERATURE); - - (*iter)->setSensorObjectTempPeriodPrv(curTime); - } else { - WS_DEBUG_PRINTLN( - "ERROR: Failed to get object temperature sensor (°C) reading!"); - } - } + displayDeviceEventMessage(&msgi2cResponse, (*iter)->getI2CAddress()); - // OBJECT_TEMPERATURE sensor (°F) - curTime = millis(); - if ((*iter)->getSensorObjectTempFPeriod() != 0L && - curTime - (*iter)->getSensorObjectTempFPeriodPrv() > - (*iter)->getSensorObjectTempFPeriod()) { - if ((*iter)->getEventObjectTempF(&event)) { - WS_DEBUG_PRINT("Sensor 0x"); - WS_DEBUG_PRINTHEX((*iter)->getI2CAddress()); - WS_DEBUG_PRINTLN(""); - WS_DEBUG_PRINT("\tTemperature: "); - WS_DEBUG_PRINT(event.temperature); - WS_DEBUG_PRINTLN("°F"); - - // pack event data into msg - fillEventMessage( - &msgi2cResponse, event.temperature, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_OBJECT_TEMPERATURE_FAHRENHEIT); - - (*iter)->setSensorObjectTempFPeriodPrv(curTime); - } else { - WS_DEBUG_PRINTLN( - "ERROR: Failed to get object temperature sensor (°F) reading!"); - } - } - - // RELATIVE_HUMIDITY sensor - curTime = millis(); - if ((*iter)->getSensorRelativeHumidityPeriod() != 0L && - curTime - (*iter)->getSensorRelativeHumidityPeriodPrv() > - (*iter)->getSensorRelativeHumidityPeriod()) { - if ((*iter)->getEventRelativeHumidity(&event)) { - WS_DEBUG_PRINT("Sensor 0x"); - WS_DEBUG_PRINTHEX((*iter)->getI2CAddress()); - WS_DEBUG_PRINTLN(""); - WS_DEBUG_PRINT("\tHumidity: "); - WS_DEBUG_PRINT(event.relative_humidity); - WS_DEBUG_PRINTLN("%RH"); - - // pack event data into msg - fillEventMessage( - &msgi2cResponse, event.relative_humidity, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_RELATIVE_HUMIDITY); - - (*iter)->setSensorRelativeHumidityPeriodPrv(curTime); - } else { - WS_DEBUG_PRINTLN("ERROR: Failed to get humidity sensor reading!"); - } - } - - // PRESSURE sensor - curTime = millis(); - if ((*iter)->getSensorPressurePeriod() != 0L && - curTime - (*iter)->getSensorPressurePeriodPrv() > - (*iter)->getSensorPressurePeriod()) { - if ((*iter)->getEventPressure(&event)) { - WS_DEBUG_PRINT("Sensor 0x"); - WS_DEBUG_PRINTHEX((*iter)->getI2CAddress()); - WS_DEBUG_PRINTLN(""); - WS_DEBUG_PRINT("\tPressure: "); - WS_DEBUG_PRINT(event.pressure); - WS_DEBUG_PRINTLN(" hPa"); - - // pack event data into msg - fillEventMessage(&msgi2cResponse, event.pressure, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PRESSURE); - - (*iter)->setSensorPressurePeriodPrv(curTime); - } else { - WS_DEBUG_PRINTLN("ERROR: Failed to get Pressure sensor reading!"); + // Encode and publish I2CDeviceEvent message + if (!encodePublishI2CDeviceEventMsg(&msgi2cResponse, + (*iter)->getI2CAddress())) { + WS_DEBUG_PRINTLN("ERROR: Failed to encode and publish I2CDeviceEvent!"); + continue; } - } - - // CO2 sensor - curTime = millis(); - if ((*iter)->getSensorCO2Period() != 0L && - curTime - (*iter)->getSensorCO2PeriodPrv() > - (*iter)->getSensorCO2Period()) { - if ((*iter)->getEventCO2(&event)) { - WS_DEBUG_PRINT("Sensor 0x"); - WS_DEBUG_PRINTHEX((*iter)->getI2CAddress()); - WS_DEBUG_PRINTLN(""); - WS_DEBUG_PRINT("\tCO2: "); - WS_DEBUG_PRINT(event.CO2); - WS_DEBUG_PRINTLN(" ppm"); - - fillEventMessage(&msgi2cResponse, event.CO2, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_CO2); - (*iter)->setSensorCO2PeriodPrv(curTime); - } else { - WS_DEBUG_PRINTLN("ERROR: Failed to obtain CO2 sensor reading!"); - } - } - - // eCO2 sensor - curTime = millis(); - if ((*iter)->getSensorECO2Period() != 0L && - curTime - (*iter)->getSensorECO2PeriodPrv() > - (*iter)->getSensorECO2Period()) { - if ((*iter)->getEventECO2(&event)) { - WS_DEBUG_PRINT("Sensor 0x"); - WS_DEBUG_PRINTHEX((*iter)->getI2CAddress()); - WS_DEBUG_PRINTLN(""); - WS_DEBUG_PRINT("\teCO2: "); - WS_DEBUG_PRINT(event.eCO2); - WS_DEBUG_PRINTLN(" ppm"); - - fillEventMessage(&msgi2cResponse, event.eCO2, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_ECO2); - (*iter)->setSensorECO2PeriodPrv(curTime); - } else { - WS_DEBUG_PRINTLN("ERROR: Failed to obtain eCO2 sensor reading!"); - } - } - - // TVOC sensor - curTime = millis(); - if ((*iter)->getSensorTVOCPeriod() != 0L && - curTime - (*iter)->getSensorTVOCPeriodPrv() > - (*iter)->getSensorTVOCPeriod()) { - if ((*iter)->getEventTVOC(&event)) { - WS_DEBUG_PRINT("Sensor 0x"); - WS_DEBUG_PRINTHEX((*iter)->getI2CAddress()); - WS_DEBUG_PRINTLN(""); - WS_DEBUG_PRINT("\tTVOC: "); - WS_DEBUG_PRINT(event.tvoc); - WS_DEBUG_PRINTLN(" ppb"); - - fillEventMessage(&msgi2cResponse, event.tvoc, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_TVOC); - (*iter)->setSensorTVOCPeriodPrv(curTime); - } else { - WS_DEBUG_PRINTLN("ERROR: Failed to obtain TVOC sensor reading!"); - } - } - - // Altitude sensor - curTime = millis(); - if ((*iter)->getSensorAltitudePeriod() != 0L && - curTime - (*iter)->getSensorAltitudePeriodPrv() > - (*iter)->getSensorAltitudePeriod()) { - if ((*iter)->getEventAltitude(&event)) { - WS_DEBUG_PRINT("Sensor 0x"); - WS_DEBUG_PRINTHEX((*iter)->getI2CAddress()); - WS_DEBUG_PRINTLN(""); - WS_DEBUG_PRINT("\tAltitude: "); - WS_DEBUG_PRINT(event.altitude); - WS_DEBUG_PRINTLN(" m"); - - // pack event data into msg - fillEventMessage(&msgi2cResponse, event.altitude, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_ALTITUDE); - - (*iter)->setSensorAltitudePeriodPrv(curTime); - } else { - WS_DEBUG_PRINTLN("ERROR: Failed to get altitude sensor reading!"); - } - } - - // Light sensor - curTime = millis(); - if ((*iter)->getSensorLightPeriod() != 0L && - curTime - (*iter)->getSensorLightPeriodPrv() > - (*iter)->getSensorLightPeriod()) { - if ((*iter)->getEventLight(&event)) { - WS_DEBUG_PRINT("Sensor 0x"); - WS_DEBUG_PRINTHEX((*iter)->getI2CAddress()); - WS_DEBUG_PRINTLN(""); - WS_DEBUG_PRINT("\tLight: "); - WS_DEBUG_PRINT(event.light); - WS_DEBUG_PRINTLN(" lux"); - - // pack event data into msg - fillEventMessage(&msgi2cResponse, event.light, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_LIGHT); - - (*iter)->setSensorLightPeriodPrv(curTime); - } else { - WS_DEBUG_PRINTLN("ERROR: Failed to get light sensor reading!"); - } - } - - // PM10_STD sensor - curTime = millis(); - if ((*iter)->getSensorPM10_STDPeriod() != 0L && - curTime - (*iter)->getSensorPM10_STDPeriodPrv() > - (*iter)->getSensorPM10_STDPeriod()) { - if ((*iter)->getEventPM10_STD(&event)) { - WS_DEBUG_PRINT("Sensor 0x"); - WS_DEBUG_PRINTHEX((*iter)->getI2CAddress()); - WS_DEBUG_PRINTLN(""); - WS_DEBUG_PRINT("\tPM1.0: "); - WS_DEBUG_PRINT(event.pm10_std); - WS_DEBUG_PRINTLN(" ppm"); - - // pack event data into msg - fillEventMessage(&msgi2cResponse, event.pm10_std, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM10_STD); - } else { - WS_DEBUG_PRINTLN("ERROR: Failed to get PM1.0 sensor reading!"); - } - // try again in curTime seconds - (*iter)->setSensorPM10_STDPeriodPrv(curTime); - } - - // PM25_STD sensor - curTime = millis(); - if ((*iter)->getSensorPM25_STDPeriod() != 0L && - curTime - (*iter)->getSensorPM25_STDPeriodPrv() > - (*iter)->getSensorPM25_STDPeriod()) { - if ((*iter)->getEventPM25_STD(&event)) { - WS_DEBUG_PRINT("Sensor 0x"); - WS_DEBUG_PRINTHEX((*iter)->getI2CAddress()); - WS_DEBUG_PRINTLN(""); - WS_DEBUG_PRINT("\tPM2.5: "); - WS_DEBUG_PRINT(event.pm25_std); - WS_DEBUG_PRINTLN(" ppm"); - - // pack event data into msg - fillEventMessage(&msgi2cResponse, event.pm25_std, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM25_STD); - } else { - WS_DEBUG_PRINTLN("ERROR: Failed to get PM2.5 sensor reading!"); - } - // try again in curTime seconds - (*iter)->setSensorPM25_STDPeriodPrv(curTime); - } - - // PM100_STD sensor - curTime = millis(); - if ((*iter)->getSensorPM100_STDPeriod() != 0L && - curTime - (*iter)->getSensorPM100_STDPeriodPrv() > - (*iter)->getSensorPM100_STDPeriod()) { - if ((*iter)->getEventPM100_STD(&event)) { - WS_DEBUG_PRINT("Sensor 0x"); - WS_DEBUG_PRINTHEX((*iter)->getI2CAddress()); - WS_DEBUG_PRINTLN(""); - WS_DEBUG_PRINT("\tPM10.0: "); - WS_DEBUG_PRINT(event.pm25_std); - WS_DEBUG_PRINTLN(" ppm"); - - // pack event data into msg - fillEventMessage(&msgi2cResponse, event.pm25_std, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PM100_STD); - } else { - WS_DEBUG_PRINTLN("ERROR: Failed to get PM10.0 sensor reading!"); - } - (*iter)->setSensorPM100_STDPeriodPrv( - curTime); // try again in curTime seconds - } - - // Voltage sensor - curTime = millis(); - if ((*iter)->getSensorVoltagePeriod() != 0L && - curTime - (*iter)->getSensorVoltagePeriodPrv() > - (*iter)->getSensorVoltagePeriod()) { - if ((*iter)->getEventVoltage(&event)) { - WS_DEBUG_PRINT("Sensor 0x"); - WS_DEBUG_PRINTHEX((*iter)->getI2CAddress()); - WS_DEBUG_PRINTLN(""); - WS_DEBUG_PRINT("\tVoltage: "); - WS_DEBUG_PRINT(event.voltage); - WS_DEBUG_PRINTLN(" v"); - - // pack event data into msg - fillEventMessage(&msgi2cResponse, event.voltage, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_VOLTAGE); - } else { - WS_DEBUG_PRINTLN("ERROR: Failed to get voltage sensor reading!"); - } - // try again in curTime seconds - (*iter)->setSensorVoltagePeriodPrv(curTime); - } - - // Current sensor - curTime = millis(); - if ((*iter)->getSensorCurrentPeriod() != 0L && - curTime - (*iter)->getSensorCurrentPeriodPrv() > - (*iter)->getSensorCurrentPeriod()) { - if ((*iter)->getEventCurrent(&event)) { - WS_DEBUG_PRINT("Sensor 0x"); - WS_DEBUG_PRINTHEX((*iter)->getI2CAddress()); - WS_DEBUG_PRINTLN(""); - WS_DEBUG_PRINT("\tCurrent: "); - WS_DEBUG_PRINT(event.current); - WS_DEBUG_PRINTLN(" mA"); - - // pack event data into msg - fillEventMessage(&msgi2cResponse, event.current, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_CURRENT); - } else { - WS_DEBUG_PRINTLN("ERROR: Failed to get Current sensor reading!"); - } - // try again in curTime seconds - (*iter)->setSensorCurrentPeriodPrv(curTime); - } - - // Unitless % sensor - curTime = millis(); - if ((*iter)->getSensorUnitlessPercentPeriod() != 0L && - curTime - (*iter)->getSensorUnitlessPercentPeriodPrv() > - (*iter)->getSensorUnitlessPercentPeriod()) { - if ((*iter)->getEventUnitlessPercent(&event)) { - WS_DEBUG_PRINT("Sensor 0x"); - WS_DEBUG_PRINTHEX((*iter)->getI2CAddress()); - WS_DEBUG_PRINTLN(""); - WS_DEBUG_PRINT("\tRead: "); - WS_DEBUG_PRINT(event.unitless_percent); - WS_DEBUG_PRINTLN(" %"); - - // pack event data into msg - fillEventMessage( - &msgi2cResponse, event.unitless_percent, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_UNITLESS_PERCENT); - } else { - WS_DEBUG_PRINTLN( - "ERROR: Failed to get unitless percent sensor reading!"); - } - // try again in curTime seconds - (*iter)->setSensorUnitlessPercentPeriodPrv(curTime); - } - - // Raw sensor - curTime = millis(); - if ((*iter)->getSensorRawPeriod() != 0L && - curTime - (*iter)->getSensorRawPeriodPrv() > - (*iter)->getSensorRawPeriod()) { - if ((*iter)->getEventRaw(&event)) { - WS_DEBUG_PRINT("Sensor 0x"); - WS_DEBUG_PRINTHEX((*iter)->getI2CAddress()); - WS_DEBUG_PRINTLN(""); - WS_DEBUG_PRINT("\tRaw: "); - WS_DEBUG_PRINTLN(event.data[0]); - - fillEventMessage(&msgi2cResponse, event.data[0], - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_RAW); - } else { - WS_DEBUG_PRINTLN("ERROR: Failed to obtain Raw sensor reading!"); - } - (*iter)->setSensorRawPeriodPrv(curTime); - } - - // Gas sensor - curTime = millis(); - if ((*iter)->getSensorGasResistancePeriod() != 0L && - curTime - (*iter)->getSensorGasResistancePeriodPrv() > - (*iter)->getSensorGasResistancePeriod()) { - if ((*iter)->getEventGasResistance(&event)) { - WS_DEBUG_PRINT("Sensor 0x"); - WS_DEBUG_PRINTHEX((*iter)->getI2CAddress()); - WS_DEBUG_PRINTLN(""); - WS_DEBUG_PRINT("\tGas Resistance: "); - WS_DEBUG_PRINT(event.gas_resistance); - WS_DEBUG_PRINT(" ohms"); - - fillEventMessage( - &msgi2cResponse, event.gas_resistance, - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_GAS_RESISTANCE); - } else { - WS_DEBUG_PRINTLN( - "ERROR: Failed to obtain gas resistance sensor reading!"); - } - (*iter)->setSensorGasResistancePeriodPrv(curTime); - } - - // NOx-index sensor - curTime = millis(); - if ((*iter)->getSensorNOxIndexPeriod() != 0L && - curTime - (*iter)->getSensorNOxIndexPeriodPrv() > - (*iter)->getSensorNOxIndexPeriod()) { - if ((*iter)->getEventNOxIndex(&event)) { - WS_DEBUG_PRINT("Sensor 0x"); - WS_DEBUG_PRINTHEX((*iter)->getI2CAddress()); - WS_DEBUG_PRINTLN(""); - WS_DEBUG_PRINT("\tNOx Index: "); - WS_DEBUG_PRINT(event.nox_index); - - fillEventMessage(&msgi2cResponse, event.data[0], - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_NOX_INDEX); - } else { - WS_DEBUG_PRINTLN("ERROR: Failed to obtain NOx index sensor reading!"); - } - (*iter)->setSensorNOxIndexPeriodPrv(curTime); - } + } // end of retry loop + } +} - // VOC-index sensor - curTime = millis(); - if ((*iter)->getSensorVOCIndexPeriod() != 0L && - curTime - (*iter)->getSensorVOCIndexPeriodPrv() > - (*iter)->getSensorVOCIndexPeriod()) { - if ((*iter)->getEventVOCIndex(&event)) { - WS_DEBUG_PRINT("Sensor 0x"); - WS_DEBUG_PRINTHEX((*iter)->getI2CAddress()); - WS_DEBUG_PRINTLN(""); - WS_DEBUG_PRINT("\tVOC Index: "); - WS_DEBUG_PRINT(event.voc_index); - - fillEventMessage(&msgi2cResponse, event.data[0], - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_VOC_INDEX); +/*******************************************************************************/ +/*! + @brief Reads a sensor event from an I2C device driver. + @param iter + An iterator pointing to the current I2C device driver. + @param curTime + The current time in milliseconds. + @param msgi2cResponse + A pointer to the I2CResponse message. + @param getEventFunc + A pointer to the I2C device driver's getEvent function. + @param getPeriodFunc + A pointer to the I2C device driver's getPeriod function. + @param getPeriodPrvFunc + A pointer to the I2C device driver's getPeriodPrv function. + @param setPeriodPrvFunc + A pointer to the I2C device driver's setPeriodPrv function. + @param sensorType + The type of sensor being read. + @param sensorName + The name of the sensor being read. + @param unit + The unit of measurement for the sensor. + @param event + A sensors_event_t struct. + @param valueMember + Pointer to sensors_event_t struct's value member unless data[0]. + @param sensorsReturningFalse + A boolean indicating if the sensor is returning false. + @param retries + The number of retries left for the sensor. +*/ +void WipperSnapper_Component_I2C::sensorEventRead( + std::vector::iterator &iter, + unsigned long curTime, wippersnapper_signal_v1_I2CResponse *msgi2cResponse, + bool (WipperSnapper_I2C_Driver::*getEventFunc)(sensors_event_t *), + long (WipperSnapper_I2C_Driver::*getPeriodFunc)(), + long (WipperSnapper_I2C_Driver::*getPeriodPrvFunc)(), + void (WipperSnapper_I2C_Driver::*setPeriodPrvFunc)(long), + wippersnapper_i2c_v1_SensorType sensorType, const char *sensorName, + const char *unit, sensors_event_t event, + float sensors_event_t::*valueMember, bool &sensorsReturningFalse, + int &retries) { + // sensorName used for prefix + error message, units is value suffix + curTime = millis(); + if (((*iter)->*getPeriodFunc)() != 0L && + curTime - ((*iter)->*getPeriodPrvFunc)() > ((*iter)->*getPeriodFunc)()) { + // within the period, read the sensor + if (((*iter)->*getEventFunc)(&event)) { + float value; + if (sensorType == wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_RAW || + sensorType == wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PROXIMITY) { + value = event.data[0]; } else { - WS_DEBUG_PRINTLN("ERROR: Failed to obtain VOC index sensor reading!"); + value = event.*valueMember; } - (*iter)->setSensorVOCIndexPeriodPrv(curTime); - } - // Proximity sensor - curTime = millis(); - if ((*iter)->sensorProximityPeriod() != 0L && - curTime - (*iter)->SensorProximityPeriodPrv() > - (*iter)->sensorProximityPeriod()) { - if ((*iter)->getEventProximity(&event)) { - WS_DEBUG_PRINT("Sensor 0x"); - WS_DEBUG_PRINTHEX((*iter)->getI2CAddress()); - WS_DEBUG_PRINTLN(""); - WS_DEBUG_PRINT("\tProximity: "); - WS_DEBUG_PRINT(event.data[0]); - - // pack event data into msg - fillEventMessage(&msgi2cResponse, event.data[0], - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_PROXIMITY); - - (*iter)->setSensorProximityPeriodPrv(curTime); - } else { - WS_DEBUG_PRINTLN("ERROR: Failed to get proximity sensor reading!"); + WS_DEBUG_PRINT("Sensor 0x"); + WS_DEBUG_PRINTHEX((*iter)->getI2CAddress()); + WS_DEBUG_PRINTLN(""); + WS_DEBUG_PRINT("\t"); + WS_DEBUG_PRINT(sensorName); + WS_DEBUG_PRINT(": "); + WS_DEBUG_PRINT(value); + WS_DEBUG_PRINTLN(unit); + + // pack event data into msg + fillEventMessage(msgi2cResponse, value, sensorType); + + ((*iter)->*setPeriodPrvFunc)(curTime); + } else { + WS_DEBUG_PRINT("ERROR: Failed to get "); + WS_DEBUG_PRINT(sensorName); + WS_DEBUG_PRINTLN(" reading!"); + sensorsReturningFalse = true; + if (retries == 1) { + ((*iter)->*setPeriodPrvFunc)(curTime); } } - - // Did this driver obtain data from sensors? - if (msgi2cResponse.payload.resp_i2c_device_event.sensor_event_count == 0) - continue; - - displayDeviceEventMessage(&msgi2cResponse, (*iter)->getI2CAddress()); - - // Encode and publish I2CDeviceEvent message - if (!encodePublishI2CDeviceEventMsg(&msgi2cResponse, - (*iter)->getI2CAddress())) { - WS_DEBUG_PRINTLN("ERROR: Failed to encode and publish I2CDeviceEvent!"); - continue; - } } } diff --git a/src/components/i2c/WipperSnapper_I2C.h b/src/components/i2c/WipperSnapper_I2C.h index 0c0fc8305..58cd5c5fc 100644 --- a/src/components/i2c/WipperSnapper_I2C.h +++ b/src/components/i2c/WipperSnapper_I2C.h @@ -28,7 +28,9 @@ #include "drivers/WipperSnapper_I2C_Driver_BMP280.h" #include "drivers/WipperSnapper_I2C_Driver_BMP3XX.h" #include "drivers/WipperSnapper_I2C_Driver_DPS310.h" +#include "drivers/WipperSnapper_I2C_Driver_DS2484.h" #include "drivers/WipperSnapper_I2C_Driver_ENS160.h" +#include "drivers/WipperSnapper_I2C_Driver_HDC302X.h" #include "drivers/WipperSnapper_I2C_Driver_HTS221.h" #include "drivers/WipperSnapper_I2C_Driver_HTU21D.h" #include "drivers/WipperSnapper_I2C_Driver_HTU31D.h" @@ -40,10 +42,12 @@ #include "drivers/WipperSnapper_I2C_Driver_LTR329_LTR303.h" #include "drivers/WipperSnapper_I2C_Driver_LTR390.h" #include "drivers/WipperSnapper_I2C_Driver_MAX17048.h" +#include "drivers/WipperSnapper_I2C_Driver_MCP3421.h" #include "drivers/WipperSnapper_I2C_Driver_MCP9808.h" #include "drivers/WipperSnapper_I2C_Driver_MPL115A2.h" #include "drivers/WipperSnapper_I2C_Driver_MPRLS.h" #include "drivers/WipperSnapper_I2C_Driver_MS8607.h" +#include "drivers/WipperSnapper_I2C_Driver_NAU7802.h" #include "drivers/WipperSnapper_I2C_Driver_PCT2075.h" #include "drivers/WipperSnapper_I2C_Driver_PM25.h" #include "drivers/WipperSnapper_I2C_Driver_SCD30.h" @@ -63,6 +67,8 @@ #include "drivers/WipperSnapper_I2C_Driver_VEML7700.h" #include "drivers/WipperSnapper_I2C_Driver_VL53L0X.h" #include "drivers/WipperSnapper_I2C_Driver_VL53L1X.h" +#include "drivers/WipperSnapper_I2C_Driver_VL53L4CD.h" +#include "drivers/WipperSnapper_I2C_Driver_VL53L4CX.h" #include "drivers/WipperSnapper_I2C_Driver_VL6180X.h" #define I2C_TIMEOUT_MS 50 ///< Default I2C timeout, in milliseconds. @@ -93,6 +99,20 @@ class WipperSnapper_Component_I2C { wippersnapper_i2c_v1_I2CDeviceDeinitRequest *msgDeviceDeinitReq); void update(); + + void sensorEventRead( + std::vector::iterator &iter, + unsigned long curTime, + wippersnapper_signal_v1_I2CResponse *msgi2cResponse, + bool (WipperSnapper_I2C_Driver::*getEventFunc)(sensors_event_t *), + long (WipperSnapper_I2C_Driver::*getPeriodFunc)(), + long (WipperSnapper_I2C_Driver::*getPeriodPrvFunc)(), + void (WipperSnapper_I2C_Driver::*setPeriodPrvFunc)(long), + wippersnapper_i2c_v1_SensorType sensorType, const char *sensorName, + const char *unit, sensors_event_t event, + float sensors_event_t::*valueMember, bool &sensorsReturningFalse, + int &retries); + void fillEventMessage(wippersnapper_signal_v1_I2CResponse *msgi2cResponse, float value, wippersnapper_i2c_v1_SensorType sensorType); @@ -114,6 +134,7 @@ class WipperSnapper_Component_I2C { // Sensor driver objects WipperSnapper_I2C_Driver_AHTX0 *_ahtx0 = nullptr; WipperSnapper_I2C_Driver_DPS310 *_dps310 = nullptr; + WipperSnapper_I2C_Driver_DS2484 *_ds2484 = nullptr; WipperSnapper_I2C_Driver_ENS160 *_ens160 = nullptr; WipperSnapper_I2C_Driver_SCD30 *_scd30 = nullptr; WipperSnapper_I2C_Driver_BH1750 *_bh1750 = nullptr; @@ -121,16 +142,19 @@ class WipperSnapper_Component_I2C { WipperSnapper_I2C_Driver_BMP280 *_bmp280 = nullptr; WipperSnapper_I2C_Driver_BMP3XX *_bmp3xx = nullptr; WipperSnapper_I2C_Driver_BME680 *_bme680 = nullptr; + WipperSnapper_I2C_Driver_HDC302X *_hdc302x = nullptr; WipperSnapper_I2C_Driver_HTS221 *_hts221 = nullptr; WipperSnapper_I2C_Driver_HTU21D *_htu21d = nullptr; WipperSnapper_I2C_Driver_HTU31D *_htu31d = nullptr; WipperSnapper_I2C_Driver_INA219 *_ina219 = nullptr; WipperSnapper_I2C_Driver_LTR329_LTR303 *_ltr329 = nullptr; WipperSnapper_I2C_Driver_LTR390 *_ltr390 = nullptr; + WipperSnapper_I2C_Driver_MCP3421 *_mcp3421 = nullptr; WipperSnapper_I2C_Driver_MCP9808 *_mcp9808 = nullptr; WipperSnapper_I2C_Driver_MPL115A2 *_mpl115a2 = nullptr; WipperSnapper_I2C_Driver_MPRLS *_mprls = nullptr; WipperSnapper_I2C_Driver_MS8607 *_ms8607 = nullptr; + WipperSnapper_I2C_Driver_NAU7802 *_nau7802 = nullptr; WipperSnapper_I2C_Driver_TMP117 *_tmp117 = nullptr; WipperSnapper_I2C_Driver_TSL2591 *_tsl2591 = nullptr; WipperSnapper_I2C_Driver_VCNL4020 *_vcnl4020 = nullptr; @@ -153,6 +177,8 @@ class WipperSnapper_Component_I2C { WipperSnapper_I2C_Driver_STEMMA_Soil_Sensor *_ss = nullptr; WipperSnapper_I2C_Driver_VL53L0X *_vl53l0x = nullptr; WipperSnapper_I2C_Driver_VL53L1X *_vl53l1x = nullptr; + WipperSnapper_I2C_Driver_VL53L4CD *_vl53l4cd = nullptr; + WipperSnapper_I2C_Driver_VL53L4CX *_vl53l4cx = nullptr; WipperSnapper_I2C_Driver_VL6180X *_vl6180x = nullptr; WipperSnapper_I2C_Driver_MAX17048 *_max17048 = nullptr; WipperSnapper_I2C_Driver_ADT7410 *_adt7410 = nullptr; diff --git a/src/components/i2c/drivers/WipperSnapper_I2C_Driver_DS2484.h b/src/components/i2c/drivers/WipperSnapper_I2C_Driver_DS2484.h new file mode 100644 index 000000000..ed276ab9d --- /dev/null +++ b/src/components/i2c/drivers/WipperSnapper_I2C_Driver_DS2484.h @@ -0,0 +1,176 @@ +/*! + * @file WipperSnapper_I2C_Driver_DS2484.h + * + * Device driver the DS2484 I2C OneWire converter (hosting a DS18b20). + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Tyeth Gundry 2024 for Adafruit Industries. + * + * MIT license, all text here must be included in any redistribution. + * + */ + +#ifndef WipperSnapper_I2C_Driver_DS2484_H +#define WipperSnapper_I2C_Driver_DS2484_H + +#define DS18B20_FAMILY_CODE 0x28 ///< DS18B20 family code +#define DS18B20_CMD_CONVERT_T 0x44 ///< Convert T command +#define DS18B20_CMD_MATCH_ROM 0x55 ///< Match ROM command +#define DS18B20_CMD_READ_SCRATCHPAD 0xBE ///< Read Scratchpad command + +#include "WipperSnapper_I2C_Driver.h" +#include + +/**************************************************************************/ +/*! + @brief Class that provides a sensor driver for the DS2484 I2C OneWire + converter hosting a DS18b20 temperature sensor. +*/ +/**************************************************************************/ +class WipperSnapper_I2C_Driver_DS2484 : public WipperSnapper_I2C_Driver { + +public: + /*******************************************************************************/ + /*! + @brief Constructor for a DS2484 sensor. + @param i2c + The I2C interface. + @param sensorAddress + 7-bit device address. + */ + /*******************************************************************************/ + WipperSnapper_I2C_Driver_DS2484(TwoWire *i2c, uint16_t sensorAddress) + : WipperSnapper_I2C_Driver(i2c, sensorAddress) { + _i2c = i2c; + _sensorAddress = sensorAddress; + } + + /*******************************************************************************/ + /*! + @brief Destructor for an DS2484 sensor. + */ + /*******************************************************************************/ + ~WipperSnapper_I2C_Driver_DS2484() { delete _ds2484; } + + /*******************************************************************************/ + /*! + @brief Initializes the DS2484 sensor and begins I2C. + @returns True if initialized successfully, False otherwise. + */ + /*******************************************************************************/ + bool begin() { + // initialize DS2484 + _ds2484 = new Adafruit_DS248x(); + if (!_ds2484->begin(_i2c, (uint8_t)_sensorAddress)) { + WS_DEBUG_PRINTLN("Could not find DS2484"); + return false; + } + + // check bus is okay + if (!_ds2484->OneWireReset()) { + WS_DEBUG_PRINTLN("Failed to do a OneWire bus reset"); + if (_ds2484->shortDetected()) { + WS_DEBUG_PRINTLN("\tShort detected"); + } + if (!_ds2484->presencePulseDetected()) { + WS_DEBUG_PRINTLN("\tNo presense pulse"); + } + return false; + } + + // locate first DS18B20 + bool found_device = false; + _ds2484->OneWireReset(); + _ds2484->OneWireSearchReset(); + while (!found_device && _ds2484->OneWireSearch(_rom)) { + if (_rom[0] == DS18B20_FAMILY_CODE) { + found_device = true; + } else { + WS_DEBUG_PRINT("Found unwanted device with family code: 0x"); + WS_DEBUG_PRINTHEX(_rom[0]); + WS_DEBUG_PRINTLN(" expected 0x28"); // DS18B20_FAMILY_CODE + } + } + + if (!found_device) { + WS_DEBUG_PRINTLN("Could not find DS18B20 attached to DS2484"); + return false; + } + + WS_DEBUG_PRINTLN("DS2484 found"); + return true; + } + + /*******************************************************************************/ + /*! + @brief Processes a temperature event. + @param tempEvent + Pointer to an Adafruit_Sensor event. + @returns True if the temperature was obtained successfully, False + otherwise. + */ + bool processTemperatureEvent(sensors_event_t *tempEvent) { + if (!_ds2484->OneWireReset()) { + WS_DEBUG_PRINTLN("Failed to do a OneWire bus reset"); + return false; + } + if (!_ds2484->presencePulseDetected()) { + tempEvent->temperature = NAN; + return true; + } + + _ds2484->OneWireWriteByte(DS18B20_CMD_MATCH_ROM); // Match ROM command + for (int i = 0; i < 8; i++) { + _ds2484->OneWireWriteByte(_rom[i]); + } + + // Start temperature conversion + _ds2484->OneWireWriteByte(DS18B20_CMD_CONVERT_T); // Convert T command + delay(750); // Wait for conversion (750ms for maximum precision) + + // Read scratchpad + if (!_ds2484->OneWireReset()) { + WS_DEBUG_PRINTLN( + "Failed to do a OneWire bus reset after starting temp conversion"); + return false; + } + _ds2484->OneWireWriteByte(DS18B20_CMD_MATCH_ROM); // Match ROM command + for (int i = 0; i < 8; i++) { + _ds2484->OneWireWriteByte(_rom[i]); + } + _ds2484->OneWireWriteByte( + DS18B20_CMD_READ_SCRATCHPAD); // Read Scratchpad command + + uint8_t data[9]; + for (int i = 0; i < sizeof(data) / sizeof(data[0]); i++) { + _ds2484->OneWireReadByte(&data[i]); + } + + // Calculate temperature + int16_t raw = (data[1] << 8) | data[0]; + tempEvent->temperature = (float)raw / 16.0; + return true; + } + + /*******************************************************************************/ + /*! + @brief Gets the DS2484's current temperature. + @param tempEvent + Pointer to an Adafruit_Sensor event. + @returns True if the temperature was obtained successfully, False + otherwise. + */ + /*******************************************************************************/ + bool getEventAmbientTemp(sensors_event_t *tempEvent) { + return processTemperatureEvent(tempEvent); + } + +protected: + Adafruit_DS248x *_ds2484; ///< DS2484 driver object + uint8_t _rom[8]; ///< DS18B20 ROM +}; + +#endif // WipperSnapper_I2C_Driver_DS2484 \ No newline at end of file diff --git a/src/components/i2c/drivers/WipperSnapper_I2C_Driver_HDC302X.h b/src/components/i2c/drivers/WipperSnapper_I2C_Driver_HDC302X.h new file mode 100644 index 000000000..ec4323fd3 --- /dev/null +++ b/src/components/i2c/drivers/WipperSnapper_I2C_Driver_HDC302X.h @@ -0,0 +1,130 @@ +/*! + * @file WipperSnapper_I2C_Driver_HDC302X.h + * + * Device driver for an HDC302X Humidity and Temperature sensor. + */ + +#ifndef WipperSnapper_I2C_Driver_HDC302X_H +#define WipperSnapper_I2C_Driver_HDC302X_H + +#include "WipperSnapper_I2C_Driver.h" +#include + +/**************************************************************************/ +/*! + @brief Class that provides a sensor driver for the HDC302X humidity and + temperature sensor. This implementation uses the 1 Hz data rate. +*/ +/**************************************************************************/ +class WipperSnapper_I2C_Driver_HDC302X : public WipperSnapper_I2C_Driver { + +public: + /*******************************************************************************/ + /*! + @brief Constructor for an HDC302X sensor. + @param i2c + The I2C interface. + @param sensorAddress + 7-bit device address. + */ + /*******************************************************************************/ + WipperSnapper_I2C_Driver_HDC302X(TwoWire *i2c, uint16_t sensorAddress) + : WipperSnapper_I2C_Driver(i2c, sensorAddress) { + _i2c = i2c; + _sensorAddress = sensorAddress; + } + + /*******************************************************************************/ + /*! + @brief Destructor for an HDC302X sensor. + */ + /*******************************************************************************/ + ~WipperSnapper_I2C_Driver_HDC302X() { delete _hdc302x; } + + /*******************************************************************************/ + /*! + @brief Initializes the HDC302X sensor and begins I2C. + @returns True if initialized successfully, False otherwise. + + */ + /*******************************************************************************/ + bool begin() { + // attempt to initialize the HDC302X using the I2C interface + _hdc302x = new Adafruit_HDC302x(); + if (!_hdc302x->begin(_sensorAddress, _i2c)) + return false; + + // set the HDC302X's data rate to 1 Hz lowest noise + _hdc302x->setAutoMode(EXIT_AUTO_MODE); + // discard first reading (It returned -45c for me once) + _hdc302x->readTemperatureHumidityOnDemand(_temp, _humidity, + TRIGGERMODE_LP0); + return true; + } + + /*******************************************************************************/ + /*! + @brief Reads the HDC302X's temperature and humidity data. + @returns True if the data was read successfully, False otherwise. + */ + /*******************************************************************************/ + bool readSensorData() { + uint16_t status = _hdc302x->readStatus(); + if (status & 0x0010) { + WS_DEBUG_PRINTLN(F("Device Reset Detected")); + return false; + } + + if (status & 0x0001) { + WS_DEBUG_PRINTLN( + F("Checksum Verification Fail (incorrect checksum received)")); + return false; + } + + if (!_hdc302x->readTemperatureHumidityOnDemand(_temp, _humidity, + TRIGGERMODE_LP0)) { + WS_DEBUG_PRINTLN(F("Failed to read temperature and humidity.")); + return false; + } + return true; + } + + /*******************************************************************************/ + /*! + @brief Gets the HDC302X's current temperature. + @param tempEvent + Pointer to an Adafruit_Sensor event. + @returns True if the temperature was obtained successfully, False + otherwise. + */ + /*******************************************************************************/ + bool getEventAmbientTemp(sensors_event_t *tempEvent) { + if (readSensorData() == false) + return false; + tempEvent->temperature = _temp; + return true; + } + + /*******************************************************************************/ + /*! + @brief Gets the HDC302X's current humidity. + @param humidEvent + Pointer to an Adafruit_Sensor event. + @returns True if the humidity was obtained successfully, False + otherwise. + */ + /*******************************************************************************/ + bool getEventRelativeHumidity(sensors_event_t *humidEvent) { + if (readSensorData() == false) + return false; + humidEvent->relative_humidity = _humidity; + return true; + } + +protected: + Adafruit_HDC302x *_hdc302x; ///< Pointer to an HDC302X object + double _temp = 0.0; ///< Holds data for the HDC302X's temperature sensor + double _humidity = 0.0; ///< Holds data for the HDC302X's humidity sensor +}; + +#endif // WipperSnapper_I2C_Driver_HDC302X \ No newline at end of file diff --git a/src/components/i2c/drivers/WipperSnapper_I2C_Driver_MCP3421.h b/src/components/i2c/drivers/WipperSnapper_I2C_Driver_MCP3421.h new file mode 100644 index 000000000..6ad1641cf --- /dev/null +++ b/src/components/i2c/drivers/WipperSnapper_I2C_Driver_MCP3421.h @@ -0,0 +1,133 @@ +/*! + * @file WipperSnapper_I2C_Driver_MCP3421.h + * + * Device driver for the MCP3421 18-bit ADC sensor. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Tyeth Gundry 2024 for Adafruit Industries. + * + * MIT license, all text here must be included in any redistribution. + * + */ +#ifndef WipperSnapper_I2C_Driver_MCP3421_H +#define WipperSnapper_I2C_Driver_MCP3421_H + +#include "WipperSnapper_I2C_Driver.h" +#include "Wippersnapper.h" +#include + +/**************************************************************************/ +/*! + @brief Class that provides a driver interface for a MCP3421 sensor. +*/ +/**************************************************************************/ +class WipperSnapper_I2C_Driver_MCP3421 : public WipperSnapper_I2C_Driver { +public: + /*******************************************************************************/ + /*! + @brief Constructor for the MCP3421 sensor. + @param i2c + The I2C interface. + @param sensorAddress + 7-bit device address. + */ + /*******************************************************************************/ + WipperSnapper_I2C_Driver_MCP3421(TwoWire *i2c, uint16_t sensorAddress) + : WipperSnapper_I2C_Driver(i2c, sensorAddress) { + _i2c = i2c; + _sensorAddress = sensorAddress; + } + + /*******************************************************************************/ + /*! + @brief Destructor for an MCP3421 sensor. + */ + /*******************************************************************************/ + ~WipperSnapper_I2C_Driver_MCP3421() { delete _mcp3421; } + + /*******************************************************************************/ + /*! + @brief Initializes the MCP3421 sensor and begins I2C. + @returns True if initialized successfully, False otherwise. + */ + /*******************************************************************************/ + bool begin() { + _mcp3421 = new Adafruit_MCP3421(); + if (!_mcp3421->begin((uint8_t)_sensorAddress, _i2c)) { + WS_DEBUG_PRINTLN("Failed to find MCP3421 chip"); + return false; + } + + if (!configureSensor()) { + WS_DEBUG_PRINTLN("Failed to configure MCP3421 sensor"); + return false; + } + return true; + } + + /*******************************************************************************/ + /*! + @brief Configures the MCP3421 sensor. + @returns True if the sensor was configured successfully, False otherwise. + */ + /*******************************************************************************/ + bool configureSensor() { + // NOTE: We should allow the gain to be set in future, like resolution + // 12_BIT (240 SPS), 14_BIT (60 SPS), 16_BIT (15 SPS), 18_BIT (3.75 SPS) + _mcp3421->setResolution(RESOLUTION_18_BIT); + if (_mcp3421->getResolution() != RESOLUTION_18_BIT) { + WS_DEBUG_PRINTLN("Failed to set resolution to 18-bit"); + return false; + } + + _mcp3421->setGain(GAIN_8X); + if (_mcp3421->getGain() != GAIN_8X) { + WS_DEBUG_PRINTLN("Failed to set gain to 8x"); + return false; + } + + _mcp3421->setMode(MODE_ONE_SHOT); + if (_mcp3421->getMode() != MODE_ONE_SHOT) { + WS_DEBUG_PRINTLN("Failed to set mode to One-Shot"); + return false; + } + return true; + } + + /*******************************************************************************/ + /*! + @brief Reads the ADC sensor with short wait for data. + @param rawEvent + ADC sensor reading + @returns True if the sensor event was obtained successfully, False + otherwise. + */ + /*******************************************************************************/ + bool getEventRaw(sensors_event_t *rawEvent) { + ulong start = millis(); + if (!_mcp3421->startOneShotConversion()) { + WS_DEBUG_PRINTLN("Failed to start one-shot conversion"); + return false; + } + while (!_mcp3421->isReady()) { + ulong newMillis = millis(); + if (newMillis - start > 500) { + WS_DEBUG_PRINTLN("Timeout waiting for conversion to complete"); + return false; + } else if (start > newMillis) { + start = millis(); // rollover + } + delay(50); + } + rawEvent->data[0] = (float)_mcp3421->readADC(); + return true; + } + +protected: + Adafruit_MCP3421 *_mcp3421; ///< Pointer to MCP3421 sensor object +}; + +#endif // WipperSnapper_I2C_Driver_MCP3421 \ No newline at end of file diff --git a/src/components/i2c/drivers/WipperSnapper_I2C_Driver_NAU7802.h b/src/components/i2c/drivers/WipperSnapper_I2C_Driver_NAU7802.h new file mode 100644 index 000000000..85a6ffae5 --- /dev/null +++ b/src/components/i2c/drivers/WipperSnapper_I2C_Driver_NAU7802.h @@ -0,0 +1,141 @@ +/*! + * @file WipperSnapper_I2C_Driver_NAU7802.h + * + * Device driver for the NAU7802 24bit ADC / load cell breakout + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Tyeth Gundry for Adafruit Industries 2024 + * + * MIT license, all text here must be included in any redistribution. + * + */ + +#ifndef WipperSnapper_I2C_Driver_NAU7802_H +#define WipperSnapper_I2C_Driver_NAU7802_H + +#include "WipperSnapper_I2C_Driver.h" +#include + +#define NAU7802_TIMEOUT_MS 250 ///< Timeout waiting for data from NAU7802 + +/**************************************************************************/ +/*! + @brief Class that provides a driver interface for the NAU7802. +*/ +/**************************************************************************/ +class WipperSnapper_I2C_Driver_NAU7802 : public WipperSnapper_I2C_Driver { +public: + /*******************************************************************************/ + /*! + @brief Constructor for an NAU7802. + @param i2c + The I2C interface. + @param sensorAddress + 7-bit device address. + */ + /*******************************************************************************/ + WipperSnapper_I2C_Driver_NAU7802(TwoWire *i2c, uint16_t sensorAddress) + : WipperSnapper_I2C_Driver(i2c, sensorAddress) { + _i2c = i2c; + _sensorAddress = sensorAddress; + _nau7802 = new Adafruit_NAU7802(); + } + + /*******************************************************************************/ + /*! + @brief Destructor for an NAU7802. + */ + /*******************************************************************************/ + ~WipperSnapper_I2C_Driver_NAU7802() { _nau7802 = nullptr; } + + /*******************************************************************************/ + /*! + @brief Initializes the NAU7802 sensor and begins I2C. + @returns True if initialized successfully, False otherwise. + */ + /*******************************************************************************/ + bool begin() { return _nau7802->begin(_i2c) && configure_nau7802(); } + + /*******************************************************************************/ + /*! + @brief Configures the NAU7802 sensor. + @returns True if configured successfully, False otherwise. + */ + /*******************************************************************************/ + bool configure_nau7802() { + if (!_nau7802->setLDO(NAU7802_3V0)) { + WS_DEBUG_PRINTLN("Failed to set LDO to 3V0"); + return false; + } + + if (!_nau7802->setGain(NAU7802_GAIN_128)) { + WS_DEBUG_PRINTLN("Failed to set gain to 128"); + return false; + } + + if (!_nau7802->setRate(NAU7802_RATE_10SPS) && + !_nau7802->setRate(NAU7802_RATE_10SPS)) { + WS_DEBUG_PRINTLN("Failed to set sample rate to 10SPS"); + return false; + } + + // Take 10 readings to flush out old readings (10 samples per second) + flushNAU7802(10); + + for (int retries = 0; retries < 3; retries++) { + if (_nau7802->calibrate(NAU7802_CALMOD_INTERNAL)) { + WS_DEBUG_PRINTLN("Calibrated internal offset"); + return true; + } + WS_DEBUG_PRINTLN("Failed to calibrate internal offset, retrying!"); + delay(1000); + } + WS_DEBUG_PRINTLN("ERROR: Failed to calibrate internal offset of NAU7802."); + return false; + } + + /*******************************************************************************/ + /*! + @brief Gets datapoints from sensor and discards (flushes old data). + @param count + Number of readings to discard. + */ + /*******************************************************************************/ + void flushNAU7802(uint8_t count) { + for (uint8_t skipCounter = 0; skipCounter < count; skipCounter++) { + while (!_nau7802->available()) + delay(1); + _nau7802->read(); + } + } + + /*******************************************************************************/ + /*! + @brief Gets the sensor's raw "force" value. + @param rawEvent + Pointer to an Adafruit_Sensor event. + @returns True if the reading was obtained successfully, False otherwise. + */ + /*******************************************************************************/ + bool getEventRaw(sensors_event_t *rawEvent) { + unsigned long start = millis(); + + // Wait for the sensor to be ready + while (!_nau7802->available()) { + if (millis() - start > NAU7802_TIMEOUT_MS) { + WS_DEBUG_PRINTLN("NAU7802 data not available"); + return false; + } + } + rawEvent->data[0] = (float)_nau7802->read(); + return true; + } + +protected: + Adafruit_NAU7802 *_nau7802 = nullptr; ///< NAU7802 object +}; + +#endif // WipperSnapper_I2C_Driver_NAU7802_H \ No newline at end of file diff --git a/src/components/i2c/drivers/WipperSnapper_I2C_Driver_SCD30.h b/src/components/i2c/drivers/WipperSnapper_I2C_Driver_SCD30.h index cb7c409b5..fcaa1d94d 100644 --- a/src/components/i2c/drivers/WipperSnapper_I2C_Driver_SCD30.h +++ b/src/components/i2c/drivers/WipperSnapper_I2C_Driver_SCD30.h @@ -53,6 +53,56 @@ class WipperSnapper_I2C_Driver_SCD30 : public WipperSnapper_I2C_Driver { return _scd->begin((uint8_t)_sensorAddress, _i2c); } + /*******************************************************************************/ + /*! + @brief Checks if sensor was read within last 1s, or is the first read. + @returns True if the sensor was recently read, False otherwise. + */ + bool alreadyRecentlyRead() { + return (_lastRead != 0 && millis() - _lastRead) < 1000; + } + + /*******************************************************************************/ + /*! + @brief Checks if the sensor is ready to be read + @returns True if the sensor is ready, False otherwise. + */ + /*******************************************************************************/ + bool sensorReady() { + if (!_scd->dataReady()) { + // failed, one more quick attempt + delay(100); + if (!_scd->dataReady()) { + return false; + } + } + return true; + } + + /*******************************************************************************/ + /*! + @brief Reads the SCD30 sensor. + @returns True if the sensor was read successfully, False otherwise. + */ + /*******************************************************************************/ + bool readSensorData() { + // dont read sensor more than once per second + if (alreadyRecentlyRead()) { + return true; + } + + if (!sensorReady()) { + return false; + } + + if (_scd->getEvent(&_humidity, &_temperature)) { + return false; + } + _CO2.CO2 = _scd->CO2; + _lastRead = millis(); + return true; + } + /*******************************************************************************/ /*! @brief Gets the SCD30's current temperature. @@ -64,14 +114,11 @@ class WipperSnapper_I2C_Driver_SCD30 : public WipperSnapper_I2C_Driver { /*******************************************************************************/ bool getEventAmbientTemp(sensors_event_t *tempEvent) { // check if sensor is enabled and data is available - if (_tempSensorPeriod != 0 && (!_scd->dataReady())) - return false; - - // attempt to get temperature data - sensors_event_t humidEvent; - if (!_scd->getEvent(&humidEvent, tempEvent)) + if (!readSensorData()) { return false; + } + tempEvent = &_temperature; return true; } @@ -86,14 +133,11 @@ class WipperSnapper_I2C_Driver_SCD30 : public WipperSnapper_I2C_Driver { /*******************************************************************************/ bool getEventRelativeHumidity(sensors_event_t *humidEvent) { // check if sensor is enabled and data is available - if (_humidSensorPeriod != 0 && (!_scd->dataReady())) - return false; - - // attempt to get temperature data - sensors_event_t tempEvent; - if (!_scd->getEvent(humidEvent, &tempEvent)) + if (!readSensorData()) { return false; + } + humidEvent = &_humidity; return true; } @@ -108,15 +152,20 @@ class WipperSnapper_I2C_Driver_SCD30 : public WipperSnapper_I2C_Driver { /*******************************************************************************/ bool getEventCO2(sensors_event_t *co2Event) { // check if sensor is enabled and data is available - if (_CO2SensorPeriod != 0 && (!_scd->dataReady())) + if (!readSensorData()) { return false; + } - co2Event->CO2 = _scd->CO2; + co2Event = &_CO2; return true; } protected: - Adafruit_SCD30 *_scd; ///< SCD30 driver object + Adafruit_SCD30 *_scd = nullptr; ///< SCD30 driver object + ulong _lastRead = 0; ///< Last time the sensor was read + sensors_event_t _temperature; ///< Temperature + sensors_event_t _humidity; ///< Relative Humidity + sensors_event_t _CO2; ///< CO2 }; #endif // WipperSnapper_I2C_Driver_SCD30 \ No newline at end of file diff --git a/src/components/i2c/drivers/WipperSnapper_I2C_Driver_SGP30.h b/src/components/i2c/drivers/WipperSnapper_I2C_Driver_SGP30.h index 32da8a53a..664695191 100644 --- a/src/components/i2c/drivers/WipperSnapper_I2C_Driver_SGP30.h +++ b/src/components/i2c/drivers/WipperSnapper_I2C_Driver_SGP30.h @@ -44,11 +44,7 @@ class WipperSnapper_I2C_Driver_SGP30 : public WipperSnapper_I2C_Driver { /*******************************************************************************/ bool begin() { _sgp30 = new Adafruit_SGP30(); - bool isInit = _sgp30->begin(_i2c); - if (isInit) { - _sgp30->IAQinit(); - } - return isInit; + return _sgp30->begin(_i2c); } bool getEventECO2(sensors_event_t *senseEvent) { diff --git a/src/components/i2c/drivers/WipperSnapper_I2C_Driver_VL53L4CD.h b/src/components/i2c/drivers/WipperSnapper_I2C_Driver_VL53L4CD.h new file mode 100644 index 000000000..95c18d701 --- /dev/null +++ b/src/components/i2c/drivers/WipperSnapper_I2C_Driver_VL53L4CD.h @@ -0,0 +1,163 @@ +/*! + * @file WipperSnapper_I2C_Driver_VL53L4CD.h + * + * Device driver for the VL53L4CD ToF sensor. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) 2022 Tyeth Gundry for Adafruit Industries + * + * MIT license, all text here must be included in any redistribution. + * + */ +#ifndef WipperSnapper_I2C_Driver_VL53L4CD_H +#define WipperSnapper_I2C_Driver_VL53L4CD_H + +#include "WipperSnapper_I2C_Driver.h" +#include + +/**************************************************************************/ +/*! + @brief Class that provides a driver interface for a VL53L4CD sensor. +*/ +/**************************************************************************/ +class WipperSnapper_I2C_Driver_VL53L4CD : public WipperSnapper_I2C_Driver { +public: + /*******************************************************************************/ + /*! + @brief Constructor for a VL53L4CD sensor. + @param i2c + The I2C interface. + @param sensorAddress + 7-bit device address. + */ + /*******************************************************************************/ + WipperSnapper_I2C_Driver_VL53L4CD(TwoWire *i2c, uint16_t sensorAddress) + : WipperSnapper_I2C_Driver(i2c, sensorAddress) { + _i2c = i2c; + _sensorAddress = sensorAddress; + } + + /*******************************************************************************/ + /*! + @brief Destructor for an VL53L4CD sensor. + */ + /*******************************************************************************/ + ~WipperSnapper_I2C_Driver_VL53L4CD() { + // Called when a VL53L4CD component is deleted. + delete _VL53L4CD; + } + + /*******************************************************************************/ + /*! + @brief Initializes the VL53L4CD sensor and begins I2C. + @returns True if initialized successfully, False otherwise. + */ + /*******************************************************************************/ + bool begin() { + _VL53L4CD = new VL53L4CD(_i2c, -1); + + if (_VL53L4CD->InitSensor((uint8_t)_sensorAddress) != VL53L4CD_ERROR_NONE) { + WS_DEBUG_PRINTLN("Failed to initialize VL53L4CD sensor!"); + return false; + } + // Program the highest possible TimingBudget, no interval time + if (_VL53L4CD->VL53L4CD_SetRangeTiming(200, 0) != VL53L4CD_ERROR_NONE) { + WS_DEBUG_PRINTLN("Failed to set VL53L4CD timing!"); + return false; + } + + if (uint16_t signalThreshold; + _VL53L4CD->VL53L4CD_GetSignalThreshold(&signalThreshold) == + VL53L4CD_ERROR_NONE) { + WS_DEBUG_PRINT("VL53L4CD old signal threshold: "); + WS_DEBUG_PRINTLN(signalThreshold); + WS_DEBUG_PRINTLN("Setting VL53L4CD signal threshold to 50"); + if (_VL53L4CD->VL53L4CD_SetSignalThreshold(50) != VL53L4CD_ERROR_NONE) { + WS_DEBUG_PRINTLN("Failed to set new VL53L4CD signal threshold!"); + } + } else { + WS_DEBUG_PRINTLN("Failed to get VL53L4CD signal threshold!"); + } + + if (uint16_t sigmaThreshold; _VL53L4CD->VL53L4CD_GetSigmaThreshold( + &sigmaThreshold) == VL53L4CD_ERROR_NONE) { + WS_DEBUG_PRINT("VL53L4CD old sigma threshold: "); + WS_DEBUG_PRINTLN(sigmaThreshold); + WS_DEBUG_PRINTLN("Setting VL53L4CD sigma threshold to 100"); + if (_VL53L4CD->VL53L4CD_SetSigmaThreshold(100) != VL53L4CD_ERROR_NONE) { + WS_DEBUG_PRINTLN("Failed to set VL53L4CD sigma threshold!"); + } + } else { + WS_DEBUG_PRINTLN("Failed to get VL53L4CD sigma threshold!"); + } + + if (_VL53L4CD->VL53L4CD_StartRanging() != VL53L4CD_ERROR_NONE) { + WS_DEBUG_PRINTLN("Failed to start VL53L4CD ranging!"); + return false; + } + return true; + } + + /*******************************************************************************/ + /*! + @brief Gets the VL53L4CD's current proximity. + @param proximityEvent + Pointer to an Adafruit_Sensor event. + @returns True if the proximity was obtained successfully, False + otherwise. + */ + /*******************************************************************************/ + bool getEventProximity(sensors_event_t *proximityEvent) { + uint8_t NewDataReady = 0; + VL53L4CD_Result_t results; + uint8_t status; + // Start fresh reading, seemed to be accepting stale value + _VL53L4CD->VL53L4CD_ClearInterrupt(); + WS_DEBUG_PRINT("Waiting for VL53L4CD data ready..."); + delay(250); + + for (uint8_t retries = 0; + (status = _VL53L4CD->VL53L4CD_CheckForDataReady(&NewDataReady)) && + !NewDataReady && retries < 3; + retries++) { + delay(300); + WS_DEBUG_PRINT(" ."); + } + WS_DEBUG_PRINTLN(); + if ((status == VL53L4CD_ERROR_NONE) && (NewDataReady != 0)) { + // (Mandatory) Clear HW interrupt to restart measurements + _VL53L4CD->VL53L4CD_ClearInterrupt(); + + // Read measured distance. RangeStatus = 0 means valid data + if (_VL53L4CD->VL53L4CD_GetResult(&results) == VL53L4CD_ERROR_NONE) { + if (results.range_status != 0) { + WS_DEBUG_PRINT("VL53L4CD range status: "); + WS_DEBUG_PRINTLN(results.range_status); + return false; + } + proximityEvent->data[0] = (float)results.distance_mm; + return true; + } + // NOTE: Once I2C sensors fire all data points during a single call, we + // can return the std deviation in MM for the measurements. See + // https://github.com/stm32duino/VL53L4CD/blob/066664f983bcf70819133c7fcf43101035b09bab/src/vl53l4cd_api.h#L130-L131 + } else { + if (status == VL53L4CD_ERROR_INVALID_ARGUMENT) { + WS_DEBUG_PRINTLN("VL53L4CD: Invalid argument to CheckForDataReady()"); + } else if (status == VL53L4CD_ERROR_TIMEOUT) { + WS_DEBUG_PRINTLN("VL53L4CD: Timeout waiting for data ready"); + } else { + WS_DEBUG_PRINTLN("VL53L4CD: data not ready yet"); + } + } + return false; + } + +protected: + VL53L4CD *_VL53L4CD; ///< Pointer to VL53L4CD temperature sensor object +}; + +#endif // WipperSnapper_I2C_Driver_VL53L4CD \ No newline at end of file diff --git a/src/components/i2c/drivers/WipperSnapper_I2C_Driver_VL53L4CX.h b/src/components/i2c/drivers/WipperSnapper_I2C_Driver_VL53L4CX.h new file mode 100644 index 000000000..ef8307256 --- /dev/null +++ b/src/components/i2c/drivers/WipperSnapper_I2C_Driver_VL53L4CX.h @@ -0,0 +1,206 @@ +/*! + * @file WipperSnapper_I2C_Driver_VL53L4CX.h + * + * Device driver for the VL53L4CX ToF sensor. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) 2024 Tyeth Gundry for Adafruit Industries + * + * MIT license, all text here must be included in any redistribution. + * + */ +#ifndef WipperSnapper_I2C_Driver_VL53L4CX_H +#define WipperSnapper_I2C_Driver_VL53L4CX_H + +#include "WipperSnapper_I2C_Driver.h" +#include +#include + +#define VL53_SHUTDOWN_PIN -1 ///< Shutdown pin for VL53L4CX sensor +#define VL53_READING_DELAY 250 ///< Delay for reading data attempts +#define VL53_TIMING_BUDGET_NS 200000 ///< Timing budget for VL53L4CX sensor + +/**************************************************************************/ +/*! + @brief Class that provides a driver interface for a VL53L4CX sensor. +*/ +/**************************************************************************/ +class WipperSnapper_I2C_Driver_VL53L4CX : public WipperSnapper_I2C_Driver { +public: + /*******************************************************************************/ + /*! + @brief Constructor for a VL53L4CX sensor. + @param i2c + The I2C interface. + @param sensorAddress + 7-bit device address. + */ + /*******************************************************************************/ + WipperSnapper_I2C_Driver_VL53L4CX(TwoWire *i2c, uint16_t sensorAddress) + : WipperSnapper_I2C_Driver(i2c, sensorAddress) { + _i2c = i2c; + _sensorAddress = sensorAddress; + } + + /*******************************************************************************/ + /*! + @brief Destructor for an VL53L4CX sensor. + */ + /*******************************************************************************/ + ~WipperSnapper_I2C_Driver_VL53L4CX() { + // Called when a VL53L4CX component is deleted. + delete _VL53L4CX; + } + + /*******************************************************************************/ + /*! + @brief Initializes the VL53L4CX sensor and begins I2C. + @returns True if initialized successfully, False otherwise. + */ + /*******************************************************************************/ + bool begin() { + _VL53L4CX = new VL53L4CX(_i2c, VL53_SHUTDOWN_PIN); + + if (_VL53L4CX->InitSensor((uint8_t)_sensorAddress) != VL53L4CX_ERROR_NONE) { + WS_DEBUG_PRINTLN("Failed to initialize VL53L4CX sensor!"); + return false; + } + + if (_VL53L4CX->VL53L4CX_SetDistanceMode(VL53L4CX_DISTANCEMODE_LONG) != + VL53L4CX_ERROR_NONE) { + WS_DEBUG_PRINTLN("Failed to set VL53L4CX distance mode to long!"); + return false; + } + + // Set 200ms measurement time, the possible TimingBudget is 8-200ms + if (_VL53L4CX->VL53L4CX_SetMeasurementTimingBudgetMicroSeconds( + VL53_TIMING_BUDGET_NS) != VL53L4CX_ERROR_NONE) { + WS_DEBUG_PRINTLN("Failed to set VL53L4CX timing budget!"); + return false; + } + + if (_VL53L4CX->VL53L4CX_StartMeasurement() != VL53L4CX_ERROR_NONE) { + WS_DEBUG_PRINTLN("Failed to start VL53L4CX ranging!"); + return false; + } + return true; + } + + /*******************************************************************************/ + /*! + @brief Gets the VL53L4CX's current proximity for first object if found. + @param proximityEvent + Pointer to an Adafruit_Sensor event. + @returns True if the proximity was obtained successfully, False + otherwise. + */ + /*******************************************************************************/ + bool getEventProximity(sensors_event_t *proximityEvent) { + return getProximity(proximityEvent, 0); + } + + /*******************************************************************************/ + /*! + @brief Gets the VL53L4CX's current proximity for second object if + found. + @param proximityEvent + Pointer to an Adafruit_Sensor event. + @returns True if the proximity was obtained successfully, False + otherwise. + */ + /*******************************************************************************/ + bool getEventRaw(sensors_event_t *proximityEvent) { + return getProximity(proximityEvent, 1); + } + + /*******************************************************************************/ + /*! + @brief Gets the VL53L4CX's current proximity (first or second object). + @param proximityEvent + Pointer to an Adafruit_Sensor event. + @param whichObject + Index of the proximity object to get (0, or 1 for second + object). + @returns True if the proximity was obtained successfully, False + otherwise. + */ + /*******************************************************************************/ + bool getProximity(sensors_event_t *proximityEvent, int whichObject = 0) { + VL53L4CX_MultiRangingData_t MultiRangingData; + VL53L4CX_MultiRangingData_t *pMultiRangingData = &MultiRangingData; + uint8_t NewDataReady = 0; + int status; + + // Start fresh reading, seemed to be accepting stale value + status = _VL53L4CX->VL53L4CX_ClearInterruptAndStartMeasurement(); + if (status != VL53L4CX_ERROR_NONE) { + WS_DEBUG_PRINT( + "VL53L4CX Error clearing interrupt and starting measurement: "); + WS_DEBUG_PRINTLN(status); + return false; + } + + // Wait for data read period then update data ready status + WS_DEBUG_PRINT("Waiting for VL53L4CX data ready..."); + delay(VL53_READING_DELAY); + status = _VL53L4CX->VL53L4CX_GetMeasurementDataReady(&NewDataReady); + + if ((status != VL53L4CX_ERROR_NONE) || (NewDataReady == 0)) { + // error or no data ready + WS_DEBUG_PRINT("VL53L4CX Error checking for data ready: "); + WS_DEBUG_PRINTLN(status); + return false; + } + + // get data - still to verify which of one or two objects found + status = _VL53L4CX->VL53L4CX_GetMultiRangingData(pMultiRangingData); + if (status != VL53L4CX_ERROR_NONE) { + WS_DEBUG_PRINT("VL53L4CX Error getting multi ranging data: "); + WS_DEBUG_PRINTLN(status); + return false; + } + + // whichObject: 0-based index, return NaN(Object not found) if too few found + if (pMultiRangingData->NumberOfObjectsFound - 1 < whichObject) { + WS_DEBUG_PRINT("Object not found at index #"); + WS_DEBUG_PRINT(whichObject + 1); // human readable 1-based index + WS_DEBUG_PRINTLN(", returning NaN"); + proximityEvent->data[0] = NAN; + return true; + } + + // RESULT: take the first or second detected object from ranging data, + // if valid then set event data in proximityEvent or return false + return updateDataPointIfValid(pMultiRangingData->RangeData[whichObject], + proximityEvent); + } + + /*******************************************************************************/ + /*! + @brief Checks the VL53L4CX's proximity result and sets event value. + @param rangingData + The ranging data to check. + @param proximityEvent + Pointer to an Adafruit_Sensor event. + @returns True if the proximity was obtained successfully, False + otherwise. + */ + /*******************************************************************************/ + bool updateDataPointIfValid(VL53L4CX_TargetRangeData_t rangingData, + sensors_event_t *proximityEvent) { + if (rangingData.RangeStatus == VL53L4CX_RANGESTATUS_RANGE_VALID) { + int16_t mm = rangingData.RangeMilliMeter; + proximityEvent->data[0] = (float)mm; + return true; + } + return false; + } + +protected: + VL53L4CX *_VL53L4CX; ///< Pointer to VL53L4CX temperature sensor object +}; + +#endif // WipperSnapper_I2C_Driver_VL53L4CX \ No newline at end of file diff --git a/src/components/pixels/ws_pixels.cpp b/src/components/pixels/ws_pixels.cpp index 73db519af..a35eb6e42 100644 --- a/src/components/pixels/ws_pixels.cpp +++ b/src/components/pixels/ws_pixels.cpp @@ -170,8 +170,8 @@ void ws_pixels::publishAddStrandResponse(bool is_success, memset(WS._buffer_outgoing, 0, sizeof(WS._buffer_outgoing)); pb_ostream_t ostream = pb_ostream_from_buffer(WS._buffer_outgoing, sizeof(WS._buffer_outgoing)); - if (!pb_encode(&ostream, wippersnapper_signal_v1_PixelsResponse_fields, - &msgInitResp)) { + if (!ws_pb_encode(&ostream, wippersnapper_signal_v1_PixelsResponse_fields, + &msgInitResp)) { WS_DEBUG_PRINTLN("ERROR: Unable to encode " "wippersnapper_signal_v1_PixelsResponse message!"); return; diff --git a/src/components/register/Wippersnapper_Register.cpp b/src/components/register/Wippersnapper_Register.cpp index a7eccae34..522818997 100644 --- a/src/components/register/Wippersnapper_Register.cpp +++ b/src/components/register/Wippersnapper_Register.cpp @@ -46,7 +46,7 @@ bool Wippersnapper::encodePubRegistrationReq() { pb_ostream_t _msg_stream = pb_ostream_from_buffer(_message_buffer, sizeof(_message_buffer)); - _status = pb_encode( + _status = ws_pb_encode( &_msg_stream, wippersnapper_description_v1_CreateDescriptionRequest_fields, &_message); size_t _message_len = _msg_stream.bytes_written; @@ -107,9 +107,10 @@ void Wippersnapper::decodeRegistrationResp(char *data, uint16_t len) { // create input stream for buffer pb_istream_t stream = pb_istream_from_buffer(buffer, len); // decode the stream - if (!pb_decode(&stream, - wippersnapper_description_v1_CreateDescriptionResponse_fields, - &message)) { + if (!ws_pb_decode( + &stream, + wippersnapper_description_v1_CreateDescriptionResponse_fields, + &message)) { WS.haltError("Could not decode registration response"); } // Decode registration response message @@ -140,7 +141,7 @@ void Wippersnapper::decodeRegistrationResp(char *data, uint16_t len) { pb_ostream_t _msg_stream = pb_ostream_from_buffer(_message_buffer, sizeof(_message_buffer)); - bool _status = pb_encode( + bool _status = ws_pb_encode( &_msg_stream, wippersnapper_description_v1_RegistrationComplete_fields, &msg); size_t _message_len = _msg_stream.bytes_written; diff --git a/src/components/uart/drivers/ws_uart_drv_pm25aqi.h b/src/components/uart/drivers/ws_uart_drv_pm25aqi.h index c38772ca9..4eb4c0c92 100644 --- a/src/components/uart/drivers/ws_uart_drv_pm25aqi.h +++ b/src/components/uart/drivers/ws_uart_drv_pm25aqi.h @@ -185,8 +185,8 @@ class ws_uart_drv_pm25aqi : public ws_uart_drv { uint8_t mqttBuffer[512] = {0}; pb_ostream_t ostream = pb_ostream_from_buffer(mqttBuffer, sizeof(mqttBuffer)); - if (!pb_encode(&ostream, wippersnapper_signal_v1_UARTResponse_fields, - &msgUARTResponse)) { + if (!ws_pb_encode(&ostream, wippersnapper_signal_v1_UARTResponse_fields, + &msgUARTResponse)) { Serial.println("[ERROR, UART]: Unable to encode device response!"); return; } diff --git a/src/nanopb/ws_pb_helpers.cpp b/src/nanopb/ws_pb_helpers.cpp new file mode 100644 index 000000000..a3b2b8ef1 --- /dev/null +++ b/src/nanopb/ws_pb_helpers.cpp @@ -0,0 +1,61 @@ +/*! + * @file ws_pb_helpers.cpp + * + * Protobuf encode/decode helpers with error logging for Wippersnapper. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Tyeth Gundry 2024 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ + +#include "ws_pb_helpers.h" +#include "../Wippersnapper.h" + +// ***************************************************************************** +/*! + @brief Decodes a protobuf message from a stream and prints any error. + @param stream + The stream to decode from. + @param fields + The protobuf message fields. + @param dest_struct + The destination struct to decode into. + @return True if decode was successful, false otherwise. +!*/ +// ***************************************************************************** +bool ws_pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, + void *dest_struct) { + bool status = pb_decode(stream, fields, dest_struct); + if (!status) { + WS_DEBUG_PRINT("Protobuf decode error: "); + WS_DEBUG_PRINTLN(PB_GET_ERROR(stream)); + } + return status; +} + +// ***************************************************************************** +/*! + @brief Encodes a protobuf message to a stream and prints any error. + @param stream + The stream to encode to. + @param fields + The protobuf message fields. + @param src_struct + The source struct to encode from. + @return True if encode was successful, false otherwise. +!*/ +// ***************************************************************************** +bool ws_pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, + const void *src_struct) { + bool status = pb_encode(stream, fields, src_struct); + if (!status) { + WS_DEBUG_PRINT("Protobuf encode error: "); + WS_DEBUG_PRINTLN(PB_GET_ERROR(stream)); + } + return status; +} \ No newline at end of file diff --git a/src/nanopb/ws_pb_helpers.h b/src/nanopb/ws_pb_helpers.h new file mode 100644 index 000000000..78a0d5c10 --- /dev/null +++ b/src/nanopb/ws_pb_helpers.h @@ -0,0 +1,28 @@ +/*! + * @file ws_pb_helpers.h + * + * Protobuf encode/decode helpers with error logging for Wippersnapper. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Tyeth Gundry 2024 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#ifndef WS_PB_ENCODE_H +#define WS_PB_ENCODE_H + +#include "pb.h" +#include "pb_decode.h" +#include "pb_encode.h" + +bool ws_pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, + void *dest_struct); + +bool ws_pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, + const void *src_struct); + +#endif // WS_PB_ENCODE_H \ No newline at end of file diff --git a/src/network_interfaces/Wippersnapper_AIRLIFT.h b/src/network_interfaces/Wippersnapper_AIRLIFT.h index ce39590b9..4c564b4e1 100644 --- a/src/network_interfaces/Wippersnapper_AIRLIFT.h +++ b/src/network_interfaces/Wippersnapper_AIRLIFT.h @@ -28,7 +28,9 @@ #include "Wippersnapper.h" #define NINAFWVER \ - "1.6.0" /*!< min. nina-fw version compatible with this library. */ + "1.7.7" /*!< min. nina-fw version compatible with this library. */ +#define AIRLIFT_CONNECT_TIMEOUT_MS 20000 /*!< Connection timeout (in ms) */ +#define AIRLIFT_CONNECT_RETRY_DELAY_MS 200 /*!< delay time between retries. */ #define SPIWIFI SPI /*!< Instance of SPI interface used by an AirLift. */ @@ -47,10 +49,14 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { */ /**************************************************************************/ Wippersnapper_AIRLIFT() : Wippersnapper() { - _ssPin = 10; - _ackPin = 7; - _rstPin = 5; + _ssPin = SPIWIFI_SS; // 10; + _ackPin = SPIWIFI_ACK; // 7; + _rstPin = SPIWIFI_RESET; // 5; // should be 7 on PyPortals +#ifdef ESP32_GPIO0 + _gpio0Pin = ESP32_GPIO0; +#else _gpio0Pin = -1; +#endif _wifi = &SPIWIFI; _ssid = 0; _pass = 0; @@ -115,8 +121,11 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { // Was the network within secrets.json found? for (int i = 0; i < n; ++i) { - if (strcmp(_ssid, WiFi.SSID(i)) == 0) + if (strcmp(_ssid, WiFi.SSID(i)) == 0) { + WS_DEBUG_PRINT("SSID found! RSSI: "); + WS_DEBUG_PRINTLN(WiFi.RSSI(i)); return true; + } } // User-set network not found, print scan results to serial console @@ -174,9 +183,43 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { /********************************************************/ bool firmwareCheck() { _fv = WiFi.firmwareVersion(); - if (_fv < NINAFWVER) + return compareVersions(_fv, NINAFWVER); + } + + /********************************************************/ + /*! + @brief Compares two version strings. + @param currentVersion + Current version string. + @param requiredVersion + Required version string. + @returns True if the current version is greater than or + equal to the required version, False otherwise. + */ + /********************************************************/ + bool compareVersions(const char *currentVersion, + const char *requiredVersion) { + int curMajor = 0, curMinor = 0, curPatch = 0; + int reqMajor = 0, reqMinor = 0, reqPatch = 0; + + if (!sscanf(currentVersion, "%d.%d.%d", &curMajor, &curMinor, &curPatch) || + !sscanf(requiredVersion, "%d.%d.%d", &reqMajor, &reqMinor, &reqPatch)) { + WS_DEBUG_PRINTLN("Error parsing firmware version strings"); + WS_PRINTER.flush(); + WS_DEBUG_PRINT("Required version: "); + WS_DEBUG_PRINTLN(requiredVersion); + WS_PRINTER.flush(); + WS_DEBUG_PRINT("Current version: "); + WS_DEBUG_PRINTLN(currentVersion); + WS_PRINTER.flush(); return false; - return true; + } + + if (curMajor != reqMajor) + return curMajor > reqMajor; + if (curMinor != reqMinor) + return curMinor > reqMinor; + return curPatch >= reqPatch; } /********************************************************/ @@ -191,6 +234,14 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { memcpy(WS._macAddr, mac, sizeof(mac)); } + /********************************************************/ + /*! + @brief Gets the current network RSSI value + @return int32_t RSSI value + */ + /********************************************************/ + int32_t getRSSI() { return WiFi.RSSI(); } + /********************************************************/ /*! @brief Initializes the MQTT client. @@ -234,7 +285,7 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { protected: const char *_ssid; /*!< Network SSID. */ const char *_pass; /*!< Network password. */ - String _fv; /*!< nina-fw firmware version. */ + const char *_fv = "0.0.1"; /*!< nina-fw firmware version. (placeholder) */ int _ssPin = -1; /*!< SPI S.S. pin. */ int _ackPin = -1; /*!< SPI ACK pin. */ int _rstPin = -1; /*!< SPI RST pin. */ @@ -249,25 +300,86 @@ class Wippersnapper_AIRLIFT : public Wippersnapper { /**************************************************************************/ void _connect() { if (strlen(_ssid) == 0) { - _status = WS_SSID_INVALID; + _status = WS_SSID_INVALID; // possibly unneccesary as already checking + // elsewhere } else { + // disconnect from possible previous connection + _disconnect(); + delay(100); + WiFi.end(); + _wifi->end(); + delay(100); + _wifi->begin(); + feedWDT(); + // reset the esp32 if possible + resetAirLift(); + feedWDT(); - // validate co-processor is physically connected connection - if (WiFi.status() == WL_NO_MODULE) { - WS_DEBUG_PRINT("No ESP32 module detected!"); - return; - } + WS_DEBUG_PRINT("ESP32 booted, version: "); + WS_PRINTER.flush(); + WS_DEBUG_PRINTLN(WiFi.firmwareVersion()); + WS_PRINTER.flush(); + feedWDT(); // validate co-processor's firmware version - if (!firmwareCheck()) + if (!firmwareCheck()) { WS_DEBUG_PRINTLN("Please upgrade the firmware on the ESP module to the " "latest version."); + } - // disconnect from possible previous connection - _disconnect(); - + WS_DEBUG_PRINT("Connecting to "); + WS_DEBUG_PRINTLN(_ssid); + WS_PRINTER.flush(); + feedWDT(); WiFi.begin(_ssid, _pass); _status = WS_NET_DISCONNECTED; + + // Use the macro to retry the status check until connected / timed out + int lastResult = -1; + RETRY_FUNCTION_UNTIL_TIMEOUT( + []() -> int { return WiFi.status(); }, // Function call each cycle + int, // return type + lastResult, // return variable + [](int status) { return status == WL_CONNECTED; }, // check + AIRLIFT_CONNECT_TIMEOUT_MS, // timeout interval (ms) + AIRLIFT_CONNECT_RETRY_DELAY_MS); // interval between retries + + if (lastResult == WL_CONNECTED) { + _status = WS_NET_CONNECTED; + // wait 2seconds for connection to stabilize + WS_DELAY_WITH_WDT(2000); + } else { + _status = WS_NET_DISCONNECTED; // maybe connect failed instead? + } + } + } + + /**************************************************************************/ + /*! + @brief Resets the ESP32 module. + */ + /**************************************************************************/ + void resetAirLift() { + if (_rstPin != -1) { + WS_DEBUG_PRINTLN("Resetting ESP32..."); + WS_PRINTER.flush(); + // Chip select for esp32 + pinMode(_ssPin, OUTPUT); + digitalWrite(_ssPin, HIGH); // Do we need to set SS low again? + if (_gpio0Pin != -1) { + pinMode(_gpio0Pin, OUTPUT); + digitalWrite(_gpio0Pin, LOW); + } + pinMode(_rstPin, OUTPUT); + digitalWrite(_rstPin, LOW); + delay(50); + digitalWrite(_rstPin, HIGH); + delay(10); + if (_gpio0Pin != -1) { + pinMode(_gpio0Pin, INPUT); + } + // wait for the ESP32 to boot + delay(2000); } } diff --git a/src/network_interfaces/Wippersnapper_ESP32.h b/src/network_interfaces/Wippersnapper_ESP32.h index 623b95979..c95f65864 100644 --- a/src/network_interfaces/Wippersnapper_ESP32.h +++ b/src/network_interfaces/Wippersnapper_ESP32.h @@ -8,7 +8,7 @@ * please support Adafruit and open-source hardware by purchasing * products from Adafruit! * - * Copyright (c) Brent Rubell 2020-2021 for Adafruit Industries. + * Copyright (c) Brent Rubell 2020-2024 for Adafruit Industries. * * MIT license, all text here must be included in any redistribution. * @@ -23,7 +23,10 @@ #include "Adafruit_MQTT.h" #include "Adafruit_MQTT_Client.h" #include "Arduino.h" -#include +#include "WiFi.h" +#include "WiFiMulti.h" +#include +#include extern Wippersnapper WS; /****************************************************************************/ @@ -42,7 +45,6 @@ class Wippersnapper_ESP32 : public Wippersnapper { Wippersnapper_ESP32() : Wippersnapper() { _ssid = 0; _pass = 0; - _mqtt_client = new WiFiClientSecure; } /**************************************************************************/ @@ -51,8 +53,10 @@ class Wippersnapper_ESP32 : public Wippersnapper { */ /**************************************************************************/ ~Wippersnapper_ESP32() { - if (_mqtt_client) - delete _mqtt_client; + if (_mqtt_client_secure) + delete _mqtt_client_secure; + if (_mqtt_client_insecure) + delete _mqtt_client_insecure; } /********************************************************/ @@ -108,8 +112,25 @@ class Wippersnapper_ESP32 : public Wippersnapper { // Was the network within secrets.json found? for (int i = 0; i < n; ++i) { - if (strcmp(_ssid, WiFi.SSID(i).c_str()) == 0) + if (strcmp(_ssid, WiFi.SSID(i).c_str()) == 0) { + WS_DEBUG_PRINT("SSID ("); + WS_DEBUG_PRINT(_ssid); + WS_DEBUG_PRINT(") found! RSSI: "); + WS_DEBUG_PRINTLN(WiFi.RSSI(i)); return true; + } + if (WS._isWiFiMulti) { + // multi network mode + for (int j = 0; j < WS_MAX_ALT_WIFI_NETWORKS; j++) { + if (strcmp(WS._multiNetworks[j].ssid, WiFi.SSID(i).c_str()) == 0) { + WS_DEBUG_PRINT("SSID ("); + WS_DEBUG_PRINT(WS._multiNetworks[j].ssid); + WS_DEBUG_PRINT(") found! RSSI: "); + WS_DEBUG_PRINTLN(WiFi.RSSI(i)); + return true; + } + } + } } // User-set network not found, print scan results to serial console @@ -133,10 +154,18 @@ class Wippersnapper_ESP32 : public Wippersnapper { /********************************************************/ void getMacAddr() { uint8_t mac[6] = {0}; - WiFi.macAddress(mac); + Network.macAddress(mac); memcpy(WS._macAddr, mac, sizeof(mac)); } + /********************************************************/ + /*! + @brief Gets the current network RSSI value + @return int32_t RSSI value + */ + /********************************************************/ + int32_t getRSSI() { return WiFi.RSSI(); } + /********************************************************/ /*! @brief Initializes the MQTT client @@ -145,16 +174,24 @@ class Wippersnapper_ESP32 : public Wippersnapper { */ /********************************************************/ void setupMQTTClient(const char *clientID) { - if (strcmp(WS._config.aio_url, "io.adafruit.com") == 0) { - _mqtt_client->setCACert(_aio_root_ca_prod); + if (strcmp(WS._config.aio_url, "io.adafruit.com") == 0 || + strcmp(WS._config.aio_url, "io.adafruit.us") == 0) { + _mqtt_client_secure = new NetworkClientSecure(); + _mqtt_client_secure->setCACert( + strcmp(WS._config.aio_url, "io.adafruit.com") == 0 + ? _aio_root_ca_prod + : _aio_root_ca_staging); + WS._mqtt = new Adafruit_MQTT_Client( + _mqtt_client_secure, WS._config.aio_url, WS._config.io_port, clientID, + WS._config.aio_user, WS._config.aio_key); } else { - _mqtt_client->setCACert(_aio_root_ca_staging); + // Insecure connections require a NetworkClient object rather than a + // NetworkClientSecure object + _mqtt_client_insecure = new NetworkClient(); + WS._mqtt = new Adafruit_MQTT_Client( + _mqtt_client_insecure, WS._config.aio_url, WS._config.io_port, + clientID, WS._config.aio_user, WS._config.aio_key); } - - // Construct MQTT client - WS._mqtt = new Adafruit_MQTT_Client(_mqtt_client, WS._config.aio_url, 8883, - clientID, WS._config.aio_user, - WS._config.aio_key); } /********************************************************/ @@ -185,36 +222,40 @@ class Wippersnapper_ESP32 : public Wippersnapper { const char *connectionType() { return "ESP32"; } protected: - const char *_ssid; ///< WiFi SSID - const char *_pass; ///< WiFi password - WiFiClientSecure *_mqtt_client; ///< Pointer to a WiFi client object (TLS/SSL) + const char *_ssid; ///< WiFi SSID + const char *_pass; ///< WiFi password + NetworkClientSecure + *_mqtt_client_secure; ///< Pointer to a secure network client object + NetworkClient + *_mqtt_client_insecure; ///< Pointer to an insecure network client object + WiFiMulti _wifiMulti; ///< WiFiMulti object for multi-network mode const char *_aio_root_ca_staging = "-----BEGIN CERTIFICATE-----\n" - "MIIEZTCCA02gAwIBAgIQQAF1BIMUpMghjISpDBbN3zANBgkqhkiG9w0BAQsFADA/\n" - "MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\n" - "DkRTVCBSb290IENBIFgzMB4XDTIwMTAwNzE5MjE0MFoXDTIxMDkyOTE5MjE0MFow\n" - "MjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxCzAJBgNVBAMT\n" - "AlIzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuwIVKMz2oJTTDxLs\n" - "jVWSw/iC8ZmmekKIp10mqrUrucVMsa+Oa/l1yKPXD0eUFFU1V4yeqKI5GfWCPEKp\n" - "Tm71O8Mu243AsFzzWTjn7c9p8FoLG77AlCQlh/o3cbMT5xys4Zvv2+Q7RVJFlqnB\n" - "U840yFLuta7tj95gcOKlVKu2bQ6XpUA0ayvTvGbrZjR8+muLj1cpmfgwF126cm/7\n" - "gcWt0oZYPRfH5wm78Sv3htzB2nFd1EbjzK0lwYi8YGd1ZrPxGPeiXOZT/zqItkel\n" - "/xMY6pgJdz+dU/nPAeX1pnAXFK9jpP+Zs5Od3FOnBv5IhR2haa4ldbsTzFID9e1R\n" - "oYvbFQIDAQABo4IBaDCCAWQwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8E\n" - "BAMCAYYwSwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5p\n" - "ZGVudHJ1c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTE\n" - "p7Gkeyxx+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEE\n" - "AYLfEwEBATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2Vu\n" - "Y3J5cHQub3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0\n" - "LmNvbS9EU1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYf\n" - "r52LFMLGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjANBgkqhkiG9w0B\n" - "AQsFAAOCAQEA2UzgyfWEiDcx27sT4rP8i2tiEmxYt0l+PAK3qB8oYevO4C5z70kH\n" - "ejWEHx2taPDY/laBL21/WKZuNTYQHHPD5b1tXgHXbnL7KqC401dk5VvCadTQsvd8\n" - "S8MXjohyc9z9/G2948kLjmE6Flh9dDYrVYA9x2O+hEPGOaEOa1eePynBgPayvUfL\n" - "qjBstzLhWVQLGAkXXmNs+5ZnPBxzDJOLxhF2JIbeQAcH5H0tZrUlo5ZYyOqA7s9p\n" - "O5b85o3AM/OJ+CktFBQtfvBhcJVd9wvlwPsk+uyOy2HI7mNxKKgsBTt375teA2Tw\n" - "UdHkhVNcsAKX1H7GNNLOEADksd86wuoXvg==\n" + "MIIEVzCCAj+gAwIBAgIRALBXPpFzlydw27SHyzpFKzgwDQYJKoZIhvcNAQELBQAw\n" + "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n" + "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjQwMzEzMDAwMDAw\n" + "WhcNMjcwMzEyMjM1OTU5WjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg\n" + "RW5jcnlwdDELMAkGA1UEAxMCRTYwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATZ8Z5G\n" + "h/ghcWCoJuuj+rnq2h25EqfUJtlRFLFhfHWWvyILOR/VvtEKRqotPEoJhC6+QJVV\n" + "6RlAN2Z17TJOdwRJ+HB7wxjnzvdxEP6sdNgA1O1tHHMWMxCcOrLqbGL0vbijgfgw\n" + "gfUwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD\n" + "ATASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBSTJ0aYA6lRaI6Y1sRCSNsj\n" + "v1iU0jAfBgNVHSMEGDAWgBR5tFnme7bl5AFzgAiIyBpY9umbbjAyBggrBgEFBQcB\n" + "AQQmMCQwIgYIKwYBBQUHMAKGFmh0dHA6Ly94MS5pLmxlbmNyLm9yZy8wEwYDVR0g\n" + "BAwwCjAIBgZngQwBAgEwJwYDVR0fBCAwHjAcoBqgGIYWaHR0cDovL3gxLmMubGVu\n" + "Y3Iub3JnLzANBgkqhkiG9w0BAQsFAAOCAgEAfYt7SiA1sgWGCIpunk46r4AExIRc\n" + "MxkKgUhNlrrv1B21hOaXN/5miE+LOTbrcmU/M9yvC6MVY730GNFoL8IhJ8j8vrOL\n" + "pMY22OP6baS1k9YMrtDTlwJHoGby04ThTUeBDksS9RiuHvicZqBedQdIF65pZuhp\n" + "eDcGBcLiYasQr/EO5gxxtLyTmgsHSOVSBcFOn9lgv7LECPq9i7mfH3mpxgrRKSxH\n" + "pOoZ0KXMcB+hHuvlklHntvcI0mMMQ0mhYj6qtMFStkF1RpCG3IPdIwpVCQqu8GV7\n" + "s8ubknRzs+3C/Bm19RFOoiPpDkwvyNfvmQ14XkyqqKK5oZ8zhD32kFRQkxa8uZSu\n" + "h4aTImFxknu39waBxIRXE4jKxlAmQc4QjFZoq1KmQqQg0J/1JF8RlFvJas1VcjLv\n" + "YlvUB2t6npO6oQjB3l+PNf0DpQH7iUx3Wz5AjQCi6L25FjyE06q6BZ/QlmtYdl/8\n" + "ZYao4SRqPEs/6cAiF+Qf5zg2UkaWtDphl1LKMuTNLotvsX99HP69V2faNyegodQ0\n" + "LyTApr/vT01YPE46vNsDLgK+4cL6TrzC/a4WcmF5SRJ938zrv/duJHLXQIku5v0+\n" + "EwOy59Hdm0PT/Er/84dDV0CSjdR/2XuZM3kpysSKLgD1cKiDA+IRguODCxfO9cyY\n" + "Ig46v9mFmBvyH04=\n" "-----END CERTIFICATE-----\n"; ///< Root certificate for io.adafruit.us const char *_aio_root_ca_prod = @@ -259,11 +300,35 @@ class Wippersnapper_ESP32 : public Wippersnapper { if (strlen(_ssid) == 0) { _status = WS_SSID_INVALID; } else { + WiFi.setAutoReconnect(false); _disconnect(); delay(100); - WiFi.begin(_ssid, _pass); - _status = WS_NET_DISCONNECTED; - delay(5000); + if (WS._isWiFiMulti) { + // multi network mode + _wifiMulti.APlistClean(); + _wifiMulti.setAllowOpenAP(false); + // add default network + _wifiMulti.addAP(_ssid, _pass); + // add array of alternative networks + for (int i = 0; i < WS_MAX_ALT_WIFI_NETWORKS; i++) { + if (strlen(WS._multiNetworks[i].ssid) > 0) { + _wifiMulti.addAP(WS._multiNetworks[i].ssid, + WS._multiNetworks[i].pass); + } + } + if (_wifiMulti.run(20000) == WL_CONNECTED) { + _status = WS_NET_CONNECTED; + } else { + _status = WS_NET_DISCONNECTED; + } + } else { + // single network mode + WiFi.begin(_ssid, _pass); + _status = WS_NET_DISCONNECTED; + WS.feedWDT(); + delay(5000); + } + WS.feedWDT(); } } diff --git a/src/network_interfaces/Wippersnapper_ESP8266.h b/src/network_interfaces/Wippersnapper_ESP8266.h index d64ba1342..c7fa6b693 100644 --- a/src/network_interfaces/Wippersnapper_ESP8266.h +++ b/src/network_interfaces/Wippersnapper_ESP8266.h @@ -21,6 +21,7 @@ #include "Adafruit_MQTT.h" #include "Adafruit_MQTT_Client.h" #include "ESP8266WiFi.h" +#include "ESP8266WiFiMulti.h" #include "Wippersnapper.h" /* NOTE - Projects that require "Secure MQTT" (TLS/SSL) also require a new @@ -64,6 +65,8 @@ class Wippersnapper_ESP8266 : public Wippersnapper { _ssid = 0; _pass = 0; _wifi_client = new WiFiClient; + WiFi.persistent(false); + WiFi.mode(WIFI_STA); } /**************************************************************************/ @@ -132,8 +135,25 @@ class Wippersnapper_ESP8266 : public Wippersnapper { // Was the network within secrets.json found? for (int i = 0; i < n; ++i) { - if (strcmp(_ssid, WiFi.SSID(i).c_str()) == 0) + if (strcmp(_ssid, WiFi.SSID(i).c_str()) == 0) { + WS_DEBUG_PRINT("SSID ("); + WS_DEBUG_PRINT(_ssid); + WS_DEBUG_PRINT(") found! RSSI: "); + WS_DEBUG_PRINTLN(WiFi.RSSI(i)); return true; + } + if (WS._isWiFiMulti) { + // multi network mode + for (int j = 0; j < WS_MAX_ALT_WIFI_NETWORKS; j++) { + if (strcmp(WS._multiNetworks[j].ssid, WiFi.SSID(i).c_str()) == 0) { + WS_DEBUG_PRINT("SSID ("); + WS_DEBUG_PRINT(WS._multiNetworks[j].ssid); + WS_DEBUG_PRINT(") found! RSSI: "); + WS_DEBUG_PRINTLN(WiFi.RSSI(i)); + return true; + } + } + } } // User-set network not found, print scan results to serial console @@ -161,6 +181,14 @@ class Wippersnapper_ESP8266 : public Wippersnapper { memcpy(WS._macAddr, mac, sizeof(mac)); } + /********************************************************/ + /*! + @brief Gets the current network RSSI value + @return int32_t RSSI value + */ + /********************************************************/ + int32_t getRSSI() { return WiFi.RSSI(); } + /*******************************************************************/ /*! @brief Sets up an Adafruit_MQTT_Client @@ -173,10 +201,11 @@ class Wippersnapper_ESP8266 : public Wippersnapper { // re-compile after. _wifi_client->setFingerprint(fingerprint); WS._mqtt = // new Adafruit_MQTT_Client(_wifi_client, WS._config.aio_url, // WS._config.io_port, clientID, WS._config.aio_user, WS._config.aio_key); - - WS._mqtt = new Adafruit_MQTT_Client(_wifi_client, WS._config.aio_url, 1883, - clientID, WS._config.aio_user, - WS._config.aio_key); + if (WS._config.io_port == 8883) + WS._config.io_port = 1883; + WS._mqtt = new Adafruit_MQTT_Client( + _wifi_client, WS._config.aio_url, WS._config.io_port, clientID, + WS._config.aio_user, WS._config.aio_key); } /********************************************************/ @@ -210,6 +239,7 @@ class Wippersnapper_ESP8266 : public Wippersnapper { const char *_ssid = NULL; const char *_pass = NULL; WiFiClient *_wifi_client; + ESP8266WiFiMulti _wifiMulti; /**************************************************************************/ /*! @@ -221,21 +251,62 @@ class Wippersnapper_ESP8266 : public Wippersnapper { if (WiFi.status() == WL_CONNECTED) return; - // Attempt connection - _disconnect(); - delay(100); - // ESP8266 MUST be in STA mode to avoid device acting as client/server - WiFi.mode(WIFI_STA); - WiFi.begin(_ssid, _pass); - _status = WS_NET_DISCONNECTED; - delay(100); + if (strlen(_ssid) == 0) { + _status = WS_SSID_INVALID; + } else { + WiFi.setAutoReconnect(false); + // Attempt connection + _disconnect(); + delay(100); + // ESP8266 MUST be in STA mode to avoid device acting as client/server + WiFi.mode(WIFI_STA); + WiFi.begin(_ssid, _pass); + _status = WS_NET_DISCONNECTED; + delay(100); + + if (WS._isWiFiMulti) { + // multi network mode + for (int i = 0; i < WS_MAX_ALT_WIFI_NETWORKS; i++) { + if (strlen(WS._multiNetworks[i].ssid) > 0 && + (_wifiMulti.existsAP(WS._multiNetworks[i].ssid) == false)) { + // doesn't exist, add it + _wifiMulti.addAP(WS._multiNetworks[i].ssid, + WS._multiNetworks[i].pass); + } + } + // add default network + if (_wifiMulti.existsAP(_ssid) == false) { + _wifiMulti.addAP(_ssid, _pass); + } + long startRetry = millis(); + WS_DEBUG_PRINTLN("CONNECTING"); + while (_wifiMulti.run(5000) != WL_CONNECTED && + millis() - startRetry < 10000) { + // ESP8266 WDT requires yield() during a busy-loop so it doesn't bite + yield(); + } + if (WiFi.status() == WL_CONNECTED) { + _status = WS_NET_CONNECTED; + } else { + _status = WS_NET_DISCONNECTED; + } + } else { + // single network mode - // wait for a connection to be established - long startRetry = millis(); - WS_DEBUG_PRINTLN("CONNECTING"); - while (WiFi.status() != WL_CONNECTED && millis() - startRetry < 10000) { - // ESP8266 WDT requires yield() during a busy-loop so it doesn't bite - yield(); + // wait for a connection to be established + long startRetry = millis(); + WS_DEBUG_PRINTLN("CONNECTING"); + while (WiFi.status() != WL_CONNECTED && millis() - startRetry < 10000) { + // ESP8266 WDT requires yield() during a busy-loop so it doesn't bite + yield(); + } + if (WiFi.status() == WL_CONNECTED) { + _status = WS_NET_CONNECTED; + } else { + _status = WS_NET_DISCONNECTED; + } + } + WS.feedWDT(); } } diff --git a/src/network_interfaces/Wippersnapper_WIFININA.h b/src/network_interfaces/Wippersnapper_WIFININA.h index aa56c3b40..74f50bffe 100644 --- a/src/network_interfaces/Wippersnapper_WIFININA.h +++ b/src/network_interfaces/Wippersnapper_WIFININA.h @@ -130,8 +130,11 @@ class Wippersnapper_WIFININA : public Wippersnapper { // Was the network within secrets.json found? for (int i = 0; i < n; ++i) { - if (strcmp(_ssid, WiFi.SSID(i)) == 0) + if (strcmp(_ssid, WiFi.SSID(i)) == 0) { + WS_DEBUG_PRINT("SSID found! RSSI: "); + WS_DEBUG_PRINTLN(WiFi.RSSI(i)); return true; + } } // User-set network not found, print scan results to serial console @@ -185,6 +188,14 @@ class Wippersnapper_WIFININA : public Wippersnapper { memcpy(WS._macAddr, mac, sizeof(mac)); } + /********************************************************/ + /*! + @brief Gets the current network RSSI value + @return int32_t RSSI value + */ + /********************************************************/ + int32_t getRSSI() { return WiFi.RSSI(); } + /********************************************************/ /*! @brief Initializes the MQTT client. diff --git a/src/network_interfaces/ws_networking_pico.h b/src/network_interfaces/ws_networking_pico.h index 153e11ef0..283a24467 100644 --- a/src/network_interfaces/ws_networking_pico.h +++ b/src/network_interfaces/ws_networking_pico.h @@ -18,11 +18,16 @@ #define WS_NETWORKING_PICO_H #ifdef ARDUINO_ARCH_RP2040 + +#define PICO_CONNECT_TIMEOUT_MS 20000 /*!< Connection timeout (in ms) */ +#define PICO_CONNECT_RETRY_DELAY_MS 200 /*!< delay time between retries. */ + #include "Wippersnapper.h" #include "Adafruit_MQTT.h" #include "Adafruit_MQTT_Client.h" #include "Arduino.h" +#include #include extern Wippersnapper WS; @@ -42,7 +47,6 @@ class ws_networking_pico : public Wippersnapper { ws_networking_pico() : Wippersnapper() { _ssid = 0; _pass = 0; - _mqtt_client = new WiFiClientSecure; } /**************************************************************************/ @@ -51,8 +55,10 @@ class ws_networking_pico : public Wippersnapper { */ /**************************************************************************/ ~ws_networking_pico() { - if (_mqtt_client) - delete _mqtt_client; + if (_mqtt_client_secure) + delete _mqtt_client_secure; + if (_mqtt_client_secure) + delete _mqtt_client_secure; } /********************************************************/ @@ -108,8 +114,25 @@ class ws_networking_pico : public Wippersnapper { // Was the network within secrets.json found? for (int i = 0; i < n; ++i) { - if (strcmp(_ssid, WiFi.SSID(i)) == 0) + if (strcmp(_ssid, WiFi.SSID(i)) == 0) { + WS_DEBUG_PRINT("SSID ("); + WS_DEBUG_PRINT(_ssid); + WS_DEBUG_PRINT(") found! RSSI: "); + WS_DEBUG_PRINTLN(WiFi.RSSI(i)); return true; + } + if (WS._isWiFiMulti) { + // multi network mode + for (int j = 0; j < WS_MAX_ALT_WIFI_NETWORKS; j++) { + if (strcmp(WS._multiNetworks[j].ssid, WiFi.SSID(i)) == 0) { + WS_DEBUG_PRINT("SSID ("); + WS_DEBUG_PRINT(WS._multiNetworks[j].ssid); + WS_DEBUG_PRINT(") found! RSSI: "); + WS_DEBUG_PRINTLN(WiFi.RSSI(i)); + return true; + } + } + } } // User-set network not found, print scan results to serial console @@ -137,6 +160,14 @@ class ws_networking_pico : public Wippersnapper { memcpy(WS._macAddr, mac, sizeof(mac)); } + /********************************************************/ + /*! + @brief Gets the current network RSSI value + @return int32_t RSSI value + */ + /********************************************************/ + int32_t getRSSI() { return WiFi.RSSI(); } + /********************************************************/ /*! @brief Initializes the MQTT client @@ -145,17 +176,22 @@ class ws_networking_pico : public Wippersnapper { */ /********************************************************/ void setupMQTTClient(const char *clientID) { - // Set CA cert depending on the server we're connecting to - // compare WS._config.aio_url to "io.adafruit.com" - if (strcmp(WS._config.aio_url, "io.adafruit.com") == 0) { - _mqtt_client->setCACert(_aio_root_ca_prod); + if (strcmp(WS._config.aio_url, "io.adafruit.com") == 0 || + strcmp(WS._config.aio_url, "io.adafruit.us") == 0) { + _mqtt_client_secure = new WiFiClientSecure(); + _mqtt_client_secure->setCACert( + strcmp(WS._config.aio_url, "io.adafruit.com") == 0 + ? _aio_root_ca_prod + : _aio_root_ca_staging); + WS._mqtt = new Adafruit_MQTT_Client( + _mqtt_client_secure, WS._config.aio_url, WS._config.io_port, clientID, + WS._config.aio_user, WS._config.aio_key); } else { - _mqtt_client->setCACert(_aio_root_ca_staging); + _mqtt_client_insecure = new WiFiClient(); + WS._mqtt = new Adafruit_MQTT_Client( + _mqtt_client_insecure, WS._config.aio_url, WS._config.io_port, + clientID, WS._config.aio_user, WS._config.aio_key); } - - WS._mqtt = new Adafruit_MQTT_Client( - _mqtt_client, WS._config.aio_url, WS._config.io_port, clientID, - WS._config.aio_user, WS._config.aio_key); } /********************************************************/ @@ -186,36 +222,40 @@ class ws_networking_pico : public Wippersnapper { const char *connectionType() { return "Pico"; } protected: - const char *_ssid; ///< WiFi SSID - const char *_pass; ///< WiFi password - WiFiClientSecure *_mqtt_client; ///< Pointer to a secure MQTT client object + const char *_ssid; ///< WiFi SSID + const char *_pass; ///< WiFi password + WiFiClient + *_mqtt_client_insecure; ///< Pointer to an insecure WiFi client object + WiFiClientSecure + *_mqtt_client_secure; ///< Pointer to a secure WiFi client object + WiFiMulti _wifiMulti; ///< WiFiMulti object for multi-network mode const char *_aio_root_ca_staging = "-----BEGIN CERTIFICATE-----\n" - "MIIEZTCCA02gAwIBAgIQQAF1BIMUpMghjISpDBbN3zANBgkqhkiG9w0BAQsFADA/\n" - "MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\n" - "DkRTVCBSb290IENBIFgzMB4XDTIwMTAwNzE5MjE0MFoXDTIxMDkyOTE5MjE0MFow\n" - "MjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxCzAJBgNVBAMT\n" - "AlIzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuwIVKMz2oJTTDxLs\n" - "jVWSw/iC8ZmmekKIp10mqrUrucVMsa+Oa/l1yKPXD0eUFFU1V4yeqKI5GfWCPEKp\n" - "Tm71O8Mu243AsFzzWTjn7c9p8FoLG77AlCQlh/o3cbMT5xys4Zvv2+Q7RVJFlqnB\n" - "U840yFLuta7tj95gcOKlVKu2bQ6XpUA0ayvTvGbrZjR8+muLj1cpmfgwF126cm/7\n" - "gcWt0oZYPRfH5wm78Sv3htzB2nFd1EbjzK0lwYi8YGd1ZrPxGPeiXOZT/zqItkel\n" - "/xMY6pgJdz+dU/nPAeX1pnAXFK9jpP+Zs5Od3FOnBv5IhR2haa4ldbsTzFID9e1R\n" - "oYvbFQIDAQABo4IBaDCCAWQwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8E\n" - "BAMCAYYwSwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5p\n" - "ZGVudHJ1c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTE\n" - "p7Gkeyxx+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEE\n" - "AYLfEwEBATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2Vu\n" - "Y3J5cHQub3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0\n" - "LmNvbS9EU1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYf\n" - "r52LFMLGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjANBgkqhkiG9w0B\n" - "AQsFAAOCAQEA2UzgyfWEiDcx27sT4rP8i2tiEmxYt0l+PAK3qB8oYevO4C5z70kH\n" - "ejWEHx2taPDY/laBL21/WKZuNTYQHHPD5b1tXgHXbnL7KqC401dk5VvCadTQsvd8\n" - "S8MXjohyc9z9/G2948kLjmE6Flh9dDYrVYA9x2O+hEPGOaEOa1eePynBgPayvUfL\n" - "qjBstzLhWVQLGAkXXmNs+5ZnPBxzDJOLxhF2JIbeQAcH5H0tZrUlo5ZYyOqA7s9p\n" - "O5b85o3AM/OJ+CktFBQtfvBhcJVd9wvlwPsk+uyOy2HI7mNxKKgsBTt375teA2Tw\n" - "UdHkhVNcsAKX1H7GNNLOEADksd86wuoXvg==\n" + "MIIEVzCCAj+gAwIBAgIRALBXPpFzlydw27SHyzpFKzgwDQYJKoZIhvcNAQELBQAw\n" + "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n" + "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjQwMzEzMDAwMDAw\n" + "WhcNMjcwMzEyMjM1OTU5WjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg\n" + "RW5jcnlwdDELMAkGA1UEAxMCRTYwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATZ8Z5G\n" + "h/ghcWCoJuuj+rnq2h25EqfUJtlRFLFhfHWWvyILOR/VvtEKRqotPEoJhC6+QJVV\n" + "6RlAN2Z17TJOdwRJ+HB7wxjnzvdxEP6sdNgA1O1tHHMWMxCcOrLqbGL0vbijgfgw\n" + "gfUwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD\n" + "ATASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBSTJ0aYA6lRaI6Y1sRCSNsj\n" + "v1iU0jAfBgNVHSMEGDAWgBR5tFnme7bl5AFzgAiIyBpY9umbbjAyBggrBgEFBQcB\n" + "AQQmMCQwIgYIKwYBBQUHMAKGFmh0dHA6Ly94MS5pLmxlbmNyLm9yZy8wEwYDVR0g\n" + "BAwwCjAIBgZngQwBAgEwJwYDVR0fBCAwHjAcoBqgGIYWaHR0cDovL3gxLmMubGVu\n" + "Y3Iub3JnLzANBgkqhkiG9w0BAQsFAAOCAgEAfYt7SiA1sgWGCIpunk46r4AExIRc\n" + "MxkKgUhNlrrv1B21hOaXN/5miE+LOTbrcmU/M9yvC6MVY730GNFoL8IhJ8j8vrOL\n" + "pMY22OP6baS1k9YMrtDTlwJHoGby04ThTUeBDksS9RiuHvicZqBedQdIF65pZuhp\n" + "eDcGBcLiYasQr/EO5gxxtLyTmgsHSOVSBcFOn9lgv7LECPq9i7mfH3mpxgrRKSxH\n" + "pOoZ0KXMcB+hHuvlklHntvcI0mMMQ0mhYj6qtMFStkF1RpCG3IPdIwpVCQqu8GV7\n" + "s8ubknRzs+3C/Bm19RFOoiPpDkwvyNfvmQ14XkyqqKK5oZ8zhD32kFRQkxa8uZSu\n" + "h4aTImFxknu39waBxIRXE4jKxlAmQc4QjFZoq1KmQqQg0J/1JF8RlFvJas1VcjLv\n" + "YlvUB2t6npO6oQjB3l+PNf0DpQH7iUx3Wz5AjQCi6L25FjyE06q6BZ/QlmtYdl/8\n" + "ZYao4SRqPEs/6cAiF+Qf5zg2UkaWtDphl1LKMuTNLotvsX99HP69V2faNyegodQ0\n" + "LyTApr/vT01YPE46vNsDLgK+4cL6TrzC/a4WcmF5SRJ938zrv/duJHLXQIku5v0+\n" + "EwOy59Hdm0PT/Er/84dDV0CSjdR/2XuZM3kpysSKLgD1cKiDA+IRguODCxfO9cyY\n" + "Ig46v9mFmBvyH04=\n" "-----END CERTIFICATE-----\n"; ///< Root certificate for io.adafruit.us const char *_aio_root_ca_prod = @@ -257,25 +297,51 @@ class ws_networking_pico : public Wippersnapper { if (WiFi.status() == WL_CONNECTED) return; + WiFi.mode(WIFI_STA); + WS.feedWDT(); + WiFi.setTimeout(20000); + WS.feedWDT(); + if (strlen(_ssid) == 0) { _status = WS_SSID_INVALID; } else { _disconnect(); delay(5000); WS.feedWDT(); - WiFi.mode(WIFI_STA); - WS.feedWDT(); - WiFi.setTimeout(20000); - WS.feedWDT(); - WiFi.begin(_ssid, _pass); - // Wait setTimeout duration for a connection and check if connected every - // 5 seconds - for (int i = 0; i < 4; i++) { + if (WS._isWiFiMulti) { + // multi network mode + _wifiMulti.clearAPList(); + // add default network + _wifiMulti.addAP(_ssid, _pass); + // add array of alternative networks + for (int i = 0; i < WS_MAX_ALT_WIFI_NETWORKS; i++) { + _wifiMulti.addAP(WS._multiNetworks[i].ssid, + WS._multiNetworks[i].pass); + } WS.feedWDT(); - delay(5000); + if (_wifiMulti.run(10000) == WL_CONNECTED) { + WS.feedWDT(); + _status = WS_NET_CONNECTED; + return; + } WS.feedWDT(); - if (WiFi.status() == WL_CONNECTED) { + } else { + WiFi.begin(_ssid, _pass); + + // Use the macro to retry the status check until connected / timed out + int lastResult; + RETRY_FUNCTION_UNTIL_TIMEOUT( + []() -> int { return WiFi.status(); }, // Function call each cycle + int, // return type + lastResult, // return variable (unused here) + [](int status) { return status == WL_CONNECTED; }, // check + PICO_CONNECT_TIMEOUT_MS, // timeout interval (ms) + PICO_CONNECT_RETRY_DELAY_MS); // interval between retries + + if (lastResult == WL_CONNECTED) { _status = WS_NET_CONNECTED; + // wait 2seconds for connection to stabilize + WS_DELAY_WITH_WDT(2000); return; } } diff --git a/src/provisioning/Config.h b/src/provisioning/Config.h index 121e840ee..0ae3e6439 100644 --- a/src/provisioning/Config.h +++ b/src/provisioning/Config.h @@ -23,7 +23,7 @@ struct secretsConfig { networkConfig network; char aio_url[64]; char aio_user[31]; - char aio_key[33]; + char aio_key[41]; int io_port; float status_pixel_brightness; }; diff --git a/src/provisioning/ConfigJson.h b/src/provisioning/ConfigJson.h index b420364fe..990aba9d6 100644 --- a/src/provisioning/ConfigJson.h +++ b/src/provisioning/ConfigJson.h @@ -19,6 +19,9 @@ #include "Config.h" #include +// Converters for network configuration +void convertToJson(const networkConfig &src, JsonVariant dst); +void convertFromJson(JsonVariantConst src, networkConfig &dst); // Converters for secrets configuration void convertToJson(const secretsConfig &src, JsonVariant dst); void convertFromJson(JsonVariantConst src, secretsConfig &dst); diff --git a/src/provisioning/littlefs/WipperSnapper_LittleFS.cpp b/src/provisioning/littlefs/WipperSnapper_LittleFS.cpp index 662f63d6d..e3ccf3831 100644 --- a/src/provisioning/littlefs/WipperSnapper_LittleFS.cpp +++ b/src/provisioning/littlefs/WipperSnapper_LittleFS.cpp @@ -17,7 +17,8 @@ defined(ARDUINO_ADAFRUIT_ITSYBITSY_ESP32) || \ defined(ARDUINO_ADAFRUIT_FEATHER_ESP32_V2) || \ defined(ARDUINO_ADAFRUIT_QTPY_ESP32_PICO) || \ - defined(ARDUINO_ADAFRUIT_QTPY_ESP32C3) + defined(ARDUINO_ADAFRUIT_QTPY_ESP32C3) || \ + defined(ARDUINO_ADAFRUIT_FEATHER_ESP32C6) #include "WipperSnapper_LittleFS.h" /**************************************************************************/ @@ -67,6 +68,49 @@ void WipperSnapper_LittleFS::parseSecrets() { fsHalt(String("ERROR: deserializeJson() failed with code ") + error.c_str()); } + if (doc.containsKey("network_type_wifi")) { + // set default network config + convertFromJson(doc["network_type_wifi"], WS._config.network); + + if (!doc["network_type_wifi"].containsKey("alternative_networks")) { + // do nothing extra, we already have the only network + WS_DEBUG_PRINTLN("Found single wifi network in secrets.json"); + + } else if (doc["network_type_wifi"]["alternative_networks"] + .is()) { + + WS_DEBUG_PRINTLN("Found multiple wifi networks in secrets.json"); + // Parse network credentials from array in secrets + JsonArray altnetworks = doc["network_type_wifi"]["alternative_networks"]; + int8_t altNetworkCount = (int8_t)altnetworks.size(); + WS_DEBUG_PRINT("Network count: "); + WS_DEBUG_PRINTLN(altNetworkCount); + if (altNetworkCount == 0) { + fsHalt("ERROR: No alternative network entries found under " + "network_type_wifi.alternative_networks in secrets.json!"); + } + // check if over 3, warn user and take first three + for (int i = 0; i < altNetworkCount; i++) { + if (i >= 3) { + WS_DEBUG_PRINT("WARNING: More than 3 networks in secrets.json, " + "only the first 3 will be used. Not using "); + WS_DEBUG_PRINTLN(altnetworks[i]["network_ssid"].as()); + break; + } + convertFromJson(altnetworks[i], WS._multiNetworks[i]); + WS_DEBUG_PRINT("Added SSID: "); + WS_DEBUG_PRINTLN(WS._multiNetworks[i].ssid); + WS_DEBUG_PRINT("PASS: "); + WS_DEBUG_PRINTLN(WS._multiNetworks[i].pass); + } + WS._isWiFiMulti = true; + } else { + fsHalt("ERROR: Unrecognised value type for " + "network_type_wifi.alternative_networks in secrets.json!"); + } + } else { + fsHalt("ERROR: Could not find network_type_wifi in secrets.json!"); + } // Extract a config struct from the JSON document WS._config = doc.as(); diff --git a/src/provisioning/tinyusb/Wippersnapper_FS.cpp b/src/provisioning/tinyusb/Wippersnapper_FS.cpp index 02aa7e4fe..6a41fe883 100644 --- a/src/provisioning/tinyusb/Wippersnapper_FS.cpp +++ b/src/provisioning/tinyusb/Wippersnapper_FS.cpp @@ -13,7 +13,8 @@ * */ #if defined(ARDUINO_MAGTAG29_ESP32S2) || defined(ARDUINO_METRO_ESP32S2) || \ - defined(ARDUINO_FUNHOUSE_ESP32S2) || defined(ADAFRUIT_PYPORTAL_M4_TITANO) || \ + defined(ARDUINO_FUNHOUSE_ESP32S2) || \ + defined(ADAFRUIT_PYPORTAL_M4_TITANO) || \ defined(ADAFRUIT_METRO_M4_AIRLIFT_LITE) || defined(ADAFRUIT_PYPORTAL) || \ defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2) || \ defined(ARDUINO_ADAFRUIT_QTPY_ESP32S2) || \ @@ -207,10 +208,8 @@ void Wippersnapper_FS::initUSBMSC() { // init MSC usb_msc.begin(); - - // re-attach the usb device + // Attach MSC and wait for enumeration TinyUSBDevice.attach(); - // wait for enumeration delay(500); } @@ -278,6 +277,16 @@ bool Wippersnapper_FS::createBootFile() { bootFile.print("MAC Address: "); bootFile.println(sMAC); + // Print ESP-specific info to boot file + #ifdef ARDUINO_ARCH_ESP32 + // Get version of ESP-IDF + bootFile.print("ESP-IDF Version: "); + bootFile.println(ESP.getSdkVersion()); + // Get version of this core + bootFile.print("ESP32 Core Version: "); + bootFile.println(ESP.getCoreVersion()); + #endif + bootFile.flush(); bootFile.close(); is_success = true; @@ -295,7 +304,7 @@ bool Wippersnapper_FS::createBootFile() { void Wippersnapper_FS::createSecretsFile() { // Open file for writing File32 secretsFile = wipperFatFs.open("/secrets.json", FILE_WRITE); - + // Create a default secretsConfig structure secretsConfig secretsConfig; strcpy(secretsConfig.aio_user, "YOUR_IO_USERNAME_HERE"); @@ -317,7 +326,8 @@ void Wippersnapper_FS::createSecretsFile() { delay(2500); // Signal to user that action must be taken (edit secrets.json) - writeToBootOut("ERROR: Please edit the secrets.json file. Then, reset your board.\n"); + writeToBootOut( + "ERROR: Please edit the secrets.json file. Then, reset your board.\n"); #ifdef USE_DISPLAY WS._ui_helper->show_scr_error( "INVALID SETTINGS FILE", @@ -344,33 +354,91 @@ void Wippersnapper_FS::parseSecrets() { JsonDocument doc; DeserializationError error = deserializeJson(doc, secretsFile); if (error) { - fsHalt(String("ERROR: Unable to parse secrets.json file - deserializeJson() failed with code") + error.c_str()); + fsHalt(String("ERROR: Unable to parse secrets.json file - " + "deserializeJson() failed with code") + + error.c_str()); + } + + if (doc.containsKey("network_type_wifi")) { + // set default network config + convertFromJson(doc["network_type_wifi"], WS._config.network); + + if (!doc["network_type_wifi"].containsKey("alternative_networks")) { + // do nothing extra, we already have the only network + WS_DEBUG_PRINTLN("Found single wifi network in secrets.json"); + + } else if (doc["network_type_wifi"]["alternative_networks"] + .is()) { + + WS_DEBUG_PRINTLN("Found multiple wifi networks in secrets.json"); + // Parse network credentials from array in secrets + JsonArray altnetworks = doc["network_type_wifi"]["alternative_networks"]; + int8_t altNetworkCount = (int8_t)altnetworks.size(); + WS_DEBUG_PRINT("Network count: "); + WS_DEBUG_PRINTLN(altNetworkCount); + if (altNetworkCount == 0) { + fsHalt("ERROR: No alternative network entries found under " + "network_type_wifi.alternative_networks in secrets.json!"); + } + // check if over 3, warn user and take first three + for (int i = 0; i < altNetworkCount; i++) { + if (i >= 3) { + WS_DEBUG_PRINT("WARNING: More than 3 networks in secrets.json, " + "only the first 3 will be used. Not using "); + WS_DEBUG_PRINTLN(altnetworks[i]["network_ssid"].as()); + break; + } + convertFromJson(altnetworks[i], WS._multiNetworks[i]); + WS_DEBUG_PRINT("Added SSID: "); + WS_DEBUG_PRINTLN(WS._multiNetworks[i].ssid); + WS_DEBUG_PRINT("PASS: "); + WS_DEBUG_PRINTLN(WS._multiNetworks[i].pass); + } + WS._isWiFiMulti = true; + } else { + fsHalt("ERROR: Unrecognised value type for " + "network_type_wifi.alternative_networks in secrets.json!"); + } + } else { + fsHalt("ERROR: Could not find network_type_wifi in secrets.json!"); } // Extract a config struct from the JSON document - WS._config = doc.as(); + WS._config = doc.as(); // Validate the config struct is not filled with default values - if (strcmp(WS._config.aio_user, "YOUR_IO_USERNAME_HERE") == 0 || strcmp(WS._config.aio_key, "YOUR_IO_KEY_HERE") == 0) { - writeToBootOut("ERROR: Invalid IO credentials in secrets.json! TO FIX: Please change io_username and io_key to match your Adafruit IO credentials!\n"); + if (strcmp(WS._config.aio_user, "YOUR_IO_USERNAME_HERE") == 0 || + strcmp(WS._config.aio_key, "YOUR_IO_KEY_HERE") == 0) { + writeToBootOut( + "ERROR: Invalid IO credentials in secrets.json! TO FIX: Please change " + "io_username and io_key to match your Adafruit IO credentials!\n"); #ifdef USE_DISPLAY WS._ui_helper->show_scr_error( "INVALID IO CREDS", - "The \"io_username/io_key\" fields within secrets.json are invalid, please " + "The \"io_username/io_key\" fields within secrets.json are invalid, " + "please " "change it to match your Adafruit IO credentials. Then, press RESET."); #endif - fsHalt("ERROR: Invalid IO credentials in secrets.json! TO FIX: Please change io_username and io_key to match your Adafruit IO credentials!"); + fsHalt( + "ERROR: Invalid IO credentials in secrets.json! TO FIX: Please change " + "io_username and io_key to match your Adafruit IO credentials!"); } - if (strcmp(WS._config.network.ssid, "YOUR_WIFI_SSID_HERE") == 0 || strcmp(WS._config.network.pass, "YOUR_WIFI_PASS_HERE") == 0) { - writeToBootOut("ERROR: Invalid network credentials in secrets.json! TO FIX: Please change network_ssid and network_password to match your Adafruit IO credentials!\n"); + if (strcmp(WS._config.network.ssid, "YOUR_WIFI_SSID_HERE") == 0 || + strcmp(WS._config.network.pass, "YOUR_WIFI_PASS_HERE") == 0) { + writeToBootOut("ERROR: Invalid network credentials in secrets.json! TO " + "FIX: Please change network_ssid and network_password to " + "match your Adafruit IO credentials!\n"); #ifdef USE_DISPLAY WS._ui_helper->show_scr_error( "INVALID NETWORK", - "The \"network_ssid and network_password\" fields within secrets.json are invalid, please " - "change it to match your WiFi credentials. Then, press RESET."); + "The \"network_ssid and network_password\" fields within secrets.json " + "are invalid, please change it to match your WiFi credentials. Then, " + "press RESET."); #endif - fsHalt("ERROR: Invalid network credentials in secrets.json! TO FIX: Please change network_ssid and network_password to match your Adafruit IO credentials!"); + fsHalt("ERROR: Invalid network credentials in secrets.json! TO FIX: Please " + "change network_ssid and network_password to match your Adafruit IO " + "credentials!"); } // Close secrets.json file @@ -437,7 +505,8 @@ void Wippersnapper_FS::createDisplayConfig() { // Create and fill JSON document from displayConfig JsonDocument doc; if (!doc.set(displayConfig)) { - fsHalt("ERROR: Unable to set displayConfig, no space in arduinoJSON document!"); + fsHalt("ERROR: Unable to set displayConfig, no space in arduinoJSON " + "document!"); } // Write the file out to the filesystem serializeJsonPretty(doc, displayFile); @@ -460,7 +529,8 @@ bool Wippersnapper_FS::parseDisplayConfig(displayConfig &dispCfg, bool forceRecr if (!wipperFatFs.exists("/display_config.json")) { WS_DEBUG_PRINTLN("Could not find display_config.json, generating..."); #ifdef ARDUINO_FUNHOUSE_ESP32S2 - createDisplayConfig(); // generate a default display_config.json for FunHouse + createDisplayConfig(); // generate a default display_config.json for + // FunHouse #endif } @@ -480,7 +550,9 @@ bool Wippersnapper_FS::parseDisplayConfig(displayConfig &dispCfg, bool forceRecr if (!forceRecreate && parseDisplayConfig(dispCfg, true)) { return true; } - fsHalt(String("FATAL ERROR: Unable to parse display_config.json - deserializeJson() failed with code") + error.c_str()); + fsHalt(String("FATAL ERROR: Unable to parse display_config.json - " + "deserializeJson() failed with code") + + error.c_str()); } // Close the file, we're done with it file.close();